private async Task <bool> InvokeConfigurationEndpointAsync() { // Metadata requests must be made via GET. // See http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest if (!HttpMethods.IsGet(Request.Method)) { Logger.LogError("The configuration request was rejected because an invalid " + "HTTP method was specified: {Method}.", Request.Method); return(await SendConfigurationResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "The specified HTTP method is not valid." })); } var request = new OpenIdConnectRequest(Request.Query); // Note: set the message type before invoking the ExtractConfigurationRequest event. request.SetProperty(OpenIdConnectConstants.Properties.MessageType, OpenIdConnectConstants.MessageTypes.ConfigurationRequest); // Store the configuration request in the ASP.NET context. Context.SetOpenIdConnectRequest(request); var @event = new ExtractConfigurationRequestContext(Context, Scheme, Options, request); await Provider.ExtractConfigurationRequest(@event); if (@event.Result != null) { if (@event.Result.Handled) { Logger.LogDebug("The configuration request was handled in user code."); return(true); } else if (@event.Result.Skipped) { Logger.LogDebug("The default configuration request handling was skipped from user code."); return(false); } } else if (@event.IsRejected) { Logger.LogError("The configuration request was rejected with the following error: {Error} ; {Description}", /* Error: */ @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, /* Description: */ @event.ErrorDescription); return(await SendConfigurationResponseAsync(new OpenIdConnectResponse { Error = @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = @event.ErrorDescription, ErrorUri = @event.ErrorUri })); } Logger.LogInformation("The configuration request was successfully extracted " + "from the HTTP request: {Request}.", request); var context = new ValidateConfigurationRequestContext(Context, Scheme, Options, request); await Provider.ValidateConfigurationRequest(context); if (context.Result != null) { if (context.Result.Handled) { Logger.LogDebug("The configuration request was handled in user code."); return(true); } else if (context.Result.Skipped) { Logger.LogDebug("The default configuration request handling was skipped from user code."); return(false); } } else if (context.IsRejected) { Logger.LogError("The configuration request was rejected with the following error: {Error} ; {Description}", /* Error: */ context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, /* Description: */ context.ErrorDescription); return(await SendConfigurationResponseAsync(new OpenIdConnectResponse { Error = context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = context.ErrorDescription, ErrorUri = context.ErrorUri })); } Logger.LogInformation("The configuration request was successfully validated."); var notification = new HandleConfigurationRequestContext(Context, Scheme, Options, request) { Issuer = Context.GetIssuer(Options) }; if (Options.AuthorizationEndpointPath.HasValue) { notification.AuthorizationEndpoint = notification.Issuer.AddPath(Options.AuthorizationEndpointPath); } if (Options.CryptographyEndpointPath.HasValue) { notification.CryptographyEndpoint = notification.Issuer.AddPath(Options.CryptographyEndpointPath); } if (Options.IntrospectionEndpointPath.HasValue) { notification.IntrospectionEndpoint = notification.Issuer.AddPath(Options.IntrospectionEndpointPath); notification.IntrospectionEndpointAuthenticationMethods.Add( OpenIdConnectConstants.ClientAuthenticationMethods.ClientSecretBasic); notification.IntrospectionEndpointAuthenticationMethods.Add( OpenIdConnectConstants.ClientAuthenticationMethods.ClientSecretPost); } if (Options.LogoutEndpointPath.HasValue) { notification.LogoutEndpoint = notification.Issuer.AddPath(Options.LogoutEndpointPath); } if (Options.RevocationEndpointPath.HasValue) { notification.RevocationEndpoint = notification.Issuer.AddPath(Options.RevocationEndpointPath); notification.RevocationEndpointAuthenticationMethods.Add( OpenIdConnectConstants.ClientAuthenticationMethods.ClientSecretBasic); notification.RevocationEndpointAuthenticationMethods.Add( OpenIdConnectConstants.ClientAuthenticationMethods.ClientSecretPost); } if (Options.TokenEndpointPath.HasValue) { notification.TokenEndpoint = notification.Issuer.AddPath(Options.TokenEndpointPath); notification.TokenEndpointAuthenticationMethods.Add( OpenIdConnectConstants.ClientAuthenticationMethods.ClientSecretBasic); notification.TokenEndpointAuthenticationMethods.Add( OpenIdConnectConstants.ClientAuthenticationMethods.ClientSecretPost); } if (Options.UserinfoEndpointPath.HasValue) { notification.UserinfoEndpoint = notification.Issuer.AddPath(Options.UserinfoEndpointPath); } if (Options.AuthorizationEndpointPath.HasValue) { notification.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.Implicit); if (Options.TokenEndpointPath.HasValue) { // Only expose the code grant type and the code challenge methods // if both the authorization and the token endpoints are enabled. notification.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.AuthorizationCode); // Note: supporting S256 is mandatory for authorization servers that implement PKCE. // See https://tools.ietf.org/html/rfc7636#section-4.2 for more information. notification.CodeChallengeMethods.Add(OpenIdConnectConstants.CodeChallengeMethods.Plain); notification.CodeChallengeMethods.Add(OpenIdConnectConstants.CodeChallengeMethods.Sha256); } } if (Options.TokenEndpointPath.HasValue) { notification.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.RefreshToken); notification.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.ClientCredentials); notification.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.Password); } // Only populate response_modes_supported and response_types_supported // if the authorization endpoint is available. if (Options.AuthorizationEndpointPath.HasValue) { notification.ResponseModes.Add(OpenIdConnectConstants.ResponseModes.FormPost); notification.ResponseModes.Add(OpenIdConnectConstants.ResponseModes.Fragment); notification.ResponseModes.Add(OpenIdConnectConstants.ResponseModes.Query); notification.ResponseTypes.Add(OpenIdConnectConstants.ResponseTypes.Token); // Only expose response types containing code when // the token endpoint has not been explicitly disabled. if (Options.TokenEndpointPath.HasValue) { notification.ResponseTypes.Add(OpenIdConnectConstants.ResponseTypes.Code); notification.ResponseTypes.Add( OpenIdConnectConstants.ResponseTypes.Code + ' ' + OpenIdConnectConstants.ResponseTypes.Token); } // Only expose the response types containing id_token if an asymmetric signing key is available. if (Options.SigningCredentials.Any(credentials => credentials.Key is AsymmetricSecurityKey)) { notification.ResponseTypes.Add(OpenIdConnectConstants.ResponseTypes.IdToken); notification.ResponseTypes.Add( OpenIdConnectConstants.ResponseTypes.IdToken + ' ' + OpenIdConnectConstants.ResponseTypes.Token); // Only expose response types containing code when // the token endpoint has not been explicitly disabled. if (Options.TokenEndpointPath.HasValue) { notification.ResponseTypes.Add( OpenIdConnectConstants.ResponseTypes.Code + ' ' + OpenIdConnectConstants.ResponseTypes.IdToken); notification.ResponseTypes.Add( OpenIdConnectConstants.ResponseTypes.Code + ' ' + OpenIdConnectConstants.ResponseTypes.IdToken + ' ' + OpenIdConnectConstants.ResponseTypes.Token); } } } notification.Scopes.Add(OpenIdConnectConstants.Scopes.OpenId); notification.Claims.Add(OpenIdConnectConstants.Claims.Audience); notification.Claims.Add(OpenIdConnectConstants.Claims.ExpiresAt); notification.Claims.Add(OpenIdConnectConstants.Claims.IssuedAt); notification.Claims.Add(OpenIdConnectConstants.Claims.Issuer); notification.Claims.Add(OpenIdConnectConstants.Claims.JwtId); notification.Claims.Add(OpenIdConnectConstants.Claims.Subject); notification.SubjectTypes.Add(OpenIdConnectConstants.SubjectTypes.Public); foreach (var credentials in Options.SigningCredentials) { // If the signing key is not an asymmetric key, ignore it. if (!(credentials.Key is AsymmetricSecurityKey)) { continue; } // Try to resolve the JWA algorithm short name. If a null value is returned, ignore it. var algorithm = OpenIdConnectServerHelpers.GetJwtAlgorithm(credentials.Algorithm); if (string.IsNullOrEmpty(algorithm)) { continue; } notification.IdTokenSigningAlgorithms.Add(algorithm); } await Provider.HandleConfigurationRequest(notification); if (notification.Result != null) { if (notification.Result.Handled) { Logger.LogDebug("The configuration request was handled in user code."); return(true); } else if (notification.Result.Skipped) { Logger.LogDebug("The default configuration request handling was skipped from user code."); return(false); } } else if (notification.IsRejected) { Logger.LogError("The configuration request was rejected with the following error: {Error} ; {Description}", /* Error: */ notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, /* Description: */ notification.ErrorDescription); return(await SendConfigurationResponseAsync(new OpenIdConnectResponse { Error = notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = notification.ErrorDescription, ErrorUri = notification.ErrorUri })); } var response = new OpenIdConnectResponse { [OpenIdConnectConstants.Metadata.Issuer] = notification.Issuer, [OpenIdConnectConstants.Metadata.AuthorizationEndpoint] = notification.AuthorizationEndpoint, [OpenIdConnectConstants.Metadata.TokenEndpoint] = notification.TokenEndpoint, [OpenIdConnectConstants.Metadata.IntrospectionEndpoint] = notification.IntrospectionEndpoint, [OpenIdConnectConstants.Metadata.EndSessionEndpoint] = notification.LogoutEndpoint, [OpenIdConnectConstants.Metadata.RevocationEndpoint] = notification.RevocationEndpoint, [OpenIdConnectConstants.Metadata.UserinfoEndpoint] = notification.UserinfoEndpoint, [OpenIdConnectConstants.Metadata.JwksUri] = notification.CryptographyEndpoint, [OpenIdConnectConstants.Metadata.GrantTypesSupported] = new JArray(notification.GrantTypes), [OpenIdConnectConstants.Metadata.ResponseTypesSupported] = new JArray(notification.ResponseTypes), [OpenIdConnectConstants.Metadata.ResponseModesSupported] = new JArray(notification.ResponseModes), [OpenIdConnectConstants.Metadata.ScopesSupported] = new JArray(notification.Scopes), [OpenIdConnectConstants.Metadata.ClaimsSupported] = new JArray(notification.Claims), [OpenIdConnectConstants.Metadata.IdTokenSigningAlgValuesSupported] = new JArray(notification.IdTokenSigningAlgorithms), [OpenIdConnectConstants.Metadata.CodeChallengeMethodsSupported] = new JArray(notification.CodeChallengeMethods), [OpenIdConnectConstants.Metadata.SubjectTypesSupported] = new JArray(notification.SubjectTypes), [OpenIdConnectConstants.Metadata.TokenEndpointAuthMethodsSupported] = new JArray(notification.TokenEndpointAuthenticationMethods), [OpenIdConnectConstants.Metadata.IntrospectionEndpointAuthMethodsSupported] = new JArray(notification.IntrospectionEndpointAuthenticationMethods), [OpenIdConnectConstants.Metadata.RevocationEndpointAuthMethodsSupported] = new JArray(notification.RevocationEndpointAuthenticationMethods) }; foreach (var metadata in notification.Metadata) { response.SetParameter(metadata.Key, metadata.Value); } return(await SendConfigurationResponseAsync(response)); }
/// <summary> /// Represents an event called for each validated configuration request /// to allow the user code to decide how the request should be handled. /// </summary> /// <param name="context">The context instance associated with this event.</param> /// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns> public virtual Task HandleConfigurationRequest(HandleConfigurationRequestContext context) => OnHandleConfigurationRequest(context);