Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practices for managing Web API JWT token in another Web API

My Project:

Web API project - ASP .NET Framework 4.8

Problem?

The code flow is as follows:

1.) The API is called -> it must call another API -> 2.) Get JWT authentication token -> 3.) Calls the desired method.

The problem is if my API is called 100 times, I will make 100 calls for the GetJwtToken() method and another 100 for the desired method itself, which seems like an overhead on the auth server. The token itself has a lifespan of 2 hours.

Are there any documented best practices on how to manage a Web API JWT token in another Web API?

What have I tried?

I've tried the following solutions and I'm still not sure whether they could be considered good practices.

  • One static class with two static properties Token and ValidTo and one static method GetJwtToken() that updates those properties. Before each call to the desired external API method, we check the ValidTo property and update the Token value if it has expired, via the static method.
  • In our service, we have one static private field Token.The method that calls the external API method is surrounded by a try catch blocks. The Catch(WebException ex) expects an Unauthorized exception if the token has expired. I check for HTTP Status Code 401 - Unauthorized.
if (response.StatusCode == HttpStatusCode.Unauthorized)

In case we go into that if clause we update the Token property by calling the GetJwtToken() method inside the catch block and then calling recursively the method again. In this way, we update the token only when it has expired and an unauthorized exception was thrown.

  • Another idea that I got, but didn't test isActionFilterAttribute with overridden OnActionExecuting(HttpActionContext actionContext) method. Before we go into the Web API controller the action attribute has already checked whether we have Token and if it has expired. The problem here was I am not sure where to save the Token property. Possibly as a static value in another class.

Are there any other ways to manage a JWT Token of a Web API inside another Web API and what is considered best practices?
Some code snippets, pseudo-code, or articles would be appreciated.


Edit1:
I've read this question, but it doesn't help me, since it's about how to manage the token on the front end part. The project here is Web API it's all on the server-side.
Edit2:
Edited some sentences here and there so it's more readable.
Edit3:
Added one more option that I thought about.

like image 245
G.Dimov Avatar asked Sep 25 '20 11:09

G.Dimov


People also ask

Is it good practice to store JWT token in database?

A JWT needs to be stored in a safe place inside the user's browser. If you store it inside localStorage, it's accessible by any script inside your page. This is as bad as it sounds; an XSS attack could give an external attacker access to the token.

How do I pass a JWT token from one Microservice to another?

One approach you can try is by having a separate session/jwt service. Roles and responsibility of that service would be to store/validate and authenticate having following endpoints. 1. First hit to login-service > login service getting token from jwt-service > returning jwt token to UI/client.


1 Answers

I'd handle this in some kind of BaseApiService

public class BaseApiService
{
    private readonly IHttpClientFactory httpClientFactory;
    private readonly ITokenHandler tokenHandler;

    public BaseApiService(IHttpClientFactory httpClientFactory, ITokenHandler tokenHandler)
    {
        this.httpClientFactory = httpClientFactory;
        this.tokenHandler = tokenHandler;
    }

    protected async Task<HttpResponseMessage> RequestAsync(HttpRequestMessage requestMessage)
    {
        var httpClient = httpClientFactory.CreateClient();
        requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokenHandler.Token);
        var response = await httpClient.SendAsync(requestMessage);
        
        if (!response.IsSuccessStatusCode)
        {
            if (response.StatusCode == HttpStatusCode.Unauthorized)
            {
                var token = await tokenHandler.UpdateTokenAsync();
                requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
                return await RequestAsync(requestMessage);
            }
        }

        return response;
    }
}

Which would be responsible for making request, response serialization (notice I've used string responses for simplicity sake) and handling token for each request. Also you might want to consider handling errors and also handle infinite loops because it's currently calling self (e.g. on second call if it's unauthorized again, exit with error).

Token handler would be defined as singleton in DI and this is implementation

public interface ITokenHandler
{
    string Token { get; }
    Task<string> UpdateTokenAsync();
}

public class TokenHandler : ITokenHandler
{
    private readonly IHttpClientFactory httpClientFactory;
    public string Token { get; private set; } 

    public TokenHandler(IHttpClientFactory httpClientFactory)
    {
        this.httpClientFactory = httpClientFactory;
    }
    
    public async Task<string> UpdateTokenAsync()
    {
        var httpClient = httpClientFactory.CreateClient();
        var result = await httpClient.PostAsync("/external-api/token", new FormUrlEncodedContent(new []
        {
            new KeyValuePair<string, string>("username", "external-admin"),
            new KeyValuePair<string, string>("password", "external-password"),
        }));

        // or handle it however you want
        var token = result.IsSuccessStatusCode
            ? await result.Content.ReadAsStringAsync()
            : null;

        if (!String.IsNullOrEmpty(token))
        {
            Token = token;
        }

        return Token;
    }
}

And this is how you'd consume your BaseApiService

public class TodoService : BaseApiService
{
        public TodoService(IHttpClientFactory httpClientFactory, ITokenHandler tokenHandler) 
        : base(httpClientFactory, tokenHandler)
    {
    }

    public async Task<string> GetTodoAsync(int id)
    {
        var response = await RequestAsync(new HttpRequestMessage(HttpMethod.Get, $"/todo/{id}"));
        return await response.Content.ReadAsStringAsync();
    }
}

I don't think you need to add any ValidTo logic, but just rely on your Unauthorized response from 3rd party API, because you'll just complicate your code and you'll have to handle Unauthorized responses anyway.

Only thing is that you might lock getting/setting of token from TokenHandler, but this is just a basic example to show an idea how I'd implement it.

like image 195
zhuber Avatar answered Sep 28 '22 20:09

zhuber