4

I'm currently using a DelegatingHandler to check requests if they become Unauthorized when sending to our Web API. If the response does become unauthorized, I'm currently sending a refresh token to log the user back in and then updating the following requests with the new access token. The issue that I'm running into, is that many of the calls are asynchronous and continue on before the other ones finish and the refresh token code is hit multiple times cause multiple refresh tokens to be updated/saved. What is the best way to handle this scenario? My current Handler looks like this..

public class AuthenticationHandler : DelegatingHandler
{
    private AccountRepository _accountRepo;

    private string _originalAuthToken = String.Empty;

    private const int _maxRefreshAttempts = 1;

    public AuthenticationHandler() : this(new HttpClientHandler())
    {
        _accountRepo = new AccountRepository();
    }

    protected AuthenticationHandler(HttpMessageHandler innerHandler) : base(innerHandler)
    {

    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpResponseMessage response = new HttpResponseMessage();

        request = CheckForAuthToken(request);

        response = await base.SendAsync(request, cancellationToken);

        if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
        {
            for (int i = 1; i == _maxRefreshAttempts; i++)
            {
                response = await _accountRepo.SignInWithRefreshToken();

                if (response.IsSuccessStatusCode)
                {
                    request = CheckForAuthToken(request);

                    response = await base.SendAsync(request, cancellationToken);
                }
            }
        }

        return response;
    }

    private HttpRequestMessage CheckForAuthToken(HttpRequestMessage request)
    {
        if (App.CurrentLoggedInUser != null)
        {
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", App.CurrentLoggedInUser.AccessToken);
        }

        return request;
    }
}

I'm not sure if using a handler is best practice or ideal. I thought it would be nice to check every request just incase the access token becomes invalid during the call itself. What is the recommended approach when using refresh tokens? I am also using a DelegatingHandler to retry failed requests 2 times but the Authentication Handler is the last handler in the HttpClient pipeline. Any suggestions is greatly appreciated!

Humpy
  • 2,004
  • 2
  • 22
  • 45
  • 1
    I think your approach is the way to go. In fact, I'm writing a blog post about this as we speak. One suggestion I would make is to use Polly to implement your retry logic (i.e. if the first call fails due to a 403 or 401, Polly will call your refresh token logic and then retry the initial call). – Sipke Schoorstra Jun 23 '18 at 16:04
  • I hit too soon. I wanted to also say that I'm not sure I understand the issue with the asynchronous calls. Does that happen in te code example you provided, or is it about other calls that are depending on this http client to be authorized? – Sipke Schoorstra Jun 23 '18 at 16:05
  • The calls after the initial call also depends on the access token. So if the first one is denied, the calls after are also denied. I have also changed a few things since, the Authentication handler is now it's own handler as well as the Retry Handler. It seems to work 'better' but still isn't the perfect solution. – Humpy Jun 25 '18 at 13:39
  • @SipkeSchoorstra do you have a link to your blog post? I'd like to take a look. – Humpy Jun 26 '18 at 13:19
  • 3
    So sorry dude, while researching I found a post that did exactly what I was writing about. I can't find it atm, but I here's a gist: https://gist.github.com/sfmskywalker/896c4e26932d163093c5b28a790eb65f It uses a custom interface called ISecurityTokenAccessor which has only one method: RenewAccessTokenAsync, which you would implement to provide a new access token. In my case, I use this to get a token from Zuora and another implementation gets it from Salesforce. But the point is how to use a custom http handler and Polly to automatically re-authorize. Let me know if you need more details! – Sipke Schoorstra Jul 06 '18 at 17:27
  • @Humpy Just found this topic. What does the `response = await _accountRepo.SignInWithRefreshToken();` line do? Assuming you need to get a new JWT token - do you need to then use a separate HttpClient to make the call to the API, send your refresh token stored on the device and get a new pair of tokens from API? Since all this logic is inside DelegatingHandler used to create your main HttpClient, the only way to make an API call is to have a second HttpClient for just token calls, right? – Varin Nov 16 '21 at 12:14

0 Answers0