For over a year now, I've been working on what is for me the most exciting OpenIddict release: OpenIddict 3.0 beta1, whose server and validation features have been almost completely rewritten. You can find all the 3.0 beta1 packages on NuGet.org.
What's new?
Some of the major changes introduced in this release were described in the OpenIddict 3.0 roadmap (the detailed list can be found on GitHub), but here's a recap of the most important ones:
The aspnet-contrib OAuth 2.0 server/validation/introspection handlers and OpenIddict were merged into a single codebase
This is the most important change of this release: ASOS – the low-level OpenID Connect server framework used in OpenIddict 1.0/2.0 – and the aspnet-contrib validation and introspection middleware were all merged into OpenIddict.
To ensure OpenIddict can be used as a replacement for ASOS (for which people usually write their own persistence layer, at least to validate things like client_id
and redirect_uri
), a new degraded mode was introduced to allow using the OpenIddict server components without all the additional logic that relies on the OpenIddict managers/stores (e.g client authentication, client validation, token storage).
For more information about this feature, read my previous post:
Creating an OpenID Connect server proxy with OpenIddict 3.0's degraded mode.OpenIddict has been decoupled from ASP.NET Core and now natively supports OWIN/Katana and ASP.NET 4.x
In OpenIddict 1.0/2.0, the core and the EF 6/EF Core/MongoDB stores were already decoupled from ASP.NET Core. In 3.0, the server and validation features have been revamped to avoid depending on ASP.NET Core. Instead, ASP.NET Core integration is now provided by separate packages named OpenIddict.Server.AspNetCore
and OpenIddict.Validation.AspNetCore
. For convenience, a metapackage named OpenIddict.AspNetCore
can be referenced to import the OpenIddict core, server and validation packages and their ASP.NET Core integration with a single PackageReference
.
Unlike the previous versions, OpenIddict 3.0 will also support multiple ASP.NET Core versions: 2.1, 3.1 and 5.0.
Decoupling OpenIddict from ASP.NET Core was also a great opportunity to make it natively compatible with OWIN/Katana, which will allow using its server or validation features in any ASP.NET (non-core) 4.6.1 application. OWIN/Katana integration is provided by the OpenIddict.Server.Owin
and OpenIddict.Validation.Owin
packages, which follows the same pattern as the ASP.NET Core hosts.
Here's the framework/runtime combinations that will be officially supported in 3.0:
Web framework version | .NET runtime version |
---|---|
ASP.NET Core 2.1 | .NET Framework 4.6.1 |
ASP.NET Core 2.1 | .NET Framework 4.7.2 |
ASP.NET Core 2.1 | .NET Framework 4.8 |
ASP.NET Core 2.1 | .NET Core 2.1 |
ASP.NET Core 3.1 | .NET Core 3.1 |
ASP.NET Core 5.0 | .NET 5.0 |
OWIN/Katana 4.1 | .NET Framework 4.6.1 |
OWIN/Katana 4.1 | .NET Framework 4.7.2 |
OWIN/Katana 4.1 | .NET Framework 4.8 |
For more information about OWIN/Katana and ASP.NET 4.x support, read this dedicated post:
Adding OpenIddict 3.0 to an OWIN application.OpenIddict now uses JSON Web Token (JWT) as the default token format
In OpenIddict 1.0/2.0, the ASP.NET Core Data Protection stack is always used to encrypt the authorization codes, refresh tokens and access tokens, unless you explicitly opt for JWT access tokens with options.UseJsonWebTokens()
.
In 3.0, OpenIddict will now use encrypted JWT as the default token format for all the token types. Developers who need to disable access token encryption will be able to do so using options.DisableAccessTokenEncryption()
:
1 | services.AddOpenIddict() |
Unlike previous versions, the OpenIddict 3.0 validation handler now supports JWT and introspection. Developers who use JWT access tokens in 2.0 and the JWT bearer middleware developed by Microsoft are strongly encouraged to move to the OpenIddict validation handler, that provides a simpler configuration story and includes dedicated logic to ensure tokens produced by OpenIddict 1.0/2.0 can still be used when migrating to OpenIddict 3.0.
ASP.NET Core Data Protection tokens are still supported in 3.0 and even recommended if you migrate from 1.0/2.0 (to ensure tokens produced by older versions can still be read after migrating to 3.0) or if you enable device flow support, as they provide additional protection against token leakage. To enable Data Protection support, simply call options.UseDataProtection()
in both the server and validation options:
1 | services.AddOpenIddict() |
ASP.NET Core Data Protection support is provided by the OpenIddict.Server.DataProtection
and OpenIddict.Validation.DataProtection
packages. These packages are referenced by the OpenIddict.AspNetCore
metapackage but not by OpenIddict.Owin
for naming and layering reasons.
Unlike the rest of ASP.NET Core, Data Protection is still compatible with the .NET Framework and thus can be used in ASP.NET 4.x applications. To enable ASP.NET Core Data Protection with the OWIN host, reference OpenIddict.Server.DataProtection
and OpenIddict.Validation.DataProtection
.
JSON.NET was replaced by System.Text.Json
All the OpenIddict libraries have been updated to use System.Text.Json
(developed by Microsoft) instead of JSON.NET.
While it's a relatively new library (that still lacks features like a writable DOM), the performance boost it offers is impressive and makes System.Text.Json
a fantastic candidate for replacing JSON.NET in OpenIddict.
Here's a tiny benchmark showing the difference between deserializing a OpenIdConnectMessage
– the type used in OpenIddict 2.0 – with JSON.NET and deserializing its equivalent in 3.0 (where OpenIddictMessage
was optimized and is now internally powered by System.Text.Json
).
1 | [ ] |
As you can see, the performance gain is excellent: not only it is 35% faster, but it also allocates much less memory!
1 | BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.264 (2004/?/20H1) |
Method | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|
OpenIddict20 | 4.510 μs | 0.0725 μs | 0.0642 μs | 1.00 | 0.8469 | 0.0229 | - | 6.92 KB |
OpenIddict30 | 2.948 μs | 0.0160 μs | 0.0149 μs | 0.65 | 0.2136 | - | - | 1.77 KB |
... and serialization is even more impressive!
1 | [ ] |
1 | BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.264 (2004/?/20H1) |
Method | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|---|
OpenIddict20 | 4.209 μs | 0.0667 μs | 0.0714 μs | 1.00 | 0.9918 | 0.0076 | - | 8.11 KB |
OpenIddict30 | 1.361 μs | 0.0257 μs | 0.0275 μs | 0.32 | 0.1621 | - | - | 1.34 KB |
In most cases, this change should be completely transparent, but if you manually use JSON.NET to serialize or deserialize OpenIdConnectMessage
, OpenIdConnectRequest
or OpenIdConnectResponse
instances, consider moving to System.Text.Json
when migrating to OpenIddict 3.0, as 3.0 no longer includes a built-in JSON.NET JsonConverter
for these types.
OpenIddict now supports the device authorization grant
Standardized in 2019, the device authorization grant (aka device flow) is now natively supported by OpenIddict 3.0.
Adding device flow support required making changes to the OpenIddict entities. This should be transparent for MongoDB users but Entity Framework 6 and Entity Framework Core users will have to create and apply a migration for the new schema to be reflected in the database.
Enabling reference tokens is no longer required to use immediate access token revocation
In 1.0/2.0, developers who needed immediate access token revocation support had to use reference tokens. As of 3.0, this is no longer required: even self-contained JWT or Data Protection access tokens can be revoked, as OpenIddict 3.0 now always creates a database entry for all token types.
Like in previous versions, the API projects needing immediate access token revocation need to either have a direct access to OpenIddict's database or use introspection:
1 | services.AddOpenIddict() |
1 | services.AddOpenIddict() |
Reference tokens can still be used in OpenIddict 3.0 and are now stored either as JWT or Data Protection tokens in the database, but enabling them only affects how tokens are returned to the client: when reference tokens are enabled, the original token payload is added to the database entry and a reference identifier (a 256-bit random value) is returned instead of the token, which greatly reduces the size of the tokens returned to the clients in exchange for more space used in the database.
Independently of whether options.UseReferenceTokens()
is called or not, user codes used in the device flow are always user-readable reference tokens. As such, the device flow cannot be enabled when disabling token storage with options.DisableTokenStorage()
and will require custom code when using the degraded mode.
OpenIddict's managers and stores now use IAsyncEnumerable<T>
The application, authorization, scope and token managers (located in OpenIddict.Core
) and all the Entity Framework 6, Entity Framework 6 and MongoDB stores now natively support IAsyncEnumerable<T>
. While this is a massive binary/source breaking change, migrating to IAsyncEnumerable<T>
allows stores to implement streamed enumerations, which was not possible in OpenIddict 2.0 (that uses Task<ImmutableArray<T>>
instead of IAsyncEnumerable<T>
).
IAsyncEnumerable<T>
is fully supported even on .NET Framework >= 4.6.1, thanks to the Microsoft.Bcl.AsyncInterfaces
compatibility package that OpenIddict.Abstractions
references.
The Entity Framework 6, Entity Framework Core and MongoDB entities have been renamed
To make more obvious the fact the OpenIddict entities are designed for a specific ORM/database, the application, authorization, scope and tokens entities have all been renamed to include the name of their corresponding store (e.g OpenIddictApplication
-> OpenIddictMongoDbApplication
).
The OpenIddict endpoints are all non-pass-through by default
In previous versions of OpenIddict, the authorization, logout, token and userinfo endpoints were always pass-through: OpenIddict validated the requests for you, but you needed to add an MVC controller – typically named AuthorizationController
– or a custom middleware to handle them later in the ASP.NET Core pipeline.
Starting in 3.0, none of the OpenIddict endpoints is pass-through by default, which helps with debugging as OpenIddict will now throw an exception if the request is not handled using the events model and if the pass-through mode was not enabled, instead of letting ASP.NET Core return a 404 response.
To enable pass-through for a specific endpoint, use the methods provided by OpenIddictServerAspNetCoreBuilder
or OpenIddictServerOwinBuilder
:
1 | services.AddOpenIddict() |
Status code pages middleware integration is no longer enabled by default
In 3.0, integration with the ASP.NET Core status code pages middleware is now opt-in:
1 | services.AddOpenIddict() |
Returning custom parameters via AuthenticationProperties.Parameters
is now supported
Returning custom parameters was already supported in 1.0/2.0, but they had to be stored as strings in AuthenticationProperties.Items
with a special suffix. This mechanism is no longer supported, but returning custom bool
, long
, string
, string[]
or JsonElement
parameters is now much simpler:
1 | var properties = new AuthenticationProperties( |
AuthenticationProperties.Parameters
was introduced in ASP.NET Core 2.1 and thus is not supported on OWIN. To return custom properties on OWIN, you must store them in AuthenticationProperties.Items
as strings and create a custom event handler to return them as part of the response. See Add additional values into Authorize endpoint response for an example.
What's next?
There'll likely be a few other beta releases before RTM to ensure everything is working as intended and to add features like localization support. All the samples contained in the openiddict-samples have already been updated to target OpenIddict 3.0 beta1 and new samples will be progressively added to cover the newly supported scenarios (e.g device flow).
Considerable effort has been dedicated to making sure that all users of ASOS, the aspnet-contrib extensions or OpenIddict have a migration path to 3.0 – whether they are using the latest ASP.NET Core version and the latest .NET Core runtime, are stuck with 2.1 on .NET Framework or still use ASP.NET 4.6.1 with OWIN/Katana.
As such, I'll no longer offer free support for OpenIddict 1.0/2.0 or the aspnet-contrib packages that were merged into OpenIddict as part of this release once the 3.0 RTM packages ship (in a few months). The 2 aspnet-contrib repositories will also be archived.
The following NuGet packages will be eventually flagged as obsolete to inform users that they are no longer actively developed or supported:
Package name | Package version |
---|---|
AspNet.Security.OpenIdConnect.Extensions | All |
AspNet.Security.OpenIdConnect.Primitives | All |
AspNet.Security.OpenIdConnect.Server | All |
Owin.Security.OpenIdConnect.Extensions | All |
Owin.Security.OpenIdConnect.Server | All |
AspNet.Security.OAuth.Introspection | All |
AspNet.Security.OAuth.Validation | All |
Owin.Security.OAuth.Introspection | All |
Owin.Security.OAuth.Validation | All |
OpenIddict.* | < 3.0 |
Users who substantially contributed to OpenIddict, sponsored the project or benefit from a support contract will still get bug fixes for these packages via a private feed to give them additional time to migrate to OpenIddict 3.0.
With that in mind, I encourage everyone to start migrating to OpenIddict 3.0 beta1 and testing it in a non-production environment.
What can you do to help?
While super exciting, this release has been way more time-consuming than I had initially anticipated, and there are still many important things to do, like adding documentation and a migration guide to help developers update their applications to OpenIddict 3.0.
For that, I need your help: please consider contributing to the effort or sponsoring me, so I can spend more time working on OpenIddict. Without external help, I likely will not have enough spare time to work on things that would benefit the community, like documentation.
Contribution area |
---|
Core/server/validation components |
Documentation |
Samples |
If you're interested in getting dedicated support or want to discuss sponsoring options, don't hesitate to reach me at contact@kevinchalet.com.