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); }
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); } }
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); }
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); } }
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); }
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(); }
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(); }
public void Configure(string name, OpenIddictServerOptions options) { }
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); }
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.");
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); }