I spent a lot of time on the client side recently – as part of our PolicyServer client libraries work, customer work, our updated guidance for our workshops as well as the various talks Brock and I gave on building clients for token-based systems (see here for a recording).
In particular for ASP.NET Core-based clients we’ve been going back and forth between various approaches, from completely manual to completely automated (see here) trying to find the best balance between control and convenience.
I think I’ve settled on an approach (at least for now) that integrates nicely with ASP.NET Core and gives you a couple of extensibility points to adjust to your environment (especially around token storage).
Which brings me to the 2nd part – now being sufficiently confident that this is the approach I want to (re-) use, how can I ship it as a library? IdentityModel deliberately has very few dependencies – so adding it there was not an option. Instead we decided to create a new project called IdentityModel.AspNetCore – and the idea is to extend IdentityModel for ASP.NET Core specific functionality and features. Feedback is as always very welcome.
OK – without further ado – how does the token management functionality work?
Basically all functionality is encapsulated in this line of code:
var token = await HttpContext.GetAccessTokenAsync();
This uses the same style as the built-in token storage APIs from Microsoft, but does a couple of things under the cover:
- It uses an extensible storage mechanism to retrieve the current access and refresh token. The default implementation will load the tokens from the authentication session in ASP.NET Core
- It will check the expiration of the access token, and if a configurable threshold is reached, refresh the access token (and also pass the refreshed tokens back to the storage abstraction)
- Return the access token back to the caller
You can now call this API from wherever you see fit, e.g. directly from the code where you make the outbound HTTP calls, or preferred (by me), from within a delegating handler that gets injected in your HTTP client via the factory.
Remark: IdentityServer (and maybe other token services) has a feature to allow a refresh token to be used only once (the RefreshTokenUsage property on the Client). This feature is designed for public clients like native apps and not so much for confidential clients like ASP.NET Core web applications. It also gets in the way with token management like described above. If your token management code gets executed (almost) at the same time but e.g. on different nodes, some nodes will get an error during refresh because the refresh token has been used more than once. You should not use this option in this scenario.
Disclaimer: This is not a generic approach for refreshing tokens from arbitrary providers. The library makes the assumption that your primary OAuth 2 provider (the one that guards your APIs), is also OpenID Connect compliant (e.g. IdentityServer) and was used for authenticating the user. At least that’s how we built our applications – so it is bit opinionated.
The repo contains a sample that shows this approach. This is the very first version of this library – so please have a look and give me feedback if this would also work for you. Thanks!