Have you ever worked on migrating from a legacy system to a newer one? That’s what I’m doing, and I learn a lot from this every day. Today we’ll learn how to translate tokens with identity server. Specifically, we will use tokens issued with Forms authentication ticket to make authenticated API calls on APIs protected with OpenID connect via Identity server.

The scenario

Imagine we have a legacy monolith API that can be broken into several microservices. This monolith is written in the old ASP.net and uses Forms authentication ticket to authenticate users. We want to migrate the monolith bit by bit to smaller microservices running on ASP.net core, dotnet 7.

The problem

The issue is that Forms authentication ticket isn’t available on dotnet 7 and even if it was, we won’t use it cause we have decided to use OpenId connect on the new system. Since we decided to migrate the monolith bit by bit, modules of the monolith will be replaced by microservices and we’ll need the monolith or clients to communicate with our microservices on behalf of the authenticated users, in a secure manner.

With open id connect, client credentials could easily be used for machine to machine calls between the monolith and the microservices. But what about scenarios where calls should be made on behalf of the authenticated user? Migrating the monolith to open id connect will require a lot of work and will impact every client application so we need to find a way to make our new system understand the former system’s authentication method so that the monolith, or clients will communicate with the microservices seamlessly with the old token though the latter is protected by OpenID connect with Identity server.

NOTE: If you’re interested in knowing how to migrate old ASP.net authentication systems to OpenId connect with IdentityServer on ASP.net core, here is an article I wrote about this topic.

If you find this article useful, please follow me on Twitter,  Github, Linkedin, or like my Facebook page to stay updated.
Follow me on social media and stay updated

The solution

The best solution to this problem is one in which the microservice’s functioning will not require any modification, the open id connect server will work seamlessly without adding a new unsecured endpoint to it. The solution will be to:

  1. Modify our microservice to detect calls made with the former token.
  2. Then create a new grant on our open id server meaning that; We will tell our open id identity provider server how it can securely decode Forms Authentication ticket tokens, verify their validity, verify the validity of the client calling it, and issue a secure token with the appropriate claims. This new token will be a kind of translation of the old token.
  3. Forward the call to our microservice with the new translated token.

Implementation

We will start implementing this solution with parts 1 and 3. These will be implemented on the microservice. Taking into consideration that this microservice has controller methods and is protected with authentication and authorization and the authority is an open id connect server we control.

We have to add a middleware on our microservice that will intercept every request, detect if the request is from the old system, ask the identity server to translate the token, and replace the token in the request with the translated token. This is what the middleware will look like.

To register the middleware in your microservice, follow this documentation.

Decrypting Forms authentication ticket token

The next step will be to translate the forms authentication ticket to an OpenId connect token. This is done by the OpenId connect server.

First, we need a way to decrypt Forms authentication tokens. On dotnet 4.x, these tokens were created using a machine key the classes used to create these tokens were not ported to dotnet core or dotnet 5+. Luckily a developer made a package available that can encrypt and decrypt these tokens. Here is the nugget package. The decryption logic will be placed in a class called “LegacyTokenValidator” with a method called “Decrypt”. This is its implementation:

The decryption of this legacy token will yield the user’s username and some other info. We will tell Identity server to use the username to get a user from our database and create the auth token with appropriate claims in it. To do that, we have to create a new grant. The steps to create and add a custom grant are listed here follow the example they show. Our own grant is named “translate” and its grant validator will be in charge of translating the old authentication token into a new one. This is what our grant validator will look like:

If you find this article useful, please follow me on Twitter,  Github, Linkedin, or like my Facebook page to stay updated.

Follow me on social media and stay updated

You then need to register your grant provider in the DI container as follows:

Then, assign the grant to a client or several clients.

Conclusion

With this, your requests from the legacy system will flow seamlessly to the new microservices and responses will be generated as if the requests were made using a token issued by the OpenID connect server. This approach could be used for several other translation scenarios. If you have a better approach, please, let me know in the comments section.

References

https://stackoverflow.com/questions/71592925/asp-net-core-modify-headers-in-middleware

Follow me on social media and stay updated

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.