Earlier today, the first OpenIddict 5.0 preview was pushed to NuGet.org 🎉
As always, all the changes can be found on GitHub but here's a recap of the most interesting ones:
What's new?
This major release introduces breaking changes that require applying migrations to update the database schema. As such it is not recommended to use it in production or in tandem with an existing deployment using OpenIddict 4.x.
Relaxed redirect_uri
comparisons for native applications
While the OAuth 2.0 and OpenID Connect specifications explicitly mandated the use of the "simple string comparison" logic – put simply, a byte-by-byte comparison – newer specifications like RFC8252 (aka OAuth 2.0 for Native Apps) relaxed this policy for mobile and desktop applications to allow using dynamic ports chosen at runtime.
While OpenIddict already allowed customizing the comparison policy by overriding OpenIddictApplicationManager.ValidateRedirectUriAsync()
, built-in support for native applications was not a thing.
OpenIddict 5.0 changes that by introducing a new ApplicationType
property for the application entity. This property is always set to web
by default, but can be explicitly set to native
to opt in the relaxed comparison policy that ignores ports for local redirect_uris
:
1 | await manager.CreateAsync(new OpenIddictApplicationDescriptor |
To avoid ambiguities, the existing OpenIddictApplicationDescriptor.Type
property (and the properties of the same name in OpenIddictEntityFrameworkApplication
, OpenIddictEntityFrameworkCoreApplication
and OpenIddictMongoDbApplication
) have been replaced by ClientType
.
Users migrating to 5.0 will be invited to update their database schema to include the new ApplicationType
property and the renamed ClientType
member.
Per-client token lifetimes
OpenIddict already supported overriding the default token lifetimes dynamically using the SetAccessTokenLifetime(...)
extension but being able to use a static value per client was a popular request.
To support that, OpenIddict 5.0 introduces a new Settings
property in OpenIddictApplicationDescriptor
(and in the EF Core/EF 6/MongoDB entities) and exposes 6 built-in settings that allow controlling the lifetime of access tokens, authorization codes, device codes, identity tokens, refresh tokens and user codes.
Here's an example that sets a static access token lifetime for a specific client:
1 | await manager.CreateAsync(new OpenIddictApplicationDescriptor |
Even if a static token lifetime was attached to the client registration, the token lifetime can always be dynamically overridden using the SetAccessTokenLifetime(...)
extension.
Client assertions
OpenIddict 4.x already supported client assertions, but only in the client stack (this feature was introduced in 4.0 as it was required for the Sign in with Apple integration).
OpenIddict 5.0 finalizes the implementation of client assertions by updating the server and validation stacks to support private_key_jwt
for all the authenticated API endpoints (device authorization, token, introspection and revocation endpoints).
To use client assertions instead of client secrets, two things must be done:
Attach a JSON Web Key Set containing at least one signing key to the client registration
For that, a public key/private key pair must be generated first: the public key will be attached to the application entry and used by the server to validate the client assertions while the private key will be only known by the client and used to dynamically generate new client assertions.
To generate an ECDSA private/public key pair, use the following snippet:
1 | using var algorithm = ECDsa.Create(ECCurve.NamedCurves.nistP256); |
Then, attach a JsonWebKeySet
instance to OpenIddictApplicationDescriptor.JsonWebKeySet
:
1 | await manager.CreateAsync(new OpenIddictApplicationDescriptor |
Attach the signing key to the client registration or the validation options
To use client assertions with the OpenIddict client, the private signing key must be attached to the client registration:
1 | services.AddOpenIddict() |
To use client assertions in introspection requests sent by the OpenIddict validation handler, the private signing key must be attached to the options:
1 | services.AddOpenIddict() |
Both a client secret and one or more signing keys can be assigned to a client application: in this case, the client will be able to use both authentication methods. To force a client application to use client assertions, you can remove its client secret.
The less secure client_secret_jwt
is deliberately not supported, as OpenIddict always "hashes" the client secret using PBKDF before storing it in the database: once a client is created, OpenIddict no longer has access to the original client secret and thus cannot use it to determine whether a JWT client assertion was signed using the correct client secret or not.
What's next?
OpenIddict 5.0 RTM is expected to ship mid-December. Until then, it's likely additional previews will be released to gather precious feedback from the community 😀