karlwancl / GoogleCloudPrintApi

A .NET wrapper for Google Cloud Print API, based on .NET standard 1.4
MIT License
12 stars 14 forks source link

Problem with RefreshToken #9

Closed Jezternz closed 7 years ago

Jezternz commented 7 years ago

I have been stepping through my code, and I noticed that after calling GetTokenAsync() the token becomes corrupted in that the refresh token dissapears on response (is set to null).

eg:

Token token = _gCPProvider.GenerateRefreshTokenAsync(authCode).Result;
_gcpClient = new GoogleCloudPrintClient(_gCPProvider, token);
// token now contains Refresh Token & Access token

// ... now wait for expiry

Token token2 = _gcpClient.GetTokenAsync().Result;
// token2 now contains Access token ONLY not RefreshToken (refreshtoken = null)

// ... now wait for expiry

Token token3 = _gcpClient.GetTokenAsync().Result;
// Failure

Examining your code I noticed the following:

GoogleClientBase.cs:33

protected async Task UpdateTokenAsync(CancellationToken cancellationToken = default(CancellationToken))
{
    if (_token.ExpireDateTime > DateTime.Now)
        return;
    _token = await _oAuth2Provider.GenerateAccessTokenAsync(_token.RefreshToken, cancellationToken).ConfigureAwait(false);
}

GoogleOAuth2ProviderBase.cs:91

public async Task<Token> GenerateAccessTokenAsync(string refreshToken, CancellationToken cancellationToken = default(CancellationToken))
{
    return await GoogleApiOAuth2TokenUri
        .PostUrlEncodedAsync( new
        {
            client_id = _clientId,
            client_secret = _clientSecret,
            refresh_token = refreshToken,
            grant_type = OAuth2GrantTypeRefreshToken
        }, cancellationToken)
        .ReceiveJson<Token>()
        .ConfigureAwait(false);
}

What I think is probably happening, is that the "ReceiveJson" response from google, does not include the original RefreshToken? So before we set our token to override previous one, we should pull out the refresh token and add it, something like eg:

protected async Task UpdateTokenAsync(CancellationToken cancellationToken = default(CancellationToken))
{
    if (_token.ExpireDateTime > DateTime.Now)
        return;
    var oldRefreshToken = _token.RefreshToken;
    _token = await _oAuth2Provider.GenerateAccessTokenAsync(oldRefreshToken, cancellationToken).ConfigureAwait(false);
    _token.RefreshToken = oldRefreshToken;
}

It looks like we are overriding our "Refresh token without out of date Access token" with "No Refresh token, but up to date Access token", which of course causes problems down the track.

Jezternz commented 7 years ago

I ended up temporarily fixing this by modifying the "UpdateTokenAsync" method, to instead of "Update Access Token", now it will "Update Access token and retain previous Refresh Token"

protected async Task UpdateTokenAsync(CancellationToken cancellationToken = default(CancellationToken))
{
    if (_token.ExpireDateTime > DateTime.Now)
        return;
    var nToken = await _oAuth2Provider.GenerateAccessTokenAsync(_token.RefreshToken, cancellationToken).ConfigureAwait(false);
    _token = new Token(
        nToken.AccessToken,
        nToken.TokenType,
        nToken.ExpiresIn,
        _token.RefreshToken,
        nToken.ExpireDateTime
    );
}
Jezternz commented 7 years ago

Thanks for the lightning fast updates as usual!