/// <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);
            }
Esempio n. 2
0
            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) ??
Esempio n. 5
0
            /// <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);
Esempio n. 6
0
        /// <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);
            }
        }
Esempio n. 7
0
            /// <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);
            }
Esempio n. 8
0
            /// <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);
Esempio n. 9
0
        /// <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);
        }
Esempio n. 10
0
            /// <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);
Esempio n. 11
0
        /// <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);
Esempio n. 12
0
    /// <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));
    }
Esempio n. 13
0
    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);
    }
Esempio n. 14
0
    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;
Esempio n. 15
0
                /// <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);
            }
        }
Esempio n. 17
0
        /// <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;
Esempio n. 18
0
        /// <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;
Esempio n. 19
0
 static T GetRequiredService <T>(IServiceProvider provider) => provider.GetService <T>() ??
 throw new InvalidOperationException(SR.GetResourceString(SR.ID0169));