As a developer and trainer, it is hard to keep up with all the changes in all the libraries. In this blog post, I will summarize the recent key changes that I have found in the ASP.NET Core JwtBearer library versions 3.1.22, 5.0.13, and 6.0.1.
The JwtBearer library is distributed in the Microsoft.AspNetCore.Authentication.JwtBearer NuGet package. The corresponding source code can be found in the /src/Security/Authentication/ folder in the ASP.NET Core repository on GitHub.
What is the purpose of the ASP.NET Core JwtBearer library?
The library is implemented as an ASP.NET Core request pipeline middleware, and its sole purpose is to authenticate incoming requests. It will look for JW bearer tokens in the request and, if a token is found, validate it and create a ClaimsPrincipal User instance. The authorization handler can then authorize the user to get access to the requested resource.
What runtime does each version target?
The different versions of the JwtBearer NuGet package target different runtimes:
- Version 3.x.x targets .NET Core 3.1
- Version 5.x.x targets .NET 5.x
- Version 6.x.x targets .NET 6.x
That sounds obvious and logical, but the annoying thing with Visual Studio (as of today) is that, even if you have a .NET Core 3.1 application, it still tells you that version 6 is available, even though it should know that version 6.x.x is not compatible with your current project:
As a good developer, you want to keep your packages up to date, but trying to update it will result in the following error:
This sure is a bit annoying
Comparing the editions of JwtBearer
To do the research, I really like to get down to the true source, and the best source is of course the ASP.NET source code. So I downloaded it for the three different versions and then compared the result using Araxis merge. I could have done the same comparisons using NDepend or JustAssembly from Telerik, but I chose to use Araxis merge because it gave me the best overview for this blog post. Using Araxis merge I can compare all three versions at the same time as shown below:
The interesting source code for the authentication handlers in ASP.Net Core is found in the /src/Security/Authentication/ directory on GitHub.
What has changed between the revisions of JwtBearer?
There are of course plenty of small changes in this library, and many of the changes consist of added XML comments and improved support for non-nullable reference types. You can read my blog post about non-nullable reference types to learn more about this very cool language feature that was added in C# 8.
But let’s look at the ones that actually matter for us using this library:
Map inbound claims option
This feature, which was introduced in version 5, allows us to very easily disable one of the most annoying features in the .NET authentication stack, and that is the automatic renaming of some of the claims found in the token.
The flag is by default set to true, but can now easily be disabled using:
services.AddAuthentication() |
Let’s compare the result when this flag is false or true. If we send the following access token in a request to the library:
{ |
Then the resulting ClaimsPrincipal User will contain the following set of claims:
MapInboundClaims = false |
Claims - nbf=1642960856 |
MapInboundClaims = true |
Claims |
I have answered questions about claims mapping on Stack Overflow for quite some time now and I wish this flag was set to false by default. This would have made life so much easier for developers working with claims.
Customizing the JwtBearer back channel HttpClient
The JwtBearer will, via the Configuration Manager, make HTTP(s) requests to our identity provider through the back channel. For example, it will use this channel to download the openid-configuration and the public keys from the authorization provider (for example IdentityServer).
Before version 6, we could provide our own HttpMessageHandler to customize the BackChannel request and response, by for example:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) |
Why would you want to provide your own? Some of the use-cases can be:
- Improve logging of the backchannel requests
- Add custom headers to the outgoing request
- Inspect the response
- Implement automatic retry when a request fails, perhaps using the Polly library.
In version 6 they also introduced the option to provide your own HttpClient instance. This was added as a response to GitHub issue #27426.
This allows us to provide our own client like this:
builder.Services.AddAuthentication() |
Why would you want to provide your own client?
- Work with the HttpClientFactory
- Better control of the HTTP protocol to be used
(i.e., only use HTTP/2 or HTTP/1.1)
Some related resources
- Use IHttpClientFactory in RemoteAuthenticationOption
- Add HttpClient Backchannel property to JwtBearerOptions
Customizing the configuration refresh intervals
In .NET 5 they introduced two new options to control the backchannel refresh intervals and you define them in code using:
services.AddAuthentication().AddJwtBearer(options =>
//.NET 6 defaults |
In .NET 6 they changed the default values as shown above, but the main question is of course, what do they control?
Automatic refresh interval
This parameter will control how often it will download and refresh the cached openid-configuration and the JWKS signing keys.
Refresh interval
If a back channel request to the identity provider fails, then this value will control the wait time between retries. If it fails to refresh the configuration, then it will keep using the existing configuration that was retrieved earlier.
JwtBearer Dependencies
The JwtBearer also depends on a few other libraries, which are:
These three libraries are all located in a different repository named azure-activedirectory-identitymodel-extensions-for-dotnet. It is not located under the ASP.NET Core, instead it is located under the AzureAD organization.
The versioning of these packages does not follow the same versioning pattern as ASP.NET Core does, so in my investigation, I used the following versions:
- ASP.NET Core 3.1 -> JwtBearer (3.1.22) -> …OpenIDConnect (5.5.0)
- ASP.NET 5.0-> JwtBearer (5.0.13) -> …OpenIDConnect (6.7.1)
- ASP.NET 6.0 -> JwtBearer (6.0.1) -> …OpenIDConnect (6.10.0)
Naturally there have been numerous improvements here too, but for those of us who are working with APIs, the TokenValidationParameters is the most interesting one for us.
services.AddAuthentication().AddJwtBearer(options => |
What interesting changes are here? If we compare version 5.5.0 with 6.10.0, we see that some of the most interesting changes are the following:
-
New validation delegates
- AlgorithmValidator
By providing a custom delegate to this parameter, you can do your own validation of the cryptographic algorithm used. If provided, then the valid algorithms provided in the ValidAlgorithms parameter will be ignored. Discussions about this delegate can be found here. - TypeValidator
By providing a custom delegate to this parameter, you can do your own token type validation instead of the built-in default one. Discussions about this delegate can be found in Issue 1378 and Issue #1385
- AlgorithmValidator
-
New properties
- IgnoreTrailingSlashWhenValidatingAudience
Setting this property will control if a ‘/’ is significant at the end of the audience or not. (default true) - TryAllIssuerSigningKeys
Will control whether all the signing keys should be tried during signature validation when the referred key in the token is not found. (default true) - ValidAlgorithms
Contains a list of the valid algorithms for cryptographic operations. - ValidTypes
Contains a list of the valid token types.
- IgnoreTrailingSlashWhenValidatingAudience
Conclusion
Keeping up with all the changes in the .NET stack is hard and using Araxis merge allowed me to quickly compare three versions of the code base at the same time. The most annoying thing when dealing with the authentication stack is that the code involved is located in two different repositories and it is really annoying that the code in the AzureAD repository is not covered by https://source.dot.net/, which otherwise is very handy when searching the Microsoft code base.
When I create my training materials I often do dive deep into the .NET source code, and looking under the hood gives you another perspective on how things actually work and the amazing work the .NET developers put into the stack!
While browsing the source code I found some inaccuracies in the code XML comments, so I filed an issue to the ASP.NET Core team on GitHub here and it was quickly resolved within a few days.
I have built training courses about this topic and it has helped several companies move forward with their journey. Check out the courses here.
About the author
Tore Nestenius has been interested in computers since he got his first Commodore VIC-20. Today he works as a freelance developer and trainer with a focus on ASP.NET Core, IdentityServer, OpenId Connect and web security. Read more about him here and about his services and training
Other posts by me:
Tore’s Newsletter
Be the First to Know! Get notified about my latest blog posts, upcoming presentations, webinars, and more — subscribe today!