Tutorial built with .NET 5.0

Other versions available:

In this tutorial we’ll go through an example of how to implement JWT (JSON Web Token) authentication with refresh tokens in a .NET 5.0 API.

Authentication implementation overview

Authentication is implemented with JWT access tokens and refresh tokens. On successful authentication the API returns a short lived JWT access token that expires after 15 minutes, and a refresh token that expires after 7 days in an HTTP Only cookie. The JWT is used for accessing secure routes on the API and the refresh token is used for generating new JWT access tokens when (or just before) they expire.

HTTP Only cookies are used for refresh tokens to increase security because they are not accessible to client-side javascript which prevents XSS (cross site scripting) attacks, and refresh tokens only have access to generate new JWT tokens (via the /users/refresh-token route) which prevents them from being used in CSRF (cross site request forgery) attacks.

API endpoints

The example .NET API has the following endpoints/routes to demonstrate authenticating with JWT, refreshing and revoking tokens, and accessing secure routes:

  • /users/authenticate - public route that accepts POST requests containing a username and password in the body. On success a JWT access token is returned with basic user details, and an HTTP Only cookie containing a refresh token.
  • /users/refresh-token - public route that accepts POST requests containing a cookie with a refresh token. On success a new JWT access token is returned with basic user details, and an HTTP Only cookie containing a new refresh token (see refresh token rotation just below for an explanation).
  • /users/revoke-token - secure route that accepts POST requests containing a refresh token either in the request body or in a cookie, if both are present priority is given to the request body. On success the token is revoked and can no longer be used to generate new JWT access tokens.
  • /users - secure route that accepts GET requests and returns a list of all the users in the application.
  • /users/{id} - secure route that accepts GET requests and returns the details of the user with the specified id.
  • /users/{id}/refresh-tokens - secure route that accepts GET requests and returns a list of all refresh tokens (active and revoked) of the user with the specified id.
Refresh token rotation

Each time a refresh token is used to generate a new JWT token (via the /users/refresh-token route), the refresh token is revoked and replaced by a new refresh token. This technique is known as Refresh Token Rotation and increases security by reducing the lifetime of refresh tokens, which makes it less likely that a compromised token will be valid (or valid for long). When a refresh token is rotated the new token is saved in the ReplacedByToken field of the revoked token to create an audit trail in the database.

Revoked and expired refresh token records are kept in the database for the number of days set in the RefreshTokenTTL property in the appsettings.json file. The default is 2 days, after which old inactive tokens are deleted by the user service in the Authenticate() and RefreshToken() methods.

Revoked token reuse detection

If an attempt is made to generate a new JWT token using a revoked refresh token, the API treats this as a potentially malicious user with a stolen (revoked) refresh token, or a valid user attempting to access the system after their token has been revoked by a malicious user with a stolen (active) refresh token. In either case the API revokes all descendant tokens because the token and its descendants were likely created on the same device which may have been compromised. The reason revoked is recorded as "Attempted reuse of revoked ancestor token" so the user can see it in the database or via the /users/{id}/refresh-tokens route.

EF Core InMemory database for testing

To keep the api code as simple as possible, it is configured to use the EF Core InMemory database provider which allows Entity Framework Core to create and connect to an in-memory database rather than you having to install a real db server. This can be easily switched out to a real db provider when you’re ready to work with a database such as SQL Server, Oracle, MySql etc. For an example api that uses SQLite in development and SQL Server in production see .NET 5.0 - Simple API for Authentication, Registration and User Management.

Code on GitHub

The tutorial project is available on GitHub at https://github.com/cornflourblue/dotnet-5-jwt-refresh-tokens-api.

#jwt #dotnet #api #web-development

How to Implement JWT Authentication with .NET 5.0 API
6.35 GEEK