/* * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) * See https://github.com/openiddict/openiddict-core for more information concerning * the license and the contributors participating to this project. */ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Tokens; using OpenIddict.Abstractions; using static OpenIddict.Abstractions.OpenIddictConstants; using SR = OpenIddict.Abstractions.OpenIddictResources; namespace OpenIddict.Server { /// /// Provides various settings needed to configure the OpenIddict server handler. /// public class OpenIddictServerOptions { /// /// Gets or sets the optional base address used to uniquely identify the authorization server. /// The URI must be absolute and may contain a path, but no query string or fragment part. /// public Uri? Issuer { get; set; } /// /// Gets the list of encryption credentials used by the OpenIddict server services. /// Multiple credentials can be added to support key rollover, but if X.509 keys /// are used, at least one of them must have a valid creation/expiration date. /// Note: the encryption credentials are not used to protect/unprotect tokens issued /// by ASP.NET Core Data Protection, that uses its own key ring, configured separately. /// /// /// Note: OpenIddict automatically sorts the credentials based on the following algorithm: /// /// Symmetric keys are always preferred when they can be used for the operation (e.g token encryption). /// X.509 keys are always preferred to non-X.509 asymmetric keys. /// X.509 keys with the furthest expiration date are preferred. /// X.509 keys whose backing certificate is not yet valid are never preferred. /// /// public List EncryptionCredentials { get; } = new(); /// /// Gets the list of signing credentials used by the OpenIddict server services. /// Multiple credentials can be added to support key rollover, but if X.509 keys /// are used, at least one of them must have a valid creation/expiration date. /// Note: the signing credentials are not used to protect/unprotect tokens issued /// by ASP.NET Core Data Protection, that uses its own key ring, configured separately. /// /// /// Note: OpenIddict automatically sorts the credentials based on the following algorithm: /// /// Symmetric keys are always preferred when they can be used for the operation (e.g token signing). /// X.509 keys are always preferred to non-X.509 asymmetric keys. /// X.509 keys with the furthest expiration date are preferred. /// X.509 keys whose backing certificate is not yet valid are never preferred. /// /// public List SigningCredentials { get; } = new(); /// /// Gets the absolute and relative URIs associated to the authorization endpoint. /// public List AuthorizationEndpointUris { get; } = new(); /// /// Gets the absolute and relative URIs associated to the configuration endpoint. /// public List ConfigurationEndpointUris { get; } = new() { new Uri("/.well-known/openid-configuration", UriKind.Relative), new Uri("/.well-known/oauth-authorization-server", UriKind.Relative) }; /// /// Gets the absolute and relative URIs associated to the cryptography endpoint. /// public List CryptographyEndpointUris { get; } = new List { new Uri("/.well-known/jwks", UriKind.Relative) }; /// /// Gets the absolute and relative URIs associated to the device endpoint. /// public List DeviceEndpointUris { get; } = new(); /// /// Gets the absolute and relative URIs associated to the introspection endpoint. /// public List IntrospectionEndpointUris { get; } = new(); /// /// Gets the absolute and relative URIs associated to the logout endpoint. /// public List LogoutEndpointUris { get; } = new(); /// /// Gets the absolute and relative URIs associated to the revocation endpoint. /// public List RevocationEndpointUris { get; } = new(); /// /// Gets the absolute and relative URIs associated to the token endpoint. /// public List TokenEndpointUris { get; } = new(); /// /// Gets the absolute and relative URIs associated to the userinfo endpoint. /// public List UserinfoEndpointUris { get; } = new(); /// /// Gets the absolute and relative URIs associated to the verification endpoint. /// public List VerificationEndpointUris { get; } = new(); /// /// Gets or sets the JWT handler used to protect and unprotect tokens. /// public JsonWebTokenHandler JsonWebTokenHandler { get; set; } = new() { SetDefaultTimesOnTokenCreation = false }; /// /// Gets the token validation parameters used by the OpenIddict server services. /// public TokenValidationParameters TokenValidationParameters { get; } = new() { AuthenticationType = TokenValidationParameters.DefaultAuthenticationType, ClockSkew = TimeSpan.Zero, NameClaimType = OpenIddictConstants.Claims.Name, RoleClaimType = OpenIddictConstants.Claims.Role, // In previous versions of OpenIddict (1.x and 2.x), all the JWT tokens (access and identity tokens) // were issued with the generic "typ": "JWT" header. To prevent confused deputy and token substitution // attacks, a special "token_usage" claim was added to the JWT payload to convey the actual token type. // This validator overrides the default logic used by IdentityModel to resolve the type from this claim. TypeValidator = (type, token, parameters) => { // If available, try to resolve the actual type from the "token_usage" claim. if (((JsonWebToken) token).TryGetPayloadValue(OpenIddictConstants.Claims.TokenUsage, out string usage)) { type = usage switch { TokenTypeHints.AccessToken => JsonWebTokenTypes.AccessToken, TokenTypeHints.IdToken => JsonWebTokenTypes.IdentityToken, _ => throw new NotSupportedException(SR.GetResourceString(SR.ID0269)) }; } // At this point, throw an exception if the type cannot be resolved from the "typ" header // (provided via the type delegate parameter) or inferred from the token_usage claim. if (string.IsNullOrEmpty(type)) { throw new SecurityTokenInvalidTypeException(SR.GetResourceString(SR.ID0270)); } // Note: unlike IdentityModel, this custom validator deliberately uses case-insensitive comparisons. if (parameters.ValidTypes is not null && parameters.ValidTypes.Any() && !parameters.ValidTypes.Contains(type, StringComparer.OrdinalIgnoreCase)) { throw new SecurityTokenInvalidTypeException(SR.GetResourceString(SR.ID0271)) { InvalidType = type }; } return type; }, // Note: audience and lifetime are manually validated by OpenIddict itself. ValidateAudience = false, ValidateLifetime = false }; /// /// Gets or sets the period of time authorization codes remain valid after being issued. The default value is 5 minutes. /// While not recommended, this property can be set to null to issue codes that never expire. /// public TimeSpan? AuthorizationCodeLifetime { get; set; } = TimeSpan.FromMinutes(5); /// /// Gets or sets the period of time access tokens remain valid after being issued. The default value is 1 hour. /// The client application is expected to refresh or acquire a new access token after the token has expired. /// While not recommended, this property can be set to null to issue access tokens that never expire. /// public TimeSpan? AccessTokenLifetime { get; set; } = TimeSpan.FromHours(1); /// /// Gets or sets the period of time device codes remain valid after being issued. The default value is 10 minutes. /// The client application is expected to start a whole new authentication flow after the device code has expired. /// While not recommended, this property can be set to null to issue codes that never expire. /// Note: the same value should be chosen for both and this property. /// public TimeSpan? DeviceCodeLifetime { get; set; } = TimeSpan.FromMinutes(10); /// /// Gets or sets the period of time identity tokens remain valid after being issued. The default value is 20 minutes. /// The client application is expected to refresh or acquire a new identity token after the token has expired. /// While not recommended, this property can be set to null to issue identity tokens that never expire. /// public TimeSpan? IdentityTokenLifetime { get; set; } = TimeSpan.FromMinutes(20); /// /// Gets or sets the period of time refresh tokens remain valid after being issued. The default value is 14 days. /// The client application is expected to start a whole new authentication flow after the refresh token has expired. /// While not recommended, this property can be set to null to issue refresh tokens that never expire. /// public TimeSpan? RefreshTokenLifetime { get; set; } = TimeSpan.FromDays(14); /// /// Gets or sets the period of time rolling refresh tokens marked as redeemed can still be /// used to make concurrent refresh token requests. The default value is 15 seconds. /// public TimeSpan? RefreshTokenReuseLeeway { get; set; } = TimeSpan.FromSeconds(15); /// /// Gets or sets the period of time user codes remain valid after being issued. The default value is 10 minutes. /// The client application is expected to start a whole new authentication flow after the user code has expired. /// While not recommended, this property can be set to null to issue codes that never expire. /// Note: the same value should be chosen for both and this property. /// public TimeSpan? UserCodeLifetime { get; set; } = TimeSpan.FromMinutes(10); /// /// Gets or sets a boolean indicating whether the degraded mode is enabled. When this degraded mode /// is enabled, all the security checks that depend on the OpenIddict core managers are disabled. /// This option MUST be enabled with extreme caution and custom handlers MUST be registered to /// properly validate OpenID Connect requests. /// [EditorBrowsable(EditorBrowsableState.Advanced)] public bool EnableDegradedMode { get; set; } /// /// Gets the list of the handlers responsible of processing the OpenIddict server operations. /// Note: the list is automatically sorted based on the order assigned to each handler descriptor. /// As such, it MUST NOT be mutated after options initialization to preserve the exact order. /// public List Handlers { get; } = new(OpenIddictServerHandlers.DefaultHandlers); /// /// Gets or sets a boolean determining whether client identification is optional. /// Enabling this option allows client applications to communicate with the token, /// introspection and revocation endpoints without having to send their client identifier. /// public bool AcceptAnonymousClients { get; set; } /// /// Gets the OAuth 2.0/OpenID Connect claims supported by this application. /// public HashSet Claims { get; } = new(StringComparer.Ordinal) { OpenIddictConstants.Claims.Audience, OpenIddictConstants.Claims.ExpiresAt, OpenIddictConstants.Claims.IssuedAt, OpenIddictConstants.Claims.Issuer, OpenIddictConstants.Claims.Subject }; /// /// Gets or sets a boolean indicating whether access token encryption should be disabled. /// Disabling encryption is NOT recommended and SHOULD only be done when issuing tokens /// to third-party resource servers/APIs you don't control and don't fully trust. /// Note: disabling encryption has no effect when using ASP.NET Core Data Protection. /// public bool DisableAccessTokenEncryption { get; set; } /// /// Gets or sets a boolean indicating whether authorization storage should be disabled. /// When disabled, ad-hoc authorizations are not created when an authorization code or /// refresh token is issued and can't be revoked to prevent associated tokens from being used. /// public bool DisableAuthorizationStorage { get; set; } /// /// Gets or sets a boolean indicating whether rolling tokens are disabled. /// When disabled, refresh tokens used in a token request are not marked /// as redeemed and can still be used until they expire. Disabling /// rolling refresh tokens is NOT recommended, for security reasons. /// public bool DisableRollingRefreshTokens { get; set; } /// /// Gets or sets a boolean indicating whether sliding expiration is disabled /// for refresh tokens. When this option is set to , /// refresh tokens are issued with a fixed expiration date: when they expire, /// a complete authorization flow must be started to retrieve a new refresh token. /// public bool DisableSlidingRefreshTokenExpiration { get; set; } /// /// Gets or sets a boolean indicating whether token storage should be disabled. /// When disabled, no database entry is created for the tokens and codes /// returned by OpenIddict. Using this option is generally NOT recommended /// as it prevents the tokens and codes from being revoked (if needed). /// public bool DisableTokenStorage { get; set; } /// /// Gets or sets a boolean indicating whether scope validation is disabled. /// public bool DisableScopeValidation { get; set; } /// /// Gets the OAuth 2.0 code challenge methods enabled for this application. /// By default, only the S256 method is allowed (if the code flow is enabled). /// public HashSet CodeChallengeMethods { get; } = new(StringComparer.Ordinal); /// /// Gets the OAuth 2.0/OpenID Connect flows enabled for this application. /// public HashSet GrantTypes { get; } = new(StringComparer.Ordinal); /// /// Gets or sets a boolean indicating whether PKCE must be used by client applications /// when requesting an authorization code (e.g when using the code or hybrid flows). /// If this property is set to , authorization requests that /// lack the code_challenge will be automatically rejected by OpenIddict. /// public bool RequireProofKeyForCodeExchange { get; set; } /// /// Gets the OAuth 2.0/OpenID Connect response types enabled for this application. /// [EditorBrowsable(EditorBrowsableState.Advanced)] public HashSet ResponseTypes { get; } = new(StringComparer.Ordinal); /// /// Gets the OAuth 2.0/OpenID Connect response modes enabled for this application. /// [EditorBrowsable(EditorBrowsableState.Advanced)] public HashSet ResponseModes { get; } = new(StringComparer.Ordinal); /// /// Gets or sets a boolean indicating whether endpoint permissions should be ignored. /// Setting this property to is NOT recommended. /// public bool IgnoreEndpointPermissions { get; set; } /// /// Gets or sets a boolean indicating whether grant type permissions should be ignored. /// Setting this property to is NOT recommended. /// public bool IgnoreGrantTypePermissions { get; set; } /// /// Gets or sets a boolean indicating whether response type permissions should be ignored. /// Setting this property to is NOT recommended. /// public bool IgnoreResponseTypePermissions { get; set; } /// /// Gets or sets a boolean indicating whether scope permissions should be ignored. /// Setting this property to is NOT recommended. /// public bool IgnoreScopePermissions { get; set; } /// /// Gets the OAuth 2.0/OpenID Connect scopes enabled for this application. /// public HashSet Scopes { get; } = new(StringComparer.Ordinal) { OpenIddictConstants.Scopes.OpenId }; /// /// Gets or sets a boolean indicating whether reference access tokens should be used. /// When set to , the token payload is stored in the database /// and a crypto-secure random identifier is returned to the client application. /// Enabling this option is useful when storing a very large number of claims /// in the tokens, but it is RECOMMENDED to enable column encryption /// in the database or use the ASP.NET Core Data Protection integration, /// that provides additional protection against token leakage. /// public bool UseReferenceAccessTokens { get; set; } /// /// Gets or sets a boolean indicating whether reference refresh tokens should be used. /// When set to , the token payload is stored in the database /// and a crypto-secure random identifier is returned to the client application. /// Enabling this option is useful when storing a very large number of claims /// in the tokens, but it is RECOMMENDED to enable column encryption /// in the database or use the ASP.NET Core Data Protection integration, /// that provides additional protection against token leakage. /// public bool UseReferenceRefreshTokens { get; set; } } }