/// <summary> /// Sets the service descriptor. /// </summary> /// <param name="descriptor">The service descriptor.</param> /// <returns>The builder instance, so that calls can be easily chained.</returns> public Builder <TContext> SetServiceDescriptor(ServiceDescriptor descriptor) { if (descriptor is null) { throw new ArgumentNullException(nameof(descriptor)); } var type = descriptor.ServiceType; if (!typeof(IOpenIddictServerHandler <>).MakeGenericType(typeof(TContext)).IsAssignableFrom(type)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0104)); } _descriptor = descriptor; return(this); }
static string?GetKeyIdentifier(SecurityKey key) { // When no key identifier can be retrieved from the security keys, a value is automatically // inferred from the hexadecimal representation of the certificate thumbprint (SHA-1) // when the key is bound to a X.509 certificate or from the public part of the signing key. if (key is X509SecurityKey x509SecurityKey) { return(x509SecurityKey.Certificate.Thumbprint); } if (key is RsaSecurityKey rsaSecurityKey) { // Note: if the RSA parameters are not attached to the signing key, // extract them by calling ExportParameters on the RSA instance. var parameters = rsaSecurityKey.Parameters; if (parameters.Modulus is null) { parameters = rsaSecurityKey.Rsa.ExportParameters(includePrivateParameters: false); Debug.Assert(parameters.Modulus is not null, SR.GetResourceString(SR.ID4003)); } // Only use the 40 first chars of the base64url-encoded modulus. var identifier = Base64UrlEncoder.Encode(parameters.Modulus); return(identifier.Substring(0, Math.Min(identifier.Length, 40)).ToUpperInvariant()); } #if SUPPORTS_ECDSA if (key is ECDsaSecurityKey ecsdaSecurityKey) { // Extract the ECDSA parameters from the signing credentials. var parameters = ecsdaSecurityKey.ECDsa.ExportParameters(includePrivateParameters: false); Debug.Assert(parameters.Q.X is not null, SR.GetResourceString(SR.ID4004)); // Only use the 40 first chars of the base64url-encoded X coordinate. var identifier = Base64UrlEncoder.Encode(parameters.Q.X); return(identifier.Substring(0, Math.Min(identifier.Length, 40)).ToUpperInvariant()); } #endif return(null); }
/// <inheritdoc/> public ValueTask HandleAsync(HandleConfigurationResponseContext context) { if (context is null) { throw new ArgumentNullException(nameof(context)); } // The issuer returned in the discovery document must exactly match the URL used to access it. // See https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation. var issuer = (string?)context.Response[Metadata.Issuer]; if (string.IsNullOrEmpty(issuer)) { context.Reject( error: Errors.ServerError, description: SR.GetResourceString(SR.ID2096), uri: SR.FormatID8000(SR.ID2096)); return(default);
/// <inheritdoc/> public async ValueTask HandleAsync(PrepareIntrospectionRequestContext context) { if (context is null) { throw new ArgumentNullException(nameof(context)); } Debug.Assert(context.Request is not null, SR.GetResourceString(SR.ID4008)); // This handler only applies to System.Net.Http requests. If the HTTP request cannot be resolved, // this may indicate that the request was incorrectly processed by another client stack. var request = context.Transaction.GetHttpRequestMessage(); if (request is null) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0173)); } var configuration = await context.Options.ConfigurationManager.GetConfigurationAsync(default) ??
/// <inheritdoc/> public ValueTask HandleAsync(ApplyLogoutResponseContext context) { if (context is null) { throw new ArgumentNullException(nameof(context)); } // This handler only applies to ASP.NET Core requests. If the HTTP context cannot be resolved, // this may indicate that the request was incorrectly processed by another server stack. var response = context.Transaction.GetHttpRequest()?.HttpContext.Response; if (response is null) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0114)); } if (string.IsNullOrEmpty(context.PostLogoutRedirectUri)) { return(default);
/// <summary> /// Initializes a new OpenIddict message. /// </summary> /// <param name="parameters">The message parameters.</param> public OpenIddictMessage(JsonElement parameters) { if (parameters.ValueKind != JsonValueKind.Object) { throw new ArgumentException(SR.GetResourceString(SR.ID0189), nameof(parameters)); } foreach (var parameter in parameters.EnumerateObject()) { // While generally discouraged, JSON objects can contain multiple properties with // the same name. In this case, the last occurrence replaces the previous ones. if (HasParameter(parameter.Name)) { RemoveParameter(parameter.Name); } AddParameter(parameter.Name, parameter.Value); } }
/// <summary> /// Imports the properties set on the specified descriptor. /// </summary> /// <param name="descriptor">The existing descriptor properties are copied from.</param> /// <remarks>All the properties previously set on this instance are automatically replaced.</remarks> /// <returns>The builder instance, so that calls can be easily chained.</returns> public Builder <TContext> Import(OpenIddictServerHandlerDescriptor descriptor) { if (descriptor is null) { throw new ArgumentNullException(nameof(descriptor)); } if (descriptor.ContextType != typeof(TContext)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0284)); } _descriptor = descriptor.ServiceDescriptor; _filters.Clear(); _filters.AddRange(descriptor.FilterTypes); _order = descriptor.Order; _type = descriptor.Type; return(this); }
/// <inheritdoc/> public ValueTask HandleAsync(ApplyAuthorizationResponseContext context) { if (context is null) { throw new ArgumentNullException(nameof(context)); } // This handler only applies to OWIN requests. If The OWIN request cannot be resolved, // this may indicate that the request was incorrectly processed by another server stack. var response = context.Transaction.GetOwinRequest()?.Context.Response; if (response is null) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0120)); } if (string.IsNullOrEmpty(context.RedirectUri) || !string.Equals(context.ResponseMode, ResponseModes.Query, StringComparison.Ordinal)) { return(default);
/// <summary> /// Retrieves a property value from the server transaction using the specified name. /// </summary> /// <typeparam name="TProperty">The type of the property.</typeparam> /// <param name="transaction">The server transaction.</param> /// <param name="name">The property name.</param> /// <returns>The property value or <c>null</c> if it couldn't be found.</returns> public static TProperty?GetProperty <TProperty>( this OpenIddictServerTransaction transaction, string name) where TProperty : class { if (transaction == null) { throw new ArgumentNullException(nameof(transaction)); } if (string.IsNullOrEmpty(name)) { throw new ArgumentException(SR.GetResourceString(SR.ID1105), nameof(name)); } if (transaction.Properties.TryGetValue(name, out var property) && property is TProperty result) { return(result); } return(null); }
/// <inheritdoc/> public ValueTask HandleAsync(ProcessRequestContext context) { if (context is null) { throw new ArgumentNullException(nameof(context)); } // This handler only applies to ASP.NET Core requests. If the HTTP context cannot be resolved, // this may indicate that the request was incorrectly processed by another server stack. var request = context.Transaction.GetHttpRequest(); if (request is null) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0114)); } // Only use the current host as the issuer if the // issuer was not explicitly set in the options. if (context.Issuer is not null) { return(default);
/// <inheritdoc/> public ValueTask HandleAsync(ApplyVerificationResponseContext context) { if (context is null) { throw new ArgumentNullException(nameof(context)); } // This handler only applies to OWIN requests. If The OWIN request cannot be resolved, // this may indicate that the request was incorrectly processed by another server stack. var response = context.Transaction.GetOwinRequest()?.Context.Response; if (response is null) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0120)); } // Note: this handler only redirects the user agent to the address specified in // the properties when there's no error or if the error is an access_denied error. if (!string.IsNullOrEmpty(context.Response.Error) && !string.Equals(context.Response.Error, Errors.AccessDenied, StringComparison.Ordinal)) { return(default);
/// <inheritdoc/> public ValueTask <IMongoDatabase> GetDatabaseAsync(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(new ValueTask <IMongoDatabase>(Task.FromCanceled <IMongoDatabase>(cancellationToken))); } var database = _options.CurrentValue.Database; if (database is null) { database = _provider.GetService <IMongoDatabase>(); } if (database is null) { return(new ValueTask <IMongoDatabase>(Task.FromException <IMongoDatabase>( new InvalidOperationException(SR.GetResourceString(SR.ID0262))))); } return(new ValueTask <IMongoDatabase>(database)); }
public async Task GetDatabaseAsync_ThrowsAnExceptionWhenDatabaseCannotBeFound() { // Arrange var services = new ServiceCollection(); var provider = services.BuildServiceProvider(); var options = Mock.Of <IOptionsMonitor <OpenIddictMongoDbOptions> >( mock => mock.CurrentValue == new OpenIddictMongoDbOptions { Database = null }); var context = new OpenIddictMongoDbContext(options, provider); // Act and assert var exception = await Assert.ThrowsAsync <InvalidOperationException>(async delegate { await context.GetDatabaseAsync(CancellationToken.None); }); Assert.Equal(SR.GetResourceString(SR.ID0262), exception.Message); }
public async ValueTask DispatchAsync <TContext>(TContext context) where TContext : BaseContext { if (context is null) { throw new ArgumentNullException(nameof(context)); } await foreach (var handler in GetHandlersAsync()) { try { await handler.HandleAsync(context); } catch (Exception exception) when(_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug(exception, SR.GetResourceString(SR.ID6132), handler.GetType().FullName, typeof(TContext).FullName); throw; } if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug(SR.GetResourceString(SR.ID6133), typeof(TContext).FullName, handler.GetType().FullName); } switch (context) { case BaseRequestContext { IsRequestHandled: true } : if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug(SR.GetResourceString(SR.ID6134), typeof(TContext).FullName, handler.GetType().FullName); } return;
/// <inheritdoc/> public async ValueTask HandleAsync(ExtractLogoutRequestContext context) { if (context is null) { throw new ArgumentNullException(nameof(context)); } Debug.Assert(context.Request is not null, SR.GetResourceString(SR.ID4008)); // If a request_id parameter can be found in the logout request, // restore the complete logout request from the distributed cache. if (string.IsNullOrEmpty(context.Request.RequestId)) { return; } // Note: the cache key is always prefixed with a specific marker // to avoid collisions with the other types of cached payloads. var token = await _cache.GetStringAsync(Cache.LogoutRequest + context.Request.RequestId); if (token is null || !context.Options.JsonWebTokenHandler.CanReadToken(token)) { context.Logger.LogError(SR.GetResourceString(SR.ID6150), Parameters.RequestId); context.Reject( error: Errors.InvalidRequest, description: context.Localizer[SR.ID2052, Parameters.RequestId]); return; } var parameters = context.Options.TokenValidationParameters.Clone(); parameters.ValidIssuer ??= context.Issuer?.AbsoluteUri; parameters.ValidAudience = context.Issuer?.AbsoluteUri; parameters.ValidTypes = new[] { JsonWebTokenTypes.Private.LogoutRequest }; var result = context.Options.JsonWebTokenHandler.ValidateToken(token, parameters); if (!result.IsValid) { context.Logger.LogError(SR.GetResourceString(SR.ID6150), Parameters.RequestId); context.Reject( error: Errors.InvalidRequest, description: context.Localizer[SR.ID2052, Parameters.RequestId]); return; } using var document = JsonDocument.Parse( Base64UrlEncoder.Decode(((JsonWebToken)result.SecurityToken).InnerToken.EncodedPayload)); if (document.RootElement.ValueKind != JsonValueKind.Object) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0118)); } // Restore the authorization request parameters from the serialized payload. foreach (var parameter in document.RootElement.EnumerateObject()) { // Avoid overriding the current request parameters. if (context.Request.HasParameter(parameter.Name)) { continue; } context.Request.SetParameter(parameter.Name, parameter.Value.Clone()); } }
public async ValueTask DispatchAsync <TContext>(TContext context) where TContext : BaseContext { if (context is null) { throw new ArgumentNullException(nameof(context)); } await foreach (var handler in GetHandlersAsync()) { try { await handler.HandleAsync(context); } catch (Exception exception) when(_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug(exception, SR.GetResourceString(SR.ID6132), handler.GetType().FullName, typeof(TContext).FullName); throw; } if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug(SR.GetResourceString(SR.ID6133), typeof(TContext).FullName, handler.GetType().FullName); } switch (context) { case BaseRequestContext notification when notification.IsRequestHandled: if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug(SR.GetResourceString(SR.ID6134), typeof(TContext).FullName, handler.GetType().FullName); } return; case BaseRequestContext notification when notification.IsRequestSkipped: if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug(SR.GetResourceString(SR.ID6135), typeof(TContext).FullName, handler.GetType().FullName); } return; case BaseValidatingContext notification when notification.IsRejected: if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug(SR.GetResourceString(SR.ID6136), typeof(TContext).FullName, handler.GetType().FullName); } return; default: continue; } } async IAsyncEnumerable <IOpenIddictValidationHandler <TContext> > GetHandlersAsync() { // Note: the descriptors collection is sorted during options initialization for performance reasons. var descriptors = _options.CurrentValue.Handlers; if (descriptors.Count == 0) { yield break; } for (var index = 0; index < descriptors.Count; index++) { var descriptor = descriptors[index]; if (descriptor.ContextType != typeof(TContext) || !await IsActiveAsync(descriptor)) { continue; } var handler = descriptor.ServiceDescriptor.ImplementationInstance is not null ? descriptor.ServiceDescriptor.ImplementationInstance as IOpenIddictValidationHandler <TContext> : _provider.GetService(descriptor.ServiceDescriptor.ServiceType) as IOpenIddictValidationHandler <TContext>; if (handler is null) { throw new InvalidOperationException(SR.FormatID0138(descriptor.ServiceDescriptor.ServiceType)); } yield return(handler); } } async ValueTask <bool> IsActiveAsync(OpenIddictValidationHandlerDescriptor descriptor) { for (var index = 0; index < descriptor.FilterTypes.Length; index++) { if (!(_provider.GetService(descriptor.FilterTypes[index]) is IOpenIddictValidationHandlerFilter <TContext> filter)) { throw new InvalidOperationException(SR.FormatID0099(descriptor.FilterTypes[index])); } if (!await filter.IsActiveAsync(context)) { return(false); } } return(true); } }
/// <summary> /// Populates the default OpenIddict server options and ensures /// that the configuration is in a consistent and valid state. /// </summary> /// <param name="name">The name of the options instance to configure, if applicable.</param> /// <param name="options">The options instance to initialize.</param> public void PostConfigure(string name, OpenIddictServerOptions options) { if (options is null) { throw new ArgumentNullException(nameof(options)); } if (options.EnableDegradedMode) { // Explicitly disable all the features that are implicitly excluded when the degraded mode is active. options.DisableAuthorizationStorage = options.DisableTokenStorage = true; options.IgnoreEndpointPermissions = options.IgnoreGrantTypePermissions = options.IgnoreScopePermissions = true; options.UseReferenceAccessTokens = options.UseReferenceRefreshTokens = false; // When the degraded mode is enabled (and the token storage disabled), OpenIddict is not able to dynamically // update the expiration date of a token. As such, either rolling tokens MUST be enabled or sliding token // expiration MUST be disabled to always issue new refresh tokens with the same fixed expiration date. // By default, OpenIddict will automatically force the rolling tokens option when using the degraded mode. options.UseRollingRefreshTokens |= !options.UseRollingRefreshTokens && !options.DisableSlidingRefreshTokenExpiration; } if (options.JsonWebTokenHandler is null) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0075)); } // Ensure at least one flow has been enabled. if (options.GrantTypes.Count == 0) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0076)); } // Ensure the authorization endpoint has been enabled when // the authorization code or implicit grants are supported. if (options.AuthorizationEndpointUris.Count == 0 && (options.GrantTypes.Contains(GrantTypes.AuthorizationCode) || options.GrantTypes.Contains(GrantTypes.Implicit))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0077)); } // Ensure the device endpoint has been enabled when the device grant is supported. if (options.DeviceEndpointUris.Count == 0 && options.GrantTypes.Contains(GrantTypes.DeviceCode)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0078)); } // Ensure the token endpoint has been enabled when the authorization code, // client credentials, device, password or refresh token grants are supported. if (options.TokenEndpointUris.Count == 0 && (options.GrantTypes.Contains(GrantTypes.AuthorizationCode) || options.GrantTypes.Contains(GrantTypes.ClientCredentials) || options.GrantTypes.Contains(GrantTypes.DeviceCode) || options.GrantTypes.Contains(GrantTypes.Password) || options.GrantTypes.Contains(GrantTypes.RefreshToken))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0079)); } // Ensure the verification endpoint has been enabled when the device grant is supported. if (options.VerificationEndpointUris.Count == 0 && options.GrantTypes.Contains(GrantTypes.DeviceCode)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0080)); } if (options.DisableTokenStorage) { if (options.UseReferenceAccessTokens || options.UseReferenceRefreshTokens) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0083)); } if (!options.DisableSlidingRefreshTokenExpiration && !options.UseRollingRefreshTokens) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0084)); } } if (options.EncryptionCredentials.Count == 0) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0085)); } if (!options.SigningCredentials.Any(credentials => credentials.Key is AsymmetricSecurityKey)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0086)); } // If all the registered encryption credentials are backed by a X.509 certificate, at least one of them must be valid. if (options.EncryptionCredentials.All(credentials => credentials.Key is X509SecurityKey x509SecurityKey && (x509SecurityKey.Certificate.NotBefore > DateTime.Now || x509SecurityKey.Certificate.NotAfter < DateTime.Now))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0087)); } // If all the registered signing credentials are backed by a X.509 certificate, at least one of them must be valid. if (options.SigningCredentials.All(credentials => credentials.Key is X509SecurityKey x509SecurityKey && (x509SecurityKey.Certificate.NotBefore > DateTime.Now || x509SecurityKey.Certificate.NotAfter < DateTime.Now))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0088)); } if (options.EnableDegradedMode) { // If the degraded mode was enabled, ensure custom validation handlers // have been registered for the endpoints that require manual validation. if (options.AuthorizationEndpointUris.Count != 0 && !options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ValidateAuthorizationRequestContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0089)); } if (options.DeviceEndpointUris.Count != 0 && !options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ValidateDeviceRequestContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0090)); } if (options.IntrospectionEndpointUris.Count != 0 && !options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ValidateIntrospectionRequestContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0091)); } if (options.LogoutEndpointUris.Count != 0 && !options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ValidateLogoutRequestContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0092)); } if (options.RevocationEndpointUris.Count != 0 && !options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ValidateRevocationRequestContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0093)); } if (options.TokenEndpointUris.Count != 0 && !options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ValidateTokenRequestContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0094)); } if (options.VerificationEndpointUris.Count != 0 && !options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ValidateVerificationRequestContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0095)); } // If the degraded mode was enabled, ensure custom authentication/sign-in handlers // have been registered to deal with device/user codes validation and generation. if (options.GrantTypes.Contains(GrantTypes.DeviceCode)) { if (!options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ProcessAuthenticationContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0096)); } if (!options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ProcessSignInContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0097)); } } } // Sort the handlers collection using the order associated with each handler. options.Handlers.Sort((left, right) => left.Order.CompareTo(right.Order)); // Sort the encryption and signing credentials. options.EncryptionCredentials.Sort((left, right) => Compare(left.Key, right.Key)); options.SigningCredentials.Sort((left, right) => Compare(left.Key, right.Key)); // Automatically add the offline_access scope if the refresh token grant has been enabled. if (options.GrantTypes.Contains(GrantTypes.RefreshToken)) { options.Scopes.Add(Scopes.OfflineAccess); } if (options.GrantTypes.Contains(GrantTypes.AuthorizationCode)) { options.CodeChallengeMethods.Add(CodeChallengeMethods.Sha256); options.ResponseTypes.Add(ResponseTypes.Code); } if (options.GrantTypes.Contains(GrantTypes.Implicit)) { options.ResponseTypes.Add(ResponseTypes.IdToken); options.ResponseTypes.Add(ResponseTypes.IdToken + ' ' + ResponseTypes.Token); options.ResponseTypes.Add(ResponseTypes.Token); } if (options.GrantTypes.Contains(GrantTypes.AuthorizationCode) && options.GrantTypes.Contains(GrantTypes.Implicit)) { options.ResponseTypes.Add(ResponseTypes.Code + ' ' + ResponseTypes.IdToken); options.ResponseTypes.Add(ResponseTypes.Code + ' ' + ResponseTypes.IdToken + ' ' + ResponseTypes.Token); options.ResponseTypes.Add(ResponseTypes.Code + ' ' + ResponseTypes.Token); } if (options.ResponseTypes.Count != 0) { options.ResponseModes.Add(ResponseModes.FormPost); options.ResponseModes.Add(ResponseModes.Fragment); if (options.ResponseTypes.Contains(ResponseTypes.Code)) { options.ResponseModes.Add(ResponseModes.Query); } } foreach (var key in options.EncryptionCredentials .Select(credentials => credentials.Key) .Concat(options.SigningCredentials.Select(credentials => credentials.Key))) { if (!string.IsNullOrEmpty(key.KeyId)) { continue; } key.KeyId = GetKeyIdentifier(key); } // Attach the signing credentials to the token validation parameters. options.TokenValidationParameters.IssuerSigningKeys = from credentials in options.SigningCredentials select credentials.Key; // Attach the encryption credentials to the token validation parameters. options.TokenValidationParameters.TokenDecryptionKeys = from credentials in options.EncryptionCredentials select credentials.Key;
/// <summary> /// Populates the default OpenIddict server options and ensures /// that the configuration is in a consistent and valid state. /// </summary> /// <param name="name">The name of the options instance to configure, if applicable.</param> /// <param name="options">The options instance to initialize.</param> public void PostConfigure(string name, OpenIddictServerOptions options) { if (options is null) { throw new ArgumentNullException(nameof(options)); } // Explicitly disable all the features that are implicitly excluded when the degraded mode is active. if (options.EnableDegradedMode) { options.DisableAuthorizationStorage = options.DisableTokenStorage = options.DisableRollingRefreshTokens = true; options.IgnoreEndpointPermissions = options.IgnoreGrantTypePermissions = true; options.IgnoreResponseTypePermissions = options.IgnoreScopePermissions = true; options.UseReferenceAccessTokens = options.UseReferenceRefreshTokens = false; } // Explicitly disable rolling refresh tokens when token storage is disabled. if (options.DisableTokenStorage) { options.DisableRollingRefreshTokens = true; } if (options.JsonWebTokenHandler is null) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0075)); } // Ensure at least one flow has been enabled. if (options.GrantTypes.Count == 0) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0076)); } var addresses = options.AuthorizationEndpointUris.Distinct() .Concat(options.ConfigurationEndpointUris.Distinct()) .Concat(options.CryptographyEndpointUris.Distinct()) .Concat(options.DeviceEndpointUris.Distinct()) .Concat(options.IntrospectionEndpointUris.Distinct()) .Concat(options.LogoutEndpointUris.Distinct()) .Concat(options.RevocationEndpointUris.Distinct()) .Concat(options.TokenEndpointUris.Distinct()) .Concat(options.UserinfoEndpointUris.Distinct()) .Concat(options.VerificationEndpointUris.Distinct()) .ToList(); // Ensure endpoint addresses are unique across endpoints. if (addresses.Count != addresses.Distinct().Count()) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0285)); } // Ensure the authorization endpoint has been enabled when // the authorization code or implicit grants are supported. if (options.AuthorizationEndpointUris.Count == 0 && (options.GrantTypes.Contains(GrantTypes.AuthorizationCode) || options.GrantTypes.Contains(GrantTypes.Implicit))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0077)); } // Ensure the device endpoint has been enabled when the device grant is supported. if (options.DeviceEndpointUris.Count == 0 && options.GrantTypes.Contains(GrantTypes.DeviceCode)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0078)); } // Ensure the token endpoint has been enabled when the authorization code, // client credentials, device, password or refresh token grants are supported. if (options.TokenEndpointUris.Count == 0 && (options.GrantTypes.Contains(GrantTypes.AuthorizationCode) || options.GrantTypes.Contains(GrantTypes.ClientCredentials) || options.GrantTypes.Contains(GrantTypes.DeviceCode) || options.GrantTypes.Contains(GrantTypes.Password) || options.GrantTypes.Contains(GrantTypes.RefreshToken))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0079)); } // Ensure the verification endpoint has been enabled when the device grant is supported. if (options.VerificationEndpointUris.Count == 0 && options.GrantTypes.Contains(GrantTypes.DeviceCode)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0080)); } // Ensure the device grant is allowed when the device endpoint is enabled. if (options.DeviceEndpointUris.Count > 0 && !options.GrantTypes.Contains(GrantTypes.DeviceCode)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0084)); } // Ensure the grant types/response types configuration is consistent. foreach (var type in options.ResponseTypes) { var types = new HashSet <string>(type.Split(Separators.Space, StringSplitOptions.RemoveEmptyEntries), StringComparer.Ordinal); if (types.Contains(ResponseTypes.Code) && !options.GrantTypes.Contains(GrantTypes.AuthorizationCode)) { throw new InvalidOperationException(SR.FormatID0281(ResponseTypes.Code)); } if (types.Contains(ResponseTypes.IdToken) && !options.GrantTypes.Contains(GrantTypes.Implicit)) { throw new InvalidOperationException(SR.FormatID0282(ResponseTypes.IdToken)); } if (types.Contains(ResponseTypes.Token) && !options.GrantTypes.Contains(GrantTypes.Implicit)) { throw new InvalidOperationException(SR.FormatID0282(ResponseTypes.Token)); } } // Ensure reference tokens support was not enabled when token storage is disabled. if (options.DisableTokenStorage && (options.UseReferenceAccessTokens || options.UseReferenceRefreshTokens)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0083)); } if (options.EncryptionCredentials.Count == 0) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0085)); } if (!options.SigningCredentials.Any(credentials => credentials.Key is AsymmetricSecurityKey)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0086)); } // If all the registered encryption credentials are backed by a X.509 certificate, at least one of them must be valid. if (options.EncryptionCredentials.All(credentials => credentials.Key is X509SecurityKey x509SecurityKey && (x509SecurityKey.Certificate.NotBefore > DateTime.Now || x509SecurityKey.Certificate.NotAfter < DateTime.Now))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0087)); } // If all the registered signing credentials are backed by a X.509 certificate, at least one of them must be valid. if (options.SigningCredentials.All(credentials => credentials.Key is X509SecurityKey x509SecurityKey && (x509SecurityKey.Certificate.NotBefore > DateTime.Now || x509SecurityKey.Certificate.NotAfter < DateTime.Now))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0088)); } if (options.EnableDegradedMode) { // If the degraded mode was enabled, ensure custom validation handlers // have been registered for the endpoints that require manual validation. if (options.AuthorizationEndpointUris.Count != 0 && !options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ValidateAuthorizationRequestContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0089)); } if (options.DeviceEndpointUris.Count != 0 && !options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ValidateDeviceRequestContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0090)); } if (options.IntrospectionEndpointUris.Count != 0 && !options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ValidateIntrospectionRequestContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0091)); } if (options.LogoutEndpointUris.Count != 0 && !options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ValidateLogoutRequestContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0092)); } if (options.RevocationEndpointUris.Count != 0 && !options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ValidateRevocationRequestContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0093)); } if (options.TokenEndpointUris.Count != 0 && !options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ValidateTokenRequestContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0094)); } if (options.VerificationEndpointUris.Count != 0 && !options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ValidateVerificationRequestContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0095)); } // If the degraded mode was enabled, ensure custom authentication/sign-in handlers // have been registered to deal with device/user codes validation and generation. if (options.GrantTypes.Contains(GrantTypes.DeviceCode)) { if (!options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ProcessAuthenticationContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0096)); } if (!options.Handlers.Any( descriptor => descriptor.ContextType == typeof(ProcessSignInContext) && descriptor.Type == OpenIddictServerHandlerType.Custom && descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type)))) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0097)); } } } // Sort the handlers collection using the order associated with each handler. options.Handlers.Sort((left, right) => left.Order.CompareTo(right.Order)); // Sort the encryption and signing credentials. options.EncryptionCredentials.Sort((left, right) => Compare(left.Key, right.Key)); options.SigningCredentials.Sort((left, right) => Compare(left.Key, right.Key)); // Generate a key identifier for the encryption/signing keys that don't already have one. foreach (var key in options.EncryptionCredentials .Select(credentials => credentials.Key) .Concat(options.SigningCredentials.Select(credentials => credentials.Key)) .Where(key => string.IsNullOrEmpty(key.KeyId))) { key.KeyId = GetKeyIdentifier(key); } // Attach the signing credentials to the token validation parameters. options.TokenValidationParameters.IssuerSigningKeys = from credentials in options.SigningCredentials select credentials.Key; // Attach the encryption credentials to the token validation parameters. options.TokenValidationParameters.TokenDecryptionKeys = from credentials in options.EncryptionCredentials select credentials.Key;
static T GetRequiredService <T>(IServiceProvider provider) => provider.GetService <T>() ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0169));