aarondcoleman / Fitbit.NET

Fitbit .NET API Client Library
MIT License
193 stars 138 forks source link

unexpected stale token response #281

Open wkeuning opened 4 years ago

wkeuning commented 4 years ago

Hi,

When I make an API request with a token that is expired I receive the following error:

FitbitTokenException: In interceptor OAuth2AutoRefreshInterceptor inside method InterceptResponse we received an unexpected stale token response - during the retry for a call whose token we just refreshed 401

I traced this to the following method in OAuth2AutoRefreshInterceptor.cs:

public async Task<HttpResponseMessage> InterceptResponse(Task<HttpResponseMessage> response, CancellationToken cancellationToken, FitbitClient Client)
        {
            if (response.Result.StatusCode == System.Net.HttpStatusCode.Unauthorized)//Unauthorized, then there is a chance token is stale
            {
                var responseBody = await response.Result.Content.ReadAsStringAsync();

                if (IsTokenStale(responseBody))
                {
                    Debug.WriteLine("Stale token detected. Invoking registered tokenManager.RefreskToken to refresh it");
                    await Client.RefreshOAuth2TokenAsync();

                    //Only retry the first time.
                    if (!response.Result.RequestMessage.Headers.Contains(CUSTOM_HEADER))
                    {
                        var clonedRequest = await response.Result.RequestMessage.CloneAsync();
                        clonedRequest.Headers.Add(CUSTOM_HEADER, CUSTOM_HEADER);
                        return  await Client.HttpClient.SendAsync(clonedRequest, cancellationToken);
                    }
                    else if (response.Result.RequestMessage.Headers.Contains(CUSTOM_HEADER))
                    {
                        throw new FitbitTokenException(response.Result, message: $"In interceptor {nameof(OAuth2AutoRefreshInterceptor)} inside method {nameof(InterceptResponse)} we received an unexpected stale token response - during the retry for a call whose token we just refreshed {(int)response.Result.StatusCode}");   
                    }
                }                
            }

            //let the pipeline continue
            return null;
        }

When debugging I verified that Client.RefreshOAuth2TokenAsync(); does actually refresh the AccessToken. However clonedRequest uses the old AccessToken in the Authorization header and not the newly refreshed AccessToken, therefore the request fails again and this triggers the 'unexpected stale token response'-FitbitTokenException as the request now contains the custom header.

Adding clonedRequest.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Client.AccessToken.Token);

here:

if (!response.Result.RequestMessage.Headers.Contains(CUSTOM_HEADER))
{

    var clonedRequest = await response.Result.RequestMessage.CloneAsync();
    clonedRequest.Headers.Add(CUSTOM_HEADER, CUSTOM_HEADER);
    clonedRequest.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Client.AccessToken.Token);
    return  await Client.HttpClient.SendAsync(clonedRequest, cancellationToken);
}

fixes this.

dsurrao commented 1 year ago

I am getting the same error. Is there a fix planned for this?