private async Task CreateAuthorizationAsync([NotNull] AuthenticationTicket ticket,
                                                    [NotNull] OpenIddictServerOptions options, [NotNull] OpenIdConnectRequest request)
        {
            var descriptor = new OpenIddictAuthorizationDescriptor
            {
                Principal = ticket.Principal,
                Status    = OpenIddictConstants.Statuses.Valid,
                Subject   = ticket.Principal.GetClaim(OpenIddictConstants.Claims.Subject),
                Type      = OpenIddictConstants.AuthorizationTypes.AdHoc
            };

            foreach (var property in ticket.Properties.Items)
            {
                descriptor.Properties.Add(property);
            }

            foreach (var scope in ticket.GetScopes())
            {
                descriptor.Scopes.Add(scope);
            }

            // If the client application is known, bind it to the authorization.
            if (!string.IsNullOrEmpty(request.ClientId))
            {
                var application = await _applicationManager.FindByClientIdAsync(request.ClientId);

                if (application == null)
                {
                    throw new InvalidOperationException("The application entry cannot be found in the database.");
                }

                descriptor.ApplicationId = await _applicationManager.GetIdAsync(application);
            }

            var authorization = await _authorizationManager.CreateAsync(descriptor);

            if (authorization != null)
            {
                var identifier = await _authorizationManager.GetIdAsync(authorization);

                if (string.IsNullOrEmpty(request.ClientId))
                {
                    _logger.LogInformation("An ad hoc authorization was automatically created and " +
                                           "associated with an unknown application: {Identifier}.", identifier);
                }

                else
                {
                    _logger.LogInformation("An ad hoc authorization was automatically created and " +
                                           "associated with the '{ClientId}' application: {Identifier}.",
                                           request.ClientId, identifier);
                }

                // Attach the unique identifier of the ad hoc authorization to the authentication ticket
                // so that it is attached to all the derived tokens, allowing batched revocations support.
                ticket.SetInternalAuthorizationId(identifier);
            }
        }
        public void Configure(OpenIddictServerOptions options)
        {
            if (options is null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            // Register the built-in event handlers used by the OpenIddict ASP.NET Core server components.
            options.Handlers.AddRange(OpenIddictServerAspNetCoreHandlers.DefaultHandlers);
        }
예제 #3
0
        public void Configure([CanBeNull] string name, [NotNull] OpenIddictServerOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            // Register the built-in event handlers used by the OpenIddict ASP.NET Core server components.
            foreach (var handler in OpenIddictServerAspNetCoreHandlers.DefaultHandlers)
            {
                options.DefaultHandlers.Add(handler);
            }
        }
예제 #4
0
        public void Configure(OpenIddictServerOptions options)
        {
            var settings = GetServerSettingsAsync().GetAwaiter().GetResult();

            if (settings == null)
            {
                return;
            }

            options.IgnoreScopePermissions = true;
            options.Issuer = settings.Authority;
            options.DisableAccessTokenEncryption = settings.DisableAccessTokenEncryption;
            options.UseRollingRefreshTokens      = settings.UseRollingRefreshTokens;
            options.UseReferenceAccessTokens     = settings.UseReferenceAccessTokens;

            foreach (var key in _serverService.GetEncryptionKeysAsync().GetAwaiter().GetResult())
            {
                options.EncryptionCredentials.Add(new EncryptingCredentials(key,
                                                                            SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes256CbcHmacSha512));
            }

            foreach (var key in _serverService.GetSigningKeysAsync().GetAwaiter().GetResult())
            {
                options.SigningCredentials.Add(new SigningCredentials(key, SecurityAlgorithms.RsaSha256));
            }

            if (settings.AuthorizationEndpointPath.HasValue)
            {
                options.AuthorizationEndpointUris.Add(new Uri(settings.AuthorizationEndpointPath.Value, UriKind.Relative));
            }
            if (settings.LogoutEndpointPath.HasValue)
            {
                options.LogoutEndpointUris.Add(new Uri(settings.LogoutEndpointPath.Value, UriKind.Relative));
            }
            if (settings.TokenEndpointPath.HasValue)
            {
                options.TokenEndpointUris.Add(new Uri(settings.TokenEndpointPath.Value, UriKind.Relative));
            }
            if (settings.UserinfoEndpointPath.HasValue)
            {
                options.UserinfoEndpointUris.Add(new Uri(settings.UserinfoEndpointPath.Value, UriKind.Relative));
            }

            options.GrantTypes.UnionWith(settings.GrantTypes);

            options.Scopes.Add(Scopes.Email);
            options.Scopes.Add(Scopes.Phone);
            options.Scopes.Add(Scopes.Profile);
            options.Scopes.Add(Scopes.Roles);
        }
        public void Configure(string name, OpenIddictServerOptions options)
        {
            // Ignore OpenIddict handler instances that don't correspond to the instance managed by the OpenID module.
            if (!string.Equals(name, OpenIddictServerDefaults.AuthenticationScheme))
            {
                return;
            }

            var settings = GetServerSettingsAsync().GetAwaiter().GetResult();

            if (settings == null)
            {
                return;
            }

            // Note: in Orchard, transport security is usually configured via the dedicated HTTPS module.
            // To make configuration easier and avoid having to configure it in two different features,
            // the transport security requirement enforced by OpenIddict by default is always turned off.
            options.AllowInsecureHttp = true;

            options.ApplicationCanDisplayErrors = true;
            options.EnableRequestCaching        = true;
            options.IgnoreScopePermissions      = true;
            options.Issuer             = settings.Authority;
            options.UseRollingTokens   = settings.UseRollingTokens;
            options.UseReferenceTokens = settings.UseReferenceTokens;

            foreach (var key in _serverService.GetSigningKeysAsync().GetAwaiter().GetResult())
            {
                options.SigningCredentials.AddKey(key);
            }

            if (settings.AccessTokenFormat == OpenIdServerSettings.TokenFormat.JWT)
            {
                options.AccessTokenHandler = new JwtSecurityTokenHandler();
            }

            options.AuthorizationEndpointPath = settings.AuthorizationEndpointPath;
            options.LogoutEndpointPath        = settings.LogoutEndpointPath;
            options.TokenEndpointPath         = settings.TokenEndpointPath;
            options.UserinfoEndpointPath      = settings.UserinfoEndpointPath;

            options.GrantTypes.Clear();
            options.GrantTypes.UnionWith(settings.GrantTypes);

            options.Scopes.Add(OpenIddictConstants.Scopes.Email);
            options.Scopes.Add(OpenIddictConstants.Scopes.Phone);
            options.Scopes.Add(OpenIddictConstants.Scopes.Profile);
            options.Scopes.Add(OpenIddictConstants.Claims.Roles);
        }
예제 #6
0
        private async Task <bool> TryExtendRefreshTokenAsync(
            [NotNull] object token, [NotNull] AuthenticationTicket ticket, [NotNull] OpenIddictServerOptions options)
        {
            var identifier = ticket.GetProperty(OpenIddictConstants.Properties.InternalTokenId);

            Debug.Assert(!string.IsNullOrEmpty(identifier), "The token identifier shouldn't be null or empty.");

            try
            {
                // Compute the new expiration date of the refresh token.
                var lifetime = ticket.GetRefreshTokenLifetime() ?? options.RefreshTokenLifetime;
                if (lifetime != null)
                {
                    // Note: the request cancellation token is deliberately not used here to ensure the caller
                    // cannot prevent this operation from being executed by resetting the TCP connection.
                    var date = options.SystemClock.UtcNow + lifetime.Value;
                    await _tokenManager.ExtendAsync(token, date);

                    _logger.LogInformation("The expiration date of the refresh token '{Identifier}' " +
                                           "was automatically updated: {Date}.", identifier, date);
                }

                else if (await _tokenManager.GetExpirationDateAsync(token) != null)
                {
                    // Note: the request cancellation token is deliberately not used here to ensure the caller
                    // cannot prevent this operation from being executed by resetting the TCP connection.
                    await _tokenManager.ExtendAsync(token, date : null);

                    _logger.LogInformation("The expiration date of the refresh token '{Identifier}' was removed.", identifier);
                }

                return(true);
            }

            catch (OpenIddictException exception) when(exception.Reason == OpenIddictConstants.Exceptions.ConcurrencyError)
            {
                _logger.LogDebug(exception, "A concurrency exception occurred while trying to update the " +
                                 "expiration date of the token '{Identifier}'.", identifier);

                return(false);
            }

            catch (Exception exception)
            {
                _logger.LogWarning(exception, "An exception occurred while trying to update the " +
                                   "expiration date of the token '{Identifier}'.", identifier);

                return(false);
            }
        }
예제 #7
0
        public void Configure(string name, OpenIddictServerOptions options)
        {
            // Ignore OpenIddict handler instances that don't correspond to the instance managed by the OpenID module.
            if (!string.Equals(name, OpenIddictServerDefaults.AuthenticationScheme, StringComparison.Ordinal))
            {
                return;
            }

            var settings = GetServerSettingsAsync().GetAwaiter().GetResult();

            if (settings == null)
            {
                return;
            }

            options.ApplicationCanDisplayErrors = true;
            options.EnableRequestCaching        = true;
            options.IgnoreScopePermissions      = true;
            options.UseRollingTokens            = settings.UseRollingTokens;
            options.AllowInsecureHttp           = settings.TestingModeEnabled;

            foreach (var key in _serverService.GetSigningKeysAsync().GetAwaiter().GetResult())
            {
                options.SigningCredentials.AddKey(key);
            }

            if (!string.IsNullOrEmpty(settings.Authority))
            {
                options.Issuer = new Uri(settings.Authority, UriKind.Absolute);
            }

            if (settings.AccessTokenFormat == OpenIdServerSettings.TokenFormat.JWT)
            {
                options.AccessTokenHandler = new JwtSecurityTokenHandler();
            }

            options.AuthorizationEndpointPath = settings.AuthorizationEndpointPath;
            options.LogoutEndpointPath        = settings.LogoutEndpointPath;
            options.TokenEndpointPath         = settings.TokenEndpointPath;
            options.UserinfoEndpointPath      = settings.UserinfoEndpointPath;

            options.GrantTypes.Clear();
            options.GrantTypes.UnionWith(settings.GrantTypes);

            options.Scopes.Add(OpenIddictConstants.Scopes.Email);
            options.Scopes.Add(OpenIddictConstants.Scopes.Phone);
            options.Scopes.Add(OpenIddictConstants.Scopes.Profile);
            options.Scopes.Add(OpenIddictConstants.Claims.Roles);
        }
예제 #8
0
        public void Should_configure_existing_keys()
        {
            A.CallTo(() => store.ReadAsync(A <DomainId> ._, default))
            .Returns((ExistingKey(), true, 0));

            var options = new OpenIddictServerOptions();

            sut.Configure(options);

            Assert.NotEmpty(options.SigningCredentials);
            Assert.NotEmpty(options.EncryptionCredentials);

            A.CallTo(() => store.WriteAsync(A <DomainId> ._, A <DefaultKeyStore.State> ._, 0, 0, default))
            .MustNotHaveHappened();
        }
예제 #9
0
        public void Should_configure_existing_keys_when_initial_setup_failed()
        {
            A.CallTo(() => store.ReadAsync(A <DomainId> ._, default))
            .Returns((null !, true, 0)).Once()
            .Then
            .Returns((ExistingKey(), true, 0));

            A.CallTo(() => store.WriteAsync(A <DomainId> ._, A <DefaultKeyStore.State> ._, 0, 0, default))
            .Throws(new InconsistentStateException(0, 0));

            var options = new OpenIddictServerOptions();

            sut.Configure(options);

            Assert.NotEmpty(options.SigningCredentials);
            Assert.NotEmpty(options.EncryptionCredentials);

            A.CallTo(() => store.WriteAsync(A <DomainId> ._, A <DefaultKeyStore.State> ._, 0, 0, default))
            .MustHaveHappened();
        }
예제 #10
0
 public void Configure(string name, OpenIddictServerOptions options)
 {
 }
예제 #11
0
        private async Task <string> CreateTokenAsync(
            [NotNull] string type, [NotNull] AuthenticationTicket ticket,
            [NotNull] OpenIddictServerOptions options,
            [NotNull] OpenIdConnectRequest request,
            [NotNull] ISecureDataFormat <AuthenticationTicket> format)
        {
            Debug.Assert(!(options.DisableTokenStorage && options.UseReferenceTokens),
                         "Token storage cannot be disabled when using reference tokens.");

            Debug.Assert(type == OpenIdConnectConstants.TokenUsages.AccessToken ||
                         type == OpenIdConnectConstants.TokenUsages.AuthorizationCode ||
                         type == OpenIdConnectConstants.TokenUsages.RefreshToken,
                         "Only authorization codes, access and refresh tokens should be created using this method.");

            // When sliding expiration is disabled, the expiration date of generated refresh tokens is fixed
            // and must exactly match the expiration date of the refresh token used in the token request.
            if (request.IsTokenRequest() && request.IsRefreshTokenGrantType() &&
                !options.UseSlidingExpiration && type == OpenIdConnectConstants.TokenUsages.RefreshToken)
            {
                var properties = request.GetProperty <AuthenticationTicket>(
                    OpenIddictConstants.Properties.AuthenticationTicket)?.Properties;
                Debug.Assert(properties != null, "The authentication properties shouldn't be null.");

                ticket.Properties.ExpiresUtc = properties.ExpiresUtc;
            }

            if (options.DisableTokenStorage)
            {
                return(null);
            }

            var descriptor = new OpenIddictTokenDescriptor
            {
                AuthorizationId = ticket.GetProperty(OpenIddictConstants.Properties.InternalAuthorizationId),
                CreationDate    = ticket.Properties.IssuedUtc,
                ExpirationDate  = ticket.Properties.ExpiresUtc,
                Principal       = ticket.Principal,
                Status          = OpenIddictConstants.Statuses.Valid,
                Subject         = ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.Subject),
                Type            = type
            };

            foreach (var property in ticket.Properties.Items)
            {
                descriptor.Properties.Add(property);
            }

            string result = null;

            // When reference tokens are enabled or when the token is an authorization code or a
            // refresh token, remove the unnecessary properties from the authentication ticket.
            if (options.UseReferenceTokens ||
                (type == OpenIdConnectConstants.TokenUsages.AuthorizationCode ||
                 type == OpenIdConnectConstants.TokenUsages.RefreshToken))
            {
                ticket.Properties.IssuedUtc = ticket.Properties.ExpiresUtc = null;
                ticket.RemoveProperty(OpenIddictConstants.Properties.InternalAuthorizationId)
                .RemoveProperty(OpenIddictConstants.Properties.InternalTokenId);
            }

            // If reference tokens are enabled, create a new entry for
            // authorization codes, refresh tokens and access tokens.
            if (options.UseReferenceTokens)
            {
                // Note: the data format is automatically replaced at startup time to ensure
                // that encrypted tokens stored in the database cannot be considered as
                // valid tokens if the developer decides to disable reference tokens support.
                descriptor.Payload = format.Protect(ticket);

                // Generate a new crypto-secure random identifier that will be
                // substituted to the ciphertext returned by the data format.
                var bytes = new byte[256 / 8];
                options.RandomNumberGenerator.GetBytes(bytes);
                result = Base64UrlEncoder.Encode(bytes);

                // Obfuscate the reference identifier so it can be safely stored in the databse.
                descriptor.ReferenceId = await _tokenManager.ObfuscateReferenceIdAsync(result);
            }

            // Otherwise, only create a token metadata entry for authorization codes and refresh tokens.
            else if (type != OpenIdConnectConstants.TokenUsages.AuthorizationCode &&
                     type != OpenIdConnectConstants.TokenUsages.RefreshToken)
            {
                return(null);
            }

            // If the client application is known, associate it with the token.
            if (!string.IsNullOrEmpty(request.ClientId))
            {
                var application = request.GetProperty($"{OpenIddictConstants.Properties.Application}:{request.ClientId}");
                Debug.Assert(application != null, "The client application shouldn't be null.");

                descriptor.ApplicationId = await _applicationManager.GetIdAsync(application);
            }

            // If a null value was returned by CreateAsync(), return immediately.

            // Note: the request cancellation token is deliberately not used here to ensure the caller
            // cannot prevent this operation from being executed by resetting the TCP connection.
            var token = await _tokenManager.CreateAsync(descriptor);

            if (token == null)
            {
                return(null);
            }

            // Throw an exception if the token identifier can't be resolved.
            var identifier = await _tokenManager.GetIdAsync(token);

            if (string.IsNullOrEmpty(identifier))
            {
                throw new InvalidOperationException("The unique key associated with a refresh token cannot be null or empty.");
            }

            // Dynamically set the creation and expiration dates.
            ticket.Properties.IssuedUtc  = descriptor.CreationDate;
            ticket.Properties.ExpiresUtc = descriptor.ExpirationDate;

            // Restore the token/authorization identifiers using the identifiers attached with the database entry.
            ticket.SetProperty(OpenIddictConstants.Properties.InternalTokenId, identifier)
            .SetProperty(OpenIddictConstants.Properties.InternalAuthorizationId, descriptor.AuthorizationId);

            if (!string.IsNullOrEmpty(result))
            {
                _logger.LogTrace("A new reference token was successfully generated and persisted " +
                                 "in the database: {Token} ; {Claims} ; {Properties}.",
                                 result, ticket.Principal.Claims, ticket.Properties.Items);
            }

            return(result);
        }
예제 #12
0
        private async Task <AuthenticationTicket> ReceiveTokenAsync(
            [NotNull] string type, [NotNull] string value,
            [NotNull] OpenIddictServerOptions options,
            [NotNull] OpenIdConnectRequest request,
            [NotNull] ISecureDataFormat <AuthenticationTicket> format)
        {
            Debug.Assert(!(options.DisableTokenStorage && options.UseReferenceTokens),
                         "Token revocation cannot be disabled when using reference tokens.");

            Debug.Assert(type == OpenIdConnectConstants.TokenUsages.AccessToken ||
                         type == OpenIdConnectConstants.TokenUsages.AuthorizationCode ||
                         type == OpenIdConnectConstants.TokenUsages.RefreshToken,
                         "Only authorization codes, access and refresh tokens should be validated using this method.");

            string identifier;
            AuthenticationTicket ticket;
            object token;

            if (options.UseReferenceTokens)
            {
                // For introspection or revocation requests, this method may be called more than once.
                // For reference tokens, this may result in multiple database calls being made.
                // To optimize that, the token is added to the request properties to indicate that
                // a database lookup was already made with the same identifier. If the marker exists,
                // the property value (that may be null) is used instead of making a database call.
                if (request.HasProperty($"{OpenIddictConstants.Properties.ReferenceToken}:{value}"))
                {
                    token = request.GetProperty($"{OpenIddictConstants.Properties.ReferenceToken}:{value}");
                }

                else
                {
                    // Retrieve the token entry from the database. If it
                    // cannot be found, assume the token is not valid.
                    token = await _tokenManager.FindByReferenceIdAsync(value);

                    // Store the token as a request property so it can be retrieved if this method is called another time.
                    request.AddProperty($"{OpenIddictConstants.Properties.ReferenceToken}:{value}", token);
                }

                if (token == null)
                {
                    _logger.LogInformation("The reference token corresponding to the '{Identifier}' " +
                                           "reference identifier cannot be found in the database.", value);

                    return(null);
                }

                identifier = await _tokenManager.GetIdAsync(token);

                if (string.IsNullOrEmpty(identifier))
                {
                    _logger.LogWarning("The identifier associated with the received token cannot be retrieved. " +
                                       "This may indicate that the token entry is corrupted.");

                    return(null);
                }

                // Extract the encrypted payload from the token. If it's null or empty,
                // assume the token is not a reference token and consider it as invalid.
                var payload = await _tokenManager.GetPayloadAsync(token);

                if (string.IsNullOrEmpty(payload))
                {
                    _logger.LogWarning("The ciphertext associated with the token '{Identifier}' cannot be retrieved. " +
                                       "This may indicate that the token is not a reference token.", identifier);

                    return(null);
                }

                ticket = format.Unprotect(payload);
                if (ticket == null)
                {
                    _logger.LogWarning("The ciphertext associated with the token '{Identifier}' cannot be decrypted. " +
                                       "This may indicate that the token entry is corrupted or tampered.",
                                       await _tokenManager.GetIdAsync(token));

                    return(null);
                }

                request.SetProperty($"{OpenIddictConstants.Properties.Token}:{identifier}", token);
            }

            else if (type == OpenIdConnectConstants.TokenUsages.AuthorizationCode ||
                     type == OpenIdConnectConstants.TokenUsages.RefreshToken)
            {
                ticket = format.Unprotect(value);
                if (ticket == null)
                {
                    _logger.LogTrace("The received token was invalid or malformed: {Token}.", value);

                    return(null);
                }

                identifier = ticket.GetProperty(OpenIddictConstants.Properties.InternalTokenId);
                if (string.IsNullOrEmpty(identifier))
                {
                    _logger.LogWarning("The identifier associated with the received token cannot be retrieved. " +
                                       "This may indicate that the token entry is corrupted.");

                    return(null);
                }

                // For introspection or revocation requests, this method may be called more than once.
                // For codes/refresh tokens, this may result in multiple database calls being made.
                // To optimize that, the token is added to the request properties to indicate that
                // a database lookup was already made with the same identifier. If the marker exists,
                // the property value (that may be null) is used instead of making a database call.
                if (request.HasProperty($"{OpenIddictConstants.Properties.Token}:{identifier}"))
                {
                    token = request.GetProperty($"{OpenIddictConstants.Properties.Token}:{identifier}");
                }

                // Otherwise, retrieve the authorization code/refresh token entry from the database.
                // If it cannot be found, assume the authorization code/refresh token is not valid.
                else
                {
                    token = await _tokenManager.FindByIdAsync(identifier);

                    // Store the token as a request property so it can be retrieved if this method is called another time.
                    request.AddProperty($"{OpenIddictConstants.Properties.Token}:{identifier}", token);
                }

                if (token == null)
                {
                    _logger.LogInformation("The token '{Identifier}' cannot be found in the database.", identifier);

                    return(null);
                }
            }

            else
            {
                return(null);
            }

            // Dynamically set the creation and expiration dates.
            ticket.Properties.IssuedUtc = await _tokenManager.GetCreationDateAsync(token);

            ticket.Properties.ExpiresUtc = await _tokenManager.GetExpirationDateAsync(token);

            // Restore the token/authorization identifiers using the identifiers attached with the database entry.
            ticket.SetProperty(OpenIddictConstants.Properties.InternalTokenId, identifier);
            ticket.SetProperty(OpenIddictConstants.Properties.InternalAuthorizationId,
                               await _tokenManager.GetAuthorizationIdAsync(token));

            _logger.LogTrace("The token '{Identifier}' was successfully decrypted and " +
                             "retrieved from the database: {Claims} ; {Properties}.",
                             identifier, ticket.Principal.Claims, ticket.Properties.Items);

            return(ticket);
        }
        /// <summary>
        /// Populates the default OpenID Connect server options and ensures
        /// that the configuration is in a consistent and valid state.
        /// </summary>
        /// <param name="name">The authentication scheme associated with the handler instance.</param>
        /// <param name="options">The options instance to initialize.</param>
        public void PostConfigure([NotNull] string name, [NotNull] OpenIddictServerOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentException("The options instance name cannot be null or empty.", nameof(name));
            }

            if (options.RandomNumberGenerator == null)
            {
                throw new InvalidOperationException("A random number generator must be registered.");
            }

            if (options.ProviderType == null || options.ProviderType != typeof(OpenIddictServerProvider))
            {
                throw new InvalidOperationException(new StringBuilder()
                                                    .AppendLine("OpenIddict can only be used with its built-in server provider.")
                                                    .AppendLine("This error may indicate that 'OpenIddictServerOptions.ProviderType' was manually set.")
                                                    .Append("To execute custom request handling logic, consider registering an event handler using ")
                                                    .Append("the generic 'services.AddOpenIddict().AddServer().AddEventHandler()' method.")
                                                    .ToString());
            }

            // When no distributed cache has been registered in the options,
            // try to resolve it from the dependency injection container.
            if (options.Cache == null)
            {
                options.Cache = _cache;
            }

            // If OpenIddict was configured to use reference tokens, replace the default access tokens/
            // authorization codes/refresh tokens formats using a specific data protector to ensure
            // that encrypted tokens stored in the database cannot be treated as valid tokens if the
            // reference tokens option is later turned off by the developer.
            if (options.UseReferenceTokens)
            {
                // Note: a default data protection provider is always registered by
                // the OpenID Connect server handler when none is explicitly set but
                // this initializer is registered to be invoked before ASOS' initializer.
                // To ensure the provider property is never null, it's manually set here.
                if (options.DataProtectionProvider == null)
                {
                    options.DataProtectionProvider = _dataProtectionProvider;
                }

                if (options.AccessTokenFormat == null)
                {
                    var protector = options.DataProtectionProvider.CreateProtector(
                        nameof(OpenIdConnectServerHandler),
                        nameof(options.AccessTokenFormat),
                        nameof(options.UseReferenceTokens), name);

                    options.AccessTokenFormat = new TicketDataFormat(protector);
                }

                if (options.AuthorizationCodeFormat == null)
                {
                    var protector = options.DataProtectionProvider.CreateProtector(
                        nameof(OpenIdConnectServerHandler),
                        nameof(options.AuthorizationCodeFormat),
                        nameof(options.UseReferenceTokens), name);

                    options.AuthorizationCodeFormat = new TicketDataFormat(protector);
                }

                if (options.RefreshTokenFormat == null)
                {
                    var protector = options.DataProtectionProvider.CreateProtector(
                        nameof(OpenIdConnectServerHandler),
                        nameof(options.RefreshTokenFormat),
                        nameof(options.UseReferenceTokens), name);

                    options.RefreshTokenFormat = new TicketDataFormat(protector);
                }
            }

            // Ensure at least one flow has been enabled.
            if (options.GrantTypes.Count == 0)
            {
                throw new InvalidOperationException("At least one OAuth2/OpenID Connect flow must be enabled.");
            }

            // Ensure the authorization endpoint has been enabled when
            // the authorization code or implicit grants are supported.
            if (!options.AuthorizationEndpointPath.HasValue && (options.GrantTypes.Contains(OpenIddictConstants.GrantTypes.AuthorizationCode) ||
                                                                options.GrantTypes.Contains(OpenIddictConstants.GrantTypes.Implicit)))
            {
                throw new InvalidOperationException("The authorization endpoint must be enabled to use the authorization code and implicit flows.");
            }

            // Ensure the token endpoint has been enabled when the authorization code,
            // client credentials, password or refresh token grants are supported.
            if (!options.TokenEndpointPath.HasValue && (options.GrantTypes.Contains(OpenIddictConstants.GrantTypes.AuthorizationCode) ||
                                                        options.GrantTypes.Contains(OpenIddictConstants.GrantTypes.ClientCredentials) ||
                                                        options.GrantTypes.Contains(OpenIddictConstants.GrantTypes.Password) ||
                                                        options.GrantTypes.Contains(OpenIddictConstants.GrantTypes.RefreshToken)))
            {
                throw new InvalidOperationException(
                          "The token endpoint must be enabled to use the authorization code, client credentials, password and refresh token flows.");
            }

            if (options.EnableRequestCaching && options.RequestCachingPolicy == null)
            {
                throw new InvalidOperationException("A caching policy must be specified when enabling request caching.");
            }

            if (options.RevocationEndpointPath.HasValue && options.DisableTokenStorage)
            {
                throw new InvalidOperationException("The revocation endpoint cannot be enabled when token storage is disabled.");
            }

            if (options.UseReferenceTokens && options.DisableTokenStorage)
            {
                throw new InvalidOperationException("Reference tokens cannot be used when disabling token storage.");
            }

            if (options.UseReferenceTokens && options.AccessTokenHandler != null)
            {
                throw new InvalidOperationException("Reference tokens cannot be used when configuring JWT as the access token format.");
            }

            if (options.UseSlidingExpiration && options.DisableTokenStorage && !options.UseRollingTokens)
            {
                throw new InvalidOperationException(
                          "Sliding expiration must be disabled when turning off token storage if rolling tokens are not used.");
            }

            if (options.AccessTokenHandler != null && options.SigningCredentials.Count == 0)
            {
                throw new InvalidOperationException(new StringBuilder()
                                                    .AppendLine("At least one signing key must be registered when using JWT as the access token format.")
                                                    .Append("Consider registering a certificate using 'services.AddOpenIddict().AddServer().AddSigningCertificate()' ")
                                                    .Append("or 'services.AddOpenIddict().AddServer().AddDevelopmentSigningCertificate()' or call ")
                                                    .Append("'services.AddOpenIddict().AddServer().AddEphemeralSigningKey()' to use an ephemeral key.")
                                                    .ToString());
            }

            // Ensure at least one asymmetric signing certificate/key was registered if the implicit flow was enabled.
            if (!options.SigningCredentials.Any(credentials => credentials.Key is AsymmetricSecurityKey) &&
                options.GrantTypes.Contains(OpenIddictConstants.GrantTypes.Implicit))
            {
                throw new InvalidOperationException(new StringBuilder()
                                                    .AppendLine("At least one asymmetric signing key must be registered when enabling the implicit flow.")
                                                    .Append("Consider registering a certificate using 'services.AddOpenIddict().AddServer().AddSigningCertificate()' ")
                                                    .Append("or 'services.AddOpenIddict().AddServer().AddDevelopmentSigningCertificate()' or call ")
                                                    .Append("'services.AddOpenIddict().AddServer().AddEphemeralSigningKey()' to use an ephemeral key.")
                                                    .ToString());
            }

            // Automatically add the offline_access scope if the refresh token grant has been enabled.
            if (options.GrantTypes.Contains(OpenIddictConstants.GrantTypes.RefreshToken))
            {
                options.Scopes.Add(OpenIddictConstants.Scopes.OfflineAccess);
            }
        }
 public void Configure(OpenIddictServerOptions options) => Debug.Fail("This infrastructure method shouldn't be called.");
예제 #15
0
 public void Configure(OpenIddictServerOptions options)
 {
 }
        private async Task <AuthenticationTicket> ReceiveTokenAsync(
            [NotNull] string type, [NotNull] string value,
            [NotNull] OpenIddictServerOptions options,
            [NotNull] OpenIdConnectRequest request,
            [NotNull] ISecureDataFormat <AuthenticationTicket> format)
        {
            Debug.Assert(!(options.DisableTokenStorage && options.UseReferenceTokens),
                         "Token revocation cannot be disabled when using reference tokens.");

            Debug.Assert(type == OpenIdConnectConstants.TokenUsages.AccessToken ||
                         type == OpenIdConnectConstants.TokenUsages.AuthorizationCode ||
                         type == OpenIdConnectConstants.TokenUsages.RefreshToken,
                         "Only authorization codes, access and refresh tokens should be validated using this method.");

            string identifier;
            AuthenticationTicket ticket;
            object token;

            if (options.UseReferenceTokens)
            {
                token = await _tokenManager.FindByReferenceIdAsync(value);

                if (token == null)
                {
                    _logger.LogInformation("The reference token corresponding to the '{Identifier}' " +
                                           "reference identifier cannot be found in the database.", value);

                    return(null);
                }

                // Optimization: avoid extracting/decrypting the token payload
                // (that relies on a format specific to the token type requested)
                // if the token type associated with the token entry isn't valid.
                var usage = await _tokenManager.GetTypeAsync(token);

                if (string.IsNullOrEmpty(usage))
                {
                    _logger.LogWarning("The token type associated with the received token cannot be retrieved. " +
                                       "This may indicate that the token entry is corrupted.");

                    return(null);
                }

                if (!string.Equals(usage, type, StringComparison.OrdinalIgnoreCase))
                {
                    _logger.LogWarning("The token type '{ActualType}' associated with the database entry doesn't match " +
                                       "the expected type: {ExpectedType}.", await _tokenManager.GetTypeAsync(token), type);

                    return(null);
                }

                identifier = await _tokenManager.GetIdAsync(token);

                if (string.IsNullOrEmpty(identifier))
                {
                    _logger.LogWarning("The identifier associated with the received token cannot be retrieved. " +
                                       "This may indicate that the token entry is corrupted.");

                    return(null);
                }

                // Extract the encrypted payload from the token. If it's null or empty,
                // assume the token is not a reference token and consider it as invalid.
                var payload = await _tokenManager.GetPayloadAsync(token);

                if (string.IsNullOrEmpty(payload))
                {
                    _logger.LogWarning("The ciphertext associated with the token '{Identifier}' cannot be retrieved. " +
                                       "This may indicate that the token is not a reference token.", identifier);

                    return(null);
                }

                ticket = format.Unprotect(payload);
                if (ticket == null)
                {
                    _logger.LogWarning("The ciphertext associated with the token '{Identifier}' cannot be decrypted. " +
                                       "This may indicate that the token entry is corrupted or tampered.",
                                       await _tokenManager.GetIdAsync(token));

                    return(null);
                }
            }

            else if (type == OpenIdConnectConstants.TokenUsages.AuthorizationCode ||
                     type == OpenIdConnectConstants.TokenUsages.RefreshToken)
            {
                ticket = format.Unprotect(value);
                if (ticket == null)
                {
                    _logger.LogTrace("The received token was invalid or malformed: {Token}.", value);

                    return(null);
                }

                identifier = ticket.GetInternalTokenId();
                if (string.IsNullOrEmpty(identifier))
                {
                    _logger.LogWarning("The identifier associated with the received token cannot be retrieved. " +
                                       "This may indicate that the token entry is corrupted.");

                    return(null);
                }

                token = await _tokenManager.FindByIdAsync(identifier);

                if (token == null)
                {
                    _logger.LogInformation("The token '{Identifier}' cannot be found in the database.", identifier);

                    return(null);
                }
            }

            else
            {
                return(null);
            }

            // Dynamically set the creation and expiration dates.
            ticket.Properties.IssuedUtc = await _tokenManager.GetCreationDateAsync(token);

            ticket.Properties.ExpiresUtc = await _tokenManager.GetExpirationDateAsync(token);

            // Restore the token/authorization identifiers using the identifiers attached with the database entry.
            ticket.SetInternalAuthorizationId(await _tokenManager.GetAuthorizationIdAsync(token))
            .SetInternalTokenId(identifier);

            _logger.LogTrace("The token '{Identifier}' was successfully decrypted and " +
                             "retrieved from the database: {Claims} ; {Properties}.",
                             identifier, ticket.Principal.Claims, ticket.Properties.Items);

            return(ticket);
        }
        public void Configure(OpenIddictServerOptions options)
        {
            var settings = GetServerSettingsAsync().GetAwaiter().GetResult();

            if (settings == null)
            {
                return;
            }

            options.Issuer = settings.Authority;
            options.DisableAccessTokenEncryption = settings.DisableAccessTokenEncryption;
            options.DisableRollingRefreshTokens  = settings.DisableRollingRefreshTokens;
            options.UseReferenceAccessTokens     = settings.UseReferenceAccessTokens;

            foreach (var key in _serverService.GetEncryptionKeysAsync().GetAwaiter().GetResult())
            {
                options.EncryptionCredentials.Add(new EncryptingCredentials(key,
                                                                            SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes256CbcHmacSha512));
            }

            foreach (var key in _serverService.GetSigningKeysAsync().GetAwaiter().GetResult())
            {
                options.SigningCredentials.Add(new SigningCredentials(key, SecurityAlgorithms.RsaSha256));
            }

            if (settings.AuthorizationEndpointPath.HasValue)
            {
                options.AuthorizationEndpointUris.Add(new Uri(settings.AuthorizationEndpointPath.Value, UriKind.Relative));
            }

            if (settings.LogoutEndpointPath.HasValue)
            {
                options.LogoutEndpointUris.Add(new Uri(settings.LogoutEndpointPath.Value, UriKind.Relative));
            }

            if (settings.TokenEndpointPath.HasValue)
            {
                options.TokenEndpointUris.Add(new Uri(settings.TokenEndpointPath.Value, UriKind.Relative));
            }

            if (settings.UserinfoEndpointPath.HasValue)
            {
                options.UserinfoEndpointUris.Add(new Uri(settings.UserinfoEndpointPath.Value, UriKind.Relative));
            }

            if (settings.IntrospectionEndpointPath.HasValue)
            {
                options.IntrospectionEndpointUris.Add(new Uri(settings.IntrospectionEndpointPath.Value, UriKind.Relative));
            }

            if (settings.RevocationEndpointPath.HasValue)
            {
                options.RevocationEndpointUris.Add(new Uri(settings.RevocationEndpointPath.Value, UriKind.Relative));
            }

            // For now, response types and response modes are not directly
            // configurable and are inferred from the selected flows.
            if (settings.AllowAuthorizationCodeFlow)
            {
                options.CodeChallengeMethods.Add(CodeChallengeMethods.Sha256);

                options.GrantTypes.Add(GrantTypes.AuthorizationCode);

                options.ResponseModes.Add(ResponseModes.FormPost);
                options.ResponseModes.Add(ResponseModes.Fragment);
                options.ResponseModes.Add(ResponseModes.Query);

                options.ResponseTypes.Add(ResponseTypes.Code);
            }

            if (settings.AllowClientCredentialsFlow)
            {
                options.GrantTypes.Add(GrantTypes.ClientCredentials);
            }

            if (settings.AllowHybridFlow)
            {
                options.CodeChallengeMethods.Add(CodeChallengeMethods.Sha256);

                options.GrantTypes.Add(GrantTypes.AuthorizationCode);
                options.GrantTypes.Add(GrantTypes.Implicit);

                options.ResponseModes.Add(ResponseModes.FormPost);
                options.ResponseModes.Add(ResponseModes.Fragment);

                options.ResponseTypes.Add(ResponseTypes.Code + ' ' + ResponseTypes.IdToken);
                options.ResponseTypes.Add(ResponseTypes.Code + ' ' + ResponseTypes.IdToken + ' ' + ResponseTypes.Token);
                options.ResponseTypes.Add(ResponseTypes.Code + ' ' + ResponseTypes.Token);
            }

            if (settings.AllowImplicitFlow)
            {
                options.GrantTypes.Add(GrantTypes.Implicit);

                options.ResponseModes.Add(ResponseModes.FormPost);
                options.ResponseModes.Add(ResponseModes.Fragment);

                options.ResponseTypes.Add(ResponseTypes.IdToken);
                options.ResponseTypes.Add(ResponseTypes.IdToken + ' ' + ResponseTypes.Token);
                options.ResponseTypes.Add(ResponseTypes.Token);
            }

            if (settings.AllowPasswordFlow)
            {
                options.GrantTypes.Add(GrantTypes.Password);
            }

            if (settings.AllowRefreshTokenFlow)
            {
                options.GrantTypes.Add(GrantTypes.RefreshToken);

                options.Scopes.Add(Scopes.OfflineAccess);
            }

            options.RequireProofKeyForCodeExchange = settings.RequireProofKeyForCodeExchange;

            options.Scopes.Add(Scopes.Email);
            options.Scopes.Add(Scopes.Phone);
            options.Scopes.Add(Scopes.Profile);
            options.Scopes.Add(Scopes.Roles);
        }