/// <summary> /// Processes the event. /// </summary> /// <param name="context">The context associated with the event to process.</param> /// <returns> /// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation. /// </returns> public async ValueTask HandleAsync([NotNull] ProcessRequestContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (context.EndpointType != OpenIddictServerEndpointType.Revocation) { return; } var notification = new ValidateRevocationRequestContext(context.Transaction); await _provider.DispatchAsync(notification); if (notification.IsRequestHandled) { context.HandleRequest(); return; } else if (notification.IsRequestSkipped) { context.SkipRequest(); return; } else if (notification.IsRejected) { context.Reject( error: notification.Error ?? Errors.InvalidRequest, description: notification.ErrorDescription, uri: notification.ErrorUri); return; } // Store the security principal extracted from the revoked token as an environment property. context.Transaction.Properties[Properties.AmbientPrincipal] = notification.Principal; context.Logger.LogInformation("The revocation request was successfully validated."); }
/// <inheritdoc/> public async ValueTask HandleAsync(ProcessRequestContext context) { if (context is null) { throw new ArgumentNullException(nameof(context)); } var notification = new ValidateRevocationRequestContext(context.Transaction); await _dispatcher.DispatchAsync(notification); // Store the context object in the transaction so it can be later retrieved by handlers // that want to access the principal without triggering a new validation process. context.Transaction.SetProperty(typeof(ValidateRevocationRequestContext).FullName !, notification); if (notification.IsRequestHandled) { context.HandleRequest(); return; } else if (notification.IsRequestSkipped) { context.SkipRequest(); return; } else if (notification.IsRejected) { context.Reject( error: notification.Error ?? Errors.InvalidRequest, description: notification.ErrorDescription, uri: notification.ErrorUri); return; } context.Logger.LogInformation(SR.GetResourceString(SR.ID6110)); }
public override async Task ValidateRevocationRequest([NotNull] ValidateRevocationRequestContext context) { var options = (OpenIddictServerOptions)context.Options; Debug.Assert(!options.DisableTokenStorage, "Token storage support shouldn't be disabled at this stage."); // When token_type_hint is specified, reject the request if it doesn't correspond to a revocable token. if (!string.IsNullOrEmpty(context.Request.TokenTypeHint)) { if (string.Equals(context.Request.TokenTypeHint, OpenIdConnectConstants.TokenTypeHints.IdToken)) { context.Reject( error: OpenIdConnectConstants.Errors.UnsupportedTokenType, description: "The specified 'token_type_hint' parameter is not supported."); return; } if (!options.UseReferenceTokens && string.Equals(context.Request.TokenTypeHint, OpenIdConnectConstants.TokenTypeHints.AccessToken)) { context.Reject( error: OpenIdConnectConstants.Errors.UnsupportedTokenType, description: "The specified 'token_type_hint' parameter is not supported."); return; } } // Skip client authentication if the client identifier is missing or reject // the revocation request if client identification is set as required. // Note: the OpenID Connect server middleware will automatically ensure that // the calling application cannot revoke a refresh token if it's not // the intended audience, even if client authentication was skipped. if (string.IsNullOrEmpty(context.ClientId)) { // Reject the request if client identification is mandatory. if (!options.AcceptAnonymousClients) { _logger.LogError("The revocation request was rejected becaused the " + "mandatory client_id parameter was missing or empty."); context.Reject( error: OpenIdConnectConstants.Errors.InvalidRequest, description: "The mandatory 'client_id' parameter is missing."); return; } _logger.LogDebug("The revocation request validation process was skipped " + "because the client_id parameter was missing or empty."); context.Skip(); return; } // Retrieve the application details corresponding to the requested client_id. var application = await _applicationManager.FindByClientIdAsync(context.ClientId); if (application == null) { _logger.LogError("The revocation request was rejected because the client " + "application was not found: '{ClientId}'.", context.ClientId); context.Reject( error: OpenIdConnectConstants.Errors.InvalidClient, description: "The specified 'client_id' parameter is invalid."); return; } // Store the application entity as a request property to make it accessible // from the other provider methods without having to call the store twice. context.Request.SetProperty($"{OpenIddictConstants.Properties.Application}:{context.ClientId}", application); // Reject the request if the application is not allowed to use the revocation endpoint. if (!options.IgnoreEndpointPermissions && !await _applicationManager.HasPermissionAsync(application, OpenIddictConstants.Permissions.Endpoints.Revocation)) { _logger.LogError("The revocation request was rejected because the application '{ClientId}' " + "was not allowed to use the revocation endpoint.", context.ClientId); context.Reject( error: OpenIdConnectConstants.Errors.UnauthorizedClient, description: "This client application is not allowed to use the revocation endpoint."); return; } // Reject revocation requests containing a client_secret if the application is a public client. if (await _applicationManager.IsPublicAsync(application)) { if (!string.IsNullOrEmpty(context.ClientSecret)) { _logger.LogError("The revocation request was rejected because the public application " + "'{ClientId}' was not allowed to use this endpoint.", context.ClientId); context.Reject( error: OpenIdConnectConstants.Errors.InvalidRequest, description: "The 'client_secret' parameter is not valid for this client application."); return; } _logger.LogDebug("The revocation request validation process was not fully validated because " + "the client '{ClientId}' was a public application.", context.ClientId); // If client authentication cannot be enforced, call context.Skip() to inform // the OpenID Connect server middleware that the caller cannot be fully trusted. context.Skip(); return; } // Confidential and hybrid applications MUST authenticate // to protect them from impersonation attacks. if (string.IsNullOrEmpty(context.ClientSecret)) { _logger.LogError("The revocation request was rejected because the confidential or hybrid application " + "'{ClientId}' didn't specify a client secret.", context.ClientId); context.Reject( error: OpenIdConnectConstants.Errors.InvalidClient, description: "The 'client_secret' parameter required for this client application is missing."); return; } if (!await _applicationManager.ValidateClientSecretAsync(application, context.ClientSecret)) { _logger.LogError("The revocation request was rejected because the confidential or hybrid application " + "'{ClientId}' didn't specify valid client credentials.", context.ClientId); context.Reject( error: OpenIdConnectConstants.Errors.InvalidClient, description: "The specified client credentials are invalid."); return; } context.Validate(); await _eventService.PublishAsync(new OpenIddictServerEvents.ValidateRevocationRequest(context)); }