Prior to OpenIddict RC3, the events model used by the OpenID Connect server middleware (i.e the OIDC server framework behind OpenIddict) was deliberately not accessible due to the nature of OpenIddict: being initially designed for non-experts, exposing such a powerful API – that allows altering the way OpenID Connect requests are processed – didn't seem like a good idea at first sight.
With time, the core audience of OpenIddict has evolved a bit to not only include beginners but also developers who were already familiar with OAuth/OpenID Connect or had used OAuthAuthorizationServerMiddleware
or ASOS in the past. For them, the fact OpenIddict didn't allow them to take control of the request processing pipeline was often a blocker. With the introduction of OpenIddict RC3, we're changing that.
Using these advanced APIs is not recommended if you're not familiar with the OAuth/OpenID Connect specifications or with the events model used by the OpenID Connect server middleware. If you're not sure whether you should use these APIs, don't hesitate to reach us on Gitter or on GitHub.
Introducing event handlers
The events model is structured around IOpenIddictServerEventHandler<TEvent>
and IOpenIddictValidationEventHandler<TEvent>
. These 2 interfaces represent handlers that are invoked every time an event of type TEvent
is triggered by the OpenIddict server or validation handlers.
At the time of writing, OpenIddict exposes 44 server events – grouped into the OpenIddictServerEvents
static class to make them easier to find – and 5 validation events, exposed under OpenIddictValidationEvents
.
Each event represents a specific moment in the request processing pipeline (e.g the moment the OpenIddict server determines whether the request is an OpenID Connect request it should handle, the moment it extracts it, handles it or returns a response).
Multiple handlers of the same type can be registered: they will be sequentially invoked in the same order as the one used to register them. As soon as a handler calls a method that indicates the request should no longer be processed (e.g HandleResponse()
or SkipHandler()
), OpenIddict will stop invoking the handlers and the next ones will be automatically ignored.
For security reasons, the custom handlers will be invoked by OpenIddict after its own validation routines. If a request is rejected by OpenIddict, your own handlers won't be invoked.
Creating and registering a custom event handler
Creating an event handler is straightforward: pick the event you need and add a class that implements either IOpenIddictServerEventHandler<TEvent>
(for a handler that receives events triggered by the OpenIddict server services) or IOpenIddictValidationEventHandler<TEvent>
(for a handler that receives events triggered by the OpenIddict token validation services):
For instance, to return custom metadata in the discovery document, you'll need to implement IOpenIddictServerEventHandler<OpenIddictServerEvents.HandleConfigurationRequest>
:
1 | public class MyEventHandler : IOpenIddictServerEventHandler<OpenIddictServerEvents.HandleConfigurationRequest> |
To register it, use options.AddEventHandler<TEvent, THandler>()
(by default, the handler is registered as a scoped service):
1 | public void ConfigureServices(IServiceCollection services) |
Alternatively, if your handler can be trivialy implemented and doesn't use constructor injection, you can register it inline:
1 | public void ConfigureServices(IServiceCollection services) |
Concrete examples
Tweaking the endpoint detection logic
By default, OpenIddict uses a path-based endpoint resolution logic to determine whether the incoming request is an OpenID Connect request it should handle. This is done by comparing the request path to the endpoint paths registered in the OpenIddict server options. In some cases, you'll probably want to listen on multiple paths at the same time instead of a single one. For that, you can use the OpenIddictServerEvents.MatchEndpoint
event:
1 | options.AddEventHandler<OpenIddictServerEvents.MatchEndpoint>(notification => |
Returning the list of supported social providers as part of the discovery document
If you need to expose the external providers that are supported by your server application, you can use the HandleConfigurationRequest
event:
1 | public class MyEventHandler : IOpenIddictServerEventHandler<OpenIddictServerEvents.HandleConfigurationRequest> |
1 | { |
Implementing the token endpoint at the handler level without having an authorization controller
With OpenIddict, the "standard" way to process authorization or token requests is to have an authorization controller dedicated to handling these requests. Starting with RC3, this can also be done directly at the OpenIddict server handler level (e.g for those who don't need or don't want to use ASP.NET Core MVC).
Here's how it could be done by writing an OpenIddictServerBuilder
extension:
1 | public static class CustomOpenIddictServerExtensions |
1 | public void ConfigureServices(IServiceCollection services) |
Extracting access tokens from the query string
In some cases, flowing the access token in the HTTP request headers is not possible (e.g when using WebSockets with JS clients). To work around these limitations, you can transfer it as a query string parameter and configure the OpenIddict validation handler to use your extraction logic by adding an event handler for OpenIddictValidationEvents.RetrieveToken
:
1 | public void ConfigureServices(IServiceCollection services) |