In this article, we are going to learn about refresh tokens and their use in modern web application development. This is part of the ASP.NET Core Authentication with JWT and Angular series.

In the first part of this series, we learned how to implement authentication with ASP.Net Core on the server-side using the JSON web tokens (JWT). Then, in the second part, we looked at how to implement authentication and authorization in a front end app using Angular. 

Now, we are going to look at how we can implement refresh tokens in the authentication workflow.

To download the source code for this article, visit our Refresh Tokens GitHub repository.

We have divided this article into the following sections:

Refresh Tokens

Let’s look at the JWT based authentication workflow that we implemented in the previous parts of this series:

jwt access token flow

  1. First, the client authenticates with the authentication component by providing the credentials.
  2. Then, the authentication component issues the access token.
  3. After that, the client requests the resource endpoint for a protected resource by providing the access token.
  4. The resource endpoint validates the access token and provides a protected resource.
  5. Steps 3 & 4 keeps on repeating until the access token expires.
  6. Once the access token expires, the client needs to authenticate with the authentication component once again and the flow repeats from step 1.

Let’s see how we can extend this flow.

Refresh token-based authentication workflow

Refresh tokens are credentials that can be used to acquire new access tokens. When access tokens expire, we can use refresh tokens to get a new access token from the authentication component. The lifetime of a refresh token is usually set much longer compared to the lifetime of an access token.

Now, we are going to introduce the refresh token to our authentication workflow:

refresh token flow

  1. First, the client authenticates with the authentication component by providing the credentials.
  2. Then, the authentication component issues the access token and the refresh token.
  3. After that, the client requests the resource endpoints for a protected resource by providing the access token.
  4. The resource endpoint validates the access token and provides a protected resource.
  5. Steps 3 & 4 keeps on repeating until the access token expires.
  6. Once the access token expires, the client requests a new access token by providing the refresh token.
  7. The authentication component issues a new access token and refresh token.
  8. Steps 3 through 7 keeps on repeating until the refresh token expires.
  9. Once the refresh token expires, the client needs to authenticate with the authentication server once again and the flow repeats from step 1.

Now, let’s discuss why we actually need refresh tokens.

The Need for Refresh Tokens

So, why do we need both access tokens and refresh tokens? Why don’t we just set a long expiration date, like a month or a year for the access tokens? Because, if we do that and someone manages to get hold of our access token they can use it for a long period, even if we change our password!

The idea of refresh tokens is that we can make the access token short-lived so that, even if it is compromised, the attacker gets access only for a shorter period. With refresh token-based flow, the authentication server issues a one time use refresh token along with the access token. The app stores the refresh token safely.

Every time the app sends a request to the server it sends the access token in the Authorization header and the server can identify the app using it. Once the access token expires, the server will send a token expired response. Once the app receives the token expired response, it sends the expired access token and the refresh token to obtain a new access token and refresh token. 

If something goes wrong, the refresh token can be revoked which means that when the app tries to use it to get a new access token, that request will be rejected and the user will have to enter credentials once again and authenticate.

Thus, refresh tokens help in a smooth authentication workflow without the need for users to submit their credentials frequently, and at the same time, without compromising the security of the app. 

Implementation

So far we have learned the concept of refresh tokens. Now without any delay, let’s dig into the implementation part. We are going to implement refresh tokens in the application that we built in the earlier part of this series.

Web API

There are some changes that we need to make in our Web API project.

Data Modelling

We need to move the user details into the database to implement the refresh token based flow. We have explained how to create a database from our models using the EF Core Code-First approach in our article ASP.NET Core Web API with EF Core Code-First Approach.

First, we need to modify the login model to include a refresh token and its expiry:

We are going to follow the steps that we discussed in the linked article to create a database from our model and add some seed data.

For that, let’s add a user DB context file and specify seed data in it: 

Cool. Now we need to register our db context in the ConfigureServices() method in Startup.cs:

Let’s also make sure that we move the database connection string into appsettings.json:

The next step is generating the database from code using migrations:

This will create the UserDB database and LoginModels table with seed data:

db and table created

Now, we need to do the credential validation against the database. We are also going to refactor the code a little bit by moving all token related logic into a separate TokenService class. 

AuthController

In the AuthController, first, we need to inject the UserContext and TokenService. Then, we are going to validate user credentials against the database. 

Once validation is successful, we need to generate refresh token in addition to the access token and save it along with the expiry date in the database:

Let’s also make sure to register the TokenService for dependency injection in ConfigureServices() method in Startp.cs:

TokenService

The logic for generating the access token, refresh token, and getting user details from the expired token goes into the TokenService class.

First, let’s define the ITokenService interface:

Then, we are going to implement the TokenService class: 

Here, we have created 3 methods. GenerateAccessToken() contains the logic to generate the access token. We have moved the existing code from AuthController to this method. 

GenerateRefreshToken() contains the logic to generate the refresh token. We use the RandomNumberGenerator class to generate a cryptographic random number for this purpose.

GetPrincipalFromExpiredToken() is used to get the user principal from the expired access token. We make use of the ValidateToken() method of JwtSecurityTokenHandler class for this purpose. This method validates the token and returns the ClaimsPrincipal object.

TokenController

Next is the implementation of the TokenController

Here, we implement a refresh endpoint, which gets the user information from the expired access token and validates the refresh token against the user. Once the validation is successful, we generate a new access token and refresh token and the new refresh token is saved against the user in DB.

We have also implemented a revoke endpoint which invalidates the refresh token.

Our Web API project is now ready.

Client App

We need to make some changes in our Angular client app as well. In the LoginComponent, we need to save the refresh token into the local storage along with the access token:

Now, let’s modify the AuthGuard service.

Here, once the access token is expired, we try refreshing it using the refresh token. If the refresh is successful, we store the new set of tokens in the local storage. If the refresh action does not work, we redirect the user back to the login page.

Finally, in the HomeComponent, we need to remove refresh token along with access token during logout.

That’s it. We have implemented refresh tokens in our application. In the next section, we are going to test the functionality.

Testing

First, we are going to test the Web API using Postman. Let’s invoke /api/auth/login by supplying the user credentials:

postman login

We can see that now the endpoint returns both access token and refresh token.

This updates both refresh token and expiry time in the database:

database table

Now, let’s wait till the access token expires. Remember, we had set the access token expiry as 5 minutes.

Once the access token is expired, we can see our protected endpoints return 401- Unauthorized response. We can verify that by accessing /api/customers

Now, we are going to refresh our access token using the refresh token. For that let’s do a POST request to /api/token/refresh with both the tokens that we received earlier:

postman refresh

This will return a new set of tokens which can be used further.

We can continue this cycle until the refresh token expires. In case we want to revoke the refresh token, we can do so by invoking the /api/token/revoke endpoint. This will require users to provide credentials once the current access token is expired. This endpoint is implemented as a security measure and we can make use of this in case current tokens are compromised.

Now, let’s test the client app. 

Earlier, the client app used to redirect us to the login page once the access token is expired. Now, we can see that once the access token is expired, the app automatically refreshes the token and keeps the session alive. Users don’t have to know about these steps happening in the background and they can continue working on the app without any interruptions. They will need to log in again only once the refresh token is expired, which gives them a longer window to work with.

By using an access token with a short expiry (typically, a few minutes) and a refresh token with a longer expiry (typically, a few days), we can make our application secure and at the same time give our users a seamless experience. If something goes wrong, we always have the option to revoke refresh tokens.

Conclusion

In this article, we have learned the following topics:

  • The concept of refresh tokens 
  • Why do we need refresh tokens in our application
  • How to implement refresh tokens in an ASP.NET Core Web API application with an Angular client app
  • How to revoke refresh tokens, if required