In our previous article, we learned about Securing ASP.NET Core API with JWT Authentication. Now, let’s go through Refresh Tokens in ASP.NET Core APIs that use JWT Authentication. We’ll be using the codebase that we built in the previous article and add functionalities that support Refreshing JWT Tokens.
Disclaimer: This is a quite detailed guide with more than 2500 words. Do not forget to grab a beer/coffee and bookmark this page before continuing! ?
The Problem
Now, JWT or JSON Web tokens are by far one of the secure ways to protect APIs. But there are slight flaws too, like any other system. Let me present to you a few scenarios that can help you understand the so-called limitations with JWT.
What if an attacker gets hold of your JWT Token? With this token, he could potentially access a secured API mimicking your usage, and compromise the entire service if he wants to. This is bad.
With JWT Token, it is advised that they must expire is less than a day (due to the above security concern). Usually, the standard is a few hours tops. So what happens when the token expires? The user get’s logged out of the system and is prompted to log in again with his/her credentials. Now that is bad user experience in today’s world. When was the last time you actually typed in your credentials on Facebook? Neither do I remember. It always stays logged in, helping me save those moments where I try to remember passwords! So how does this work?
What are Refresh Tokens? - The Solution
In simpler terms, it means that you pass in your credentials to the Authentication API endpoint, the API validates the credentials and returns you a JWT which is likely to expire in a few hours or less, and a Refresh token that can stay active for months.
Using Refresh Tokens, one can request for valid JWT Tokens till the Refresh Token expires. Hence the above-mentioned problems are addressed easily with the concept of Refreshing JWT Tokens. They carry the information needed to acquire new access tokens (JWT). A refresh token allows an application to obtain a new JWT without prompting the user.
Implementing Refresh Tokens in ASP.NET Core APIs
For this demonstration, we will use the solution that we have already built in our previous guide. I will implement refresh tokens over the previous solution.
IMPORTANT
If you have not read my article on Build Secure ASP.NET Core API with JWT Authentication – Detailed Guide , GO READ IT NOW.
Let’s go step by step with the implementation part. Now by theory, this is how the system should work. We will have an endpoint, which we request with valid credentials. In turn, the endpoint returns a response with JWT and Refresh Token. This JWT Token will expire is let’s say 2 minutes. So, we use the Refresh Token (which is stored as cookies) to obtain a new JWT by requesting another endpoint.
We will also implement a way to see all the refresh tokens of a user, and an endpoint to revoke (cancel) a refresh token so that it cannot be used further to generate new JWTs.
First, create a Refresh Token Model to Entities/RefreshToken.cs. This will be the entity with the details of refresh tokens. This model will have the token itself, the expiry date of the token, the created date, and a flag to check if it is active.
Now, for testing purposes let’s reduce the expiry duration of our JWT token to 1 minute. You can find these settings at appsettings.json/JWT. Change DurationInMinutes to 1. This means that our JWT will expire in a minute after creation. This is set to 1 minute so that we get to wait less while testing.
Now, let’s modify Models/AuthenticationModel.cs.
JSONIgnore is an attribute that restricts the property from being shown in JSON results. This is the model that will be returned to the client on request with valid credentials. Now, add the RefreshToken to our ApplicationUser class, so that we can relate / link refresh tokens with specific users on our database.
Adding Migrations & Updating the Database
Since we have made changes to our model classes, let’s make sure that our database is updated with these schemas.
Generating a Refresh Token
Refresh tokens are nothing but random numbers. You can add your own logic to generate the random string. To enhance security, many devs generate these token using the IP address of the client browser. It’s up to you to choose a mechanism. I will use a built-in Crypto Function to generate a 32-byte string.
Let’s build a function that can return a RefreshToken object with properties like Token, Expiration Data and Created Data. You can see the function below.
Saving the Refresh Token
Now that we have a method to generate Refresh Tokens, let’s think about storing it somewhere. An optimal way is to store these tokens as cookies. Let’s build another helper function that can take in the refresh token as the parameter and store it as a browser cookie.
Here, we set the expiration to 10 days and the name of the cookies as refreshToken.
Authentication
In the previous tutorial, we built an endpoint to generate an AuthenticationModel object that includes the JWT itself. Let’s try to modidy the endpoint so as to return a Refresh Token as well.
Go to Services/UserServices.cs, and modify the GetTokenAsync method.
Line #22 checks if there are any active refresh tokens available for the authenticated user.
Line #24-26 sets the available active refresh token to our response.*
Line #30-35 If there are not active Refresh Token available, we call our CreateRefreshToken method to generate a refresh token. Once generated, we set the details of the Refresh Token to the Response Object. Finally, we need to add these tokens into our RefreshTokens Table, so that we can reuse them.
Now, modify our controller at Controllers/UserController.cs
Line #5 - Let’s save our refreshTokens as cookies.
Open up Postman and request for JWT token by providing the valid credentials like I have. Click on Send. You will find a new Property, RefreshTokenExpiration, which states the validity period of our newly created Refresh Token.
Now where is our actual Refresh Token? Remember we configured our controller to set it to the cookie? On Postman open the Cookies Tab. You will be able to see our refresh token as a cookie with expiration date. Cool right?
Remeber that we set the expiration time of our JWT to 1 min ? By now, your JWT would have expired. So let’s request for a new JWT again using the password and username. Don’t worry, we will get to the refresh token in a while. Once you receive the JWT, make a call to a secured endpoint which in our case is …/api/secured
You can see that we are authenticated. Now wait for a minute and try to request again.
Gone! Your JWT token has expired. Now, let’s see this is a practical point of view. Requesting for JWT with username/password everytime is not a good User experience. This is why we generated Refresh Tokens. We’ll start using the refresh tokens, but before that we’ll have to build endpoint to help refresh our jwt tokens.
Refreshing the Expired JWT Token
Add a definition to Services/IUserService.cs
Let’s implement the method in Services/UserService.cs
Line #3 creates a new Response object.
Line #4 checks if there any matching user for the token in our database.
Line #5 -10 If no matching user found, pass a message “Token did not match any users.”
Line #12 - Get the Refresh token object of the matching record.
Line #14-19 Checks is the selected token is active, if not active, send a message “Token Not Active.”
Line #22 - For security reasons, we can use the Refresh Token only once. So, every time we request a new JWT, we have to make sure that we replace the refresh token with a new one. Let’s set the Revoked property to the current time. This makes the refresh token inactive.
Line #25 - 28 Generates a new Refresh token and updates it into our database.
Line #31 - 40 Let’s generate another JWT for the corresponding user and return the response object, along with the new Refresh Token.
Now, let’s wire up this service method to our controller.
Line #4 gets the Refresh Token from our cookies. Remember setting it there?
Line #5 Returns the response object from the Service Method.
Line #6 - 7 Sets the new Refresh Token to our Cookie.
Switch to Postman and POST to the ../api/user/refresh-token endpoint. You can see that we got a new JWT and a new Refresh token in the cookies.
Using this JWT, try to request to a secure endpoint. You can see that you are able to access.
Getting the User’s Refresh Tokens
Now, we need an endpoint where an authenticated user can request along with his / her user id and we get to see all the refresh tokens that were generated for the particular user. Add a definition to Services/IUserService.cs
PS - Feel free to change this implementation as it is totally optional.
Simple Implementation at Services/UserService.cs
Add a new Action method in our Controller at Controllers/UserController.cs that returns all the refresh tokens.
Now , go to your database and copy the required User Id from the AspNetUsers Table.
In Postman, POST to ../api/user/tokens/<user id here>
. You would see a list of all the refresh tokens ever generated for the user along with several other information.
Revoking a Refresh Token
With Refresh Tokens, it is a never ending cycle of expiration and generation of JWTs. What if in certain cases, we need to manually revoke (cancel) a Refresh token, so that it cannot be used to generate a valid JWT.
Let’s see how to build such an endpoint. Now the user needs to pass the token to this endpoint so that we can revoke it. Create a model in Models/RevokeTokenRequest.cs that has a token property.
Add another definition to Services/IUserService.cs
Implement it at Services/UserService.cs
Line #14 - If the passed refresh token is valid, we revoke it here and save to the database.
Now, add a Action Method in Controllers/UserController.cs
Line #5 - Gets the Refresh Token from the Cookies or the Passed Object.
Line #10 - Revokes the Token.
Let’s test it out with Postman. ( This is the last endpoint. I Promise :P )
POST to ../api/user/revoke-token
with the Refresh Token (the latest one. You can get the latest active token using the previous endpoint.). you will see a message saying “Token Revoked”!
Now check all the tokens generated for the user. You can see that all of them are not active. It means that we have successfully revoked the token.
Now request to the refresh token endpoint. You can find that your Refresh Token is no longer Valid and hence you can no more Generate a JWT unless you request with username/password!
Pretty much Secure right? This is all I have got for you in this Guide. Hope things are clear. I will be linking to GitHub repository where you can find the complete source code of this implementation.
Summary
In this detailed guide on Refresh Tokens in ASP.NET Core API, we have learned the basics of Refresh Tokens, it’s importance, how to implement them in ASP.NET Core, generating the refresh token, refreshing jwt tokens, revoking them and more. Have I missed out anything? Feel free to leave below your comments and suggestion. Do not forget to share this article within your developer community. Happy Coding :D