Description
Background and Motivation
Improvements in JsonWebToken
and JsonWebTokenHandler
have been made in Microsoft.IdentityModel
, which include a 30% performance improvement over JwtSecurityToken
which is currently used in ASP.NET today. In later versions of Microsoft.IdentityModel 7.x (before .NET8 RC1), we will enable AOT support by having fully trimmable assemblies in Microsoft.IdentityModel
and remove the dependency of Newtonsoft, enabling a smaller dll for AOT.
Microsoft.IdentityModel
offers two generations of JSON web token (JWT) handling, which are in two assemblies:
-
System.IdentityModel.Tokens.Jwt
is the old generation. Notable types areJwtSecurityToken
andJwtSecurityTokenHandler
. This is the assembly currently used by ASP.NET Core. -
Microsoft.IdentityModel.Tokens.JsonWebToken
is the next generation. It offersJsonWebToken
andJsonWebTokenHandler
with:-
Improved performance (30%)
-
Better resilience: IdentityModel will fetch and maintain the OIDC metadata and uses its last known good state (repair item from March 2020 outage)
-
Defense in depth: IdentityModel provides additional AAD key issuer validation protection
-
Support for async token validation, returning a
TokenValidationResult
rather than throwing
-
Microsoft.IdentityModel
also has two abstractions for Token Handlers:
ISecurityTokenValidator
is the old generation.TokenHandler
(abstract class) is the next generation and offers asynchronous token validation.
The following assemblies have a dependency on JwtSecurityToken
, or JwtSecurityTokenHandler
:
Microsoft.AspNetCore.Authentication.JwtBearer
,Microsoft.AspNetCore.Authentication.OpenIdConnect
,Microsoft.AspNetCore.Authentication.WsFederation
,Microsoft.AspNetCore.Authentication
Proposed API
We introduce a new boolean, UseTokenHandlers
, in the JwtBearer and WsFederation options to enable developers to decide whether the access token validation will be done with the new TokenHandlers
(more performant, resilient and async) or with the legacy SecurityTokenValidators
. We also expose the list of TokenHandlers
used to validate the token. Developers can decide to add their own TokenHandlers
(token type) for each protocol (JwtBearer/WsFed). By default JwtBearerOptions.TokenHandlers
contains an instance of the JsonWebTokenHandler
and WsFederationOptions.TokenHandlers
contains handlers for SAML1, SAML2, and JsonWebTokenHandler
.
Additions in JwtBearer
:
namespace Microsoft.AspNetCore.Authentication.JwtBearer;
public class JwtBearerOptions: AuthenticationSchemeOptions
{
+ public IList<TokenHandler> TokenHandlers { get; }
+ public bool UseTokenHandlers { get; set; } = true;
}
Additions in WsFederation
:
namespace Microsoft.AspNetCore.Authentication.WsFederation;
public class WsFederationOptions: RemoteAuthenticationOptions
{
+ public IList<TokenHandler> TokenHandlers { get; }
+ public bool UseTokenHandlers { get; set; } = true;
}
Additions in OpenIdConnect
:
We introduce a new boolean, UseTokenHandler
, in the OpenIdConnect options to enable developers to decide whether the ID token validation will be done with the new TokenHandler
(more performant, resilient and async) or with the legacy SecurityTokenValidator
.
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect;
public class OpenIdConnectOptions: RemoteAuthenticationOptions
{
+ public TokenHandler TokenHandler { get; }
+ public bool UseTokenHandler { get; set; } = true;
+ public bool MapInboundClaimsTokenHandler {get; set;}
}
Usage Examples
By default, ASP.NET Core in .NET 8 uses the new TokenHandler. If a developer wants to use the legacy validators, they can set UseTokenHandlers = false
.
services.Configure<JwtBearerOptions>(JwtBearerDefault.AuthenticationScheme, options => { options.UseTokenHandlers = false; });
If a developer wants to have their own TokenHandler, they can add it to the list of TokenHandlers:
services.Configure<JwtBearerOptions>(JwtBearerDefault.AuthenticationScheme, options => { options.TokenHandlers.Add( new MyTokenHandler()); });
Alternative Designs
Alternative designs were discussed with @Tratcher, @eerhardt, @halter73 . We went with Option A_1, but the alternatives discussed were:
Option A_1:
Have the same assemblies as today with a dual dependency on System.IdentityModel.Tokens.Jwt and Microsoft.IdentityModel.Tokens.JsonWebToken, and offer both interfaces (Jwt for compatibility, whereas the processing is done with JsonWebToken). In practice, note that the Jwt Wilson assembly already depends on the JsonWebToken Wilson assembly, so there would not be any additional dependencies than today.
Additionally, to leverage the new generation of Wilson assemblies without breaking changes, both ISecurityTokenValidator and TokenHandler members would have to be in ASP.NET core’s surface area.
Validation would need to be done on the options such that when using a new generation of Jwt classes, the new generation of the TokenHandlers would also need be used.
Option A_2:
Duplicate the current ASP.NET Core assemblies, and have a new generation (let’s name them Microsoft.AspNetCore.Authentication.JwtBearer2. Microsoft.AspNetCore.Authentication.OpenIdConnect2, Microsoft.AspNetCore.Authentication.WsFederation2, Microsoft.AspNetCore.Authentication2 for now, until we have a better name), and have these new generation depend only on JsonWebToken, and only expose these concepts. Letting the old generation leverage only Jwt. Additionally, these new assemblies would only rely on TokenHandler and drop support for ISecurityTokenValidator
In practice, we could, for this Option A2, use the same codebase, but add the files of the old projects as links in the new project, with conditional projects)
Option A_3:
Breaking changes in ASP.NET to rely only on the Microsoft.IdentityModel.Tokens.JsonWebToken assembly for Jwts and only TokenHandler for token handler abstractions.
In practice, these breaking changes should only affect users that leverage the extensibility features. We need to understand how large of a population this would affect.
Also gathering customer feedback in the GitHub discussion in Microsoft.IdentityModel repo and the two above mentioned PRs.
Risks
When setting UseTokenHandlers
or UseTokenHandler
to true
, the SecurityToken passed in the context of the TokenValidated event needs to be downcast to JsonWebToken instead of JwtSecurityToken for users who were already doing this, which is not a common scenario, but for more advanced users. Mitigation for the risk is to have an implicit operator.
Initial feedback on 7.0.0-preview of Microsoft.IdentityModel from @kevinchalet: "FYI, I tested the 7.0.0-preview packages with OpenIddict and haven't seen any particular regression. Good job"