// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using Microsoft.AspNetCore.Authentication.Internal; using Microsoft.AspNetCore.Authentication.OAuth.Claims; using Microsoft.AspNetCore.Http; using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Tokens; namespace Microsoft.AspNetCore.Authentication.OpenIdConnect { /// /// Configuration options for /// public class OpenIdConnectOptions : RemoteAuthenticationOptions { private CookieBuilder _nonceCookieBuilder; /// /// Initializes a new /// /// /// Defaults: /// AddNonceToRequest: true. /// BackchannelTimeout: 1 minute. /// ProtocolValidator: new . /// RefreshOnIssuerKeyNotFound: true /// ResponseType: /// Scope: . /// TokenValidationParameters: new with AuthenticationScheme = authenticationScheme. /// UseTokenLifetime: false. /// public OpenIdConnectOptions() { CallbackPath = new PathString("/signin-oidc"); SignedOutCallbackPath = new PathString("/signout-callback-oidc"); RemoteSignOutPath = new PathString("/signout-oidc"); Events = new OpenIdConnectEvents(); Scope.Add("openid"); Scope.Add("profile"); ClaimActions.DeleteClaim("nonce"); ClaimActions.DeleteClaim("aud"); ClaimActions.DeleteClaim("azp"); ClaimActions.DeleteClaim("acr"); ClaimActions.DeleteClaim("amr"); ClaimActions.DeleteClaim("iss"); ClaimActions.DeleteClaim("iat"); ClaimActions.DeleteClaim("nbf"); ClaimActions.DeleteClaim("exp"); ClaimActions.DeleteClaim("at_hash"); ClaimActions.DeleteClaim("c_hash"); ClaimActions.DeleteClaim("auth_time"); ClaimActions.DeleteClaim("ipaddr"); ClaimActions.DeleteClaim("platf"); ClaimActions.DeleteClaim("ver"); // http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims ClaimActions.MapUniqueJsonKey("sub", "sub"); ClaimActions.MapUniqueJsonKey("name", "name"); ClaimActions.MapUniqueJsonKey("given_name", "given_name"); ClaimActions.MapUniqueJsonKey("family_name", "family_name"); ClaimActions.MapUniqueJsonKey("profile", "profile"); ClaimActions.MapUniqueJsonKey("email", "email"); _nonceCookieBuilder = new OpenIdConnectNonceCookieBuilder(this) { Name = OpenIdConnectDefaults.CookieNoncePrefix, HttpOnly = true, SameSite = SameSiteMode.None, SecurePolicy = CookieSecurePolicy.SameAsRequest, }; } /// /// Check that the options are valid. Should throw an exception if things are not ok. /// public override void Validate() { base.Validate(); if (MaxAge != null && MaxAge.Value < TimeSpan.Zero) { throw new InvalidOperationException("MaxAge must not be a negative TimeSpan."); } if (string.IsNullOrEmpty(ClientId)) { throw new ArgumentException("Options.ClientId must be provided", nameof(ClientId)); } if (!CallbackPath.HasValue) { throw new ArgumentException("Options.CallbackPath must be provided.", nameof(CallbackPath)); } if (ConfigurationManager == null) { throw new InvalidOperationException($"Provide {nameof(Authority)}, {nameof(MetadataAddress)}, " + $"{nameof(Configuration)}, or {nameof(ConfigurationManager)} to {nameof(OpenIdConnectOptions)}"); } } /// /// Gets or sets the Authority to use when making OpenIdConnect calls. /// public string Authority { get; set; } /// /// Gets or sets the 'client_id'. /// public string ClientId { get; set; } /// /// Gets or sets the 'client_secret'. /// public string ClientSecret { get; set; } /// /// Configuration provided directly by the developer. If provided, then MetadataAddress and the Backchannel properties /// will not be used. This information should not be updated during request processing. /// public OpenIdConnectConfiguration Configuration { get; set; } /// /// Responsible for retrieving, caching, and refreshing the configuration from metadata. /// If not provided, then one will be created using the MetadataAddress and Backchannel properties. /// public IConfigurationManager ConfigurationManager { get; set; } /// /// Boolean to set whether the handler should go to user info endpoint to retrieve additional claims or not after creating an identity from id_token received from token endpoint. /// The default is 'false'. /// public bool GetClaimsFromUserInfoEndpoint { get; set; } /// /// A collection of claim actions used to select values from the json user data and create Claims. /// public ClaimActionCollection ClaimActions { get; } = new ClaimActionCollection(); /// /// Gets or sets if HTTPS is required for the metadata address or authority. /// The default is true. This should be disabled only in development environments. /// public bool RequireHttpsMetadata { get; set; } = true; /// /// Gets or sets the discovery endpoint for obtaining metadata /// public string MetadataAddress { get; set; } /// /// Gets or sets the to notify when processing OpenIdConnect messages. /// public new OpenIdConnectEvents Events { get => (OpenIdConnectEvents)base.Events; set => base.Events = value; } /// /// Gets or sets the 'max_age'. If set the 'max_age' parameter will be sent with the authentication request. If the identity /// provider has not actively authenticated the user within the length of time specified, the user will be prompted to /// re-authenticate. By default no max_age is specified. /// public TimeSpan? MaxAge { get; set; } = null; /// /// Gets or sets the that is used to ensure that the 'id_token' received /// is valid per: http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation /// /// if 'value' is null. public OpenIdConnectProtocolValidator ProtocolValidator { get; set; } = new OpenIdConnectProtocolValidator() { RequireStateValidation = false, NonceLifetime = TimeSpan.FromMinutes(15) }; /// /// The request path within the application's base path where the user agent will be returned after sign out from the identity provider. /// See post_logout_redirect_uri from http://openid.net/specs/openid-connect-session-1_0.html#RedirectionAfterLogout. /// public PathString SignedOutCallbackPath { get; set; } /// /// The uri where the user agent will be redirected to after application is signed out from the identity provider. /// The redirect will happen after the SignedOutCallbackPath is invoked. /// /// This URI can be out of the application's domain. By default it points to the root. public string SignedOutRedirectUri { get; set; } = "/"; /// /// Gets or sets if a metadata refresh should be attempted after a SecurityTokenSignatureKeyNotFoundException. This allows for automatic /// recovery in the event of a signature key rollover. This is enabled by default. /// public bool RefreshOnIssuerKeyNotFound { get; set; } = true; /// /// Gets or sets the method used to redirect the user agent to the identity provider. /// public OpenIdConnectRedirectBehavior AuthenticationMethod { get; set; } = OpenIdConnectRedirectBehavior.RedirectGet; /// /// Gets or sets the 'resource'. /// public string Resource { get; set; } /// /// Gets or sets the 'response_mode'. /// public string ResponseMode { get; set; } = OpenIdConnectResponseMode.FormPost; /// /// Gets or sets the 'response_type'. /// public string ResponseType { get; set; } = OpenIdConnectResponseType.IdToken; /// /// Gets or sets the 'prompt'. /// public string Prompt { get; set; } /// /// Gets the list of permissions to request. /// public ICollection Scope { get; } = new HashSet(); /// /// Requests received on this path will cause the handler to invoke SignOut using the SignInScheme. /// public PathString RemoteSignOutPath { get; set; } /// /// The Authentication Scheme to use with SignOut on the SignOutPath. SignInScheme will be used if this /// is not set. /// public string SignOutScheme { get; set; } /// /// Gets or sets the type used to secure data handled by the handler. /// public ISecureDataFormat StateDataFormat { get; set; } /// /// Gets or sets the type used to secure strings used by the handler. /// public ISecureDataFormat StringDataFormat { get; set; } /// /// Gets or sets the used to validate identity tokens. /// public ISecurityTokenValidator SecurityTokenValidator { get; set; } = new JwtSecurityTokenHandler(); /// /// Gets or sets the parameters used to validate identity tokens. /// /// Contains the types and definitions required for validating a token. public TokenValidationParameters TokenValidationParameters { get; set; } = new TokenValidationParameters(); /// /// Indicates that the authentication session lifetime (e.g. cookies) should match that of the authentication token. /// If the token does not provide lifetime information then normal session lifetimes will be used. /// This is disabled by default. /// public bool UseTokenLifetime { get; set; } /// /// Indicates if requests to the CallbackPath may also be for other components. If enabled the handler will pass /// requests through that do not contain OpenIdConnect authentication responses. Disabling this and setting the /// CallbackPath to a dedicated endpoint may provide better error handling. /// This is disabled by default. /// public bool SkipUnrecognizedRequests { get; set; } = false; /// /// Indicates whether telemetry should be disabled. When this feature is enabled, /// the assembly version of the Microsoft IdentityModel packages is sent to the /// remote OpenID Connect provider as an authorization/logout request parameter. /// public bool DisableTelemetry { get; set; } /// /// Determines the settings used to create the nonce cookie before the /// cookie gets added to the response. /// /// /// The value of is treated as the prefix to the cookie name, and defaults to . /// public CookieBuilder NonceCookie { get => _nonceCookieBuilder; set => _nonceCookieBuilder = value ?? throw new ArgumentNullException(nameof(value)); } private class OpenIdConnectNonceCookieBuilder : RequestPathBaseCookieBuilder { private readonly OpenIdConnectOptions _options; public OpenIdConnectNonceCookieBuilder(OpenIdConnectOptions oidcOptions) { _options = oidcOptions; } protected override string AdditionalPath => _options.CallbackPath; public override CookieOptions Build(HttpContext context, DateTimeOffset expiresFrom) { var cookieOptions = base.Build(context, expiresFrom); if (!Expiration.HasValue || !cookieOptions.Expires.HasValue) { cookieOptions.Expires = expiresFrom.Add(_options.ProtocolValidator.NonceLifetime); } return cookieOptions; } } } }