Web API project - ASP .NET Framework 4.8
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?
I've tried the following solutions and I'm still not sure whether they could be considered good practices.
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.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.
ActionFilterAttribute
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With