Course Content
ASP.NET Core Web API Fundamentials
0/2
Method Safety And Method Idempotency
0/1
Working With OPTIONS and HEAD Requests
0/2
Root Document in ASP.NET Core Web API
0/1
Ultimate ASP.NET Core Web API

It is a good practice to have a separate endpoint for the refresh token action, and that’s exactly what we will do now. Let’s start by creating a new TokenController in the Presentation project:

using CompanyEmployees.Core.Services.Abstractions;
using Microsoft.AspNetCore.Mvc;

namespace CompanyEmployees.Infrastructure.Presentation.Controllers;

[Route("api/token")]
[ApiController]
public class TokenController : ControllerBase
{
    private readonly IServiceManager _service;

    public TokenController(IServiceManager service) => _service = service;
}

Before we continue with the controller modification, we are going to modify the IAuthenticationService interface:

public interface IAuthenticationService
{
    Task<IdentityResult> RegisterUser(UserForRegistrationDto userForRegistration);
    Task<bool> ValidateUser(UserForAuthenticationDto userForAuth);
    Task<TokenDto> CreateToken(bool populateExp);
    Task<TokenDto> RefreshToken(TokenDto tokenDto);
}

We also have to implement this method:

public async Task<TokenDto> RefreshToken(TokenDto tokenDto)
{
    var principal = GetPrincipalFromExpiredToken(tokenDto.AccessToken);

    var user = await _userManager.FindByNameAsync(principal.Identity.Name);
    if (user == null || user.RefreshToken != tokenDto.RefreshToken ||
        user.RefreshTokenExpiryTime <= DateTime.Now)
        throw new RefreshTokenBadRequest();

    _user = user;

    return await CreateToken(populateExp: false);
}

We first extract the principal from the expired token and use the Identity.Name property, which is the user’s username, to fetch that user from the database. If the user doesn’t exist, or the refresh tokens are not equal, or the refresh token has expired, we stop the flow and return the BadRequest response to the user. Then we just populate the _user field and call the CreateToken() method to generate new Access and Refresh tokens.

This time, we don’t want to update the expiry time of the refresh token thus sending false as an argument. Since we don’t have the RefreshTokenBadRequest class, let’s create it in the DomainExceptions folder: And add a required using directive in the AuthenticationService class to remove the current error:

using CompanyEmployees.Core.Domain.Exceptions;

Finally, let’s create a new action in the TokenController:

[HttpPost("refresh")]
[ServiceFilter(typeof(ValidationFilterAttribute))]
public async Task<IActionResult> Refresh([FromBody] TokenDto tokenDto)
{
    var tokenDtoToReturn = await _service.AuthenticationService.RefreshToken(tokenDto);

    return Ok(tokenDtoToReturn);
}

That’s it. Our refresh token logic is prepared and ready for testing. Let’s first send the POST authentication request:

curl --location 'https://localhost:5001/api/authentication/login' 
--header 'Content-Type: application/json' 
--header 'Accept: application/json' 
--data '{
    "username": "JDoe",
    "password": "Password1000"
}'

As before, we have both tokens in the response body. Now, let’s send the POST refresh request with these tokens as the request body:

curl --location 'https://localhost:5001/api/token/refresh' 
--header 'Content-Type: application/json' 
--header 'Accept: application/json' 
--data '{
    "accessToken":"your access token",
    "refreshToken": "your refresh token"
}'

And we can see new tokens in the response body. Additionally, if we inspect the database, we will find the same refresh token value. Usually, in your client application (if you are the one creating the client for the API), you would inspect the exp claim of the access token and if it is about to expire, your client app will send the request to the api/token endpoint and get a new set of valid tokens.

0% Complete