seraphx2 / ESI.NET

A C# wrapper for the Eve Online ESI API.
https://www.nuget.org/packages/ESI.NET/
MIT License
40 stars 23 forks source link

SSO.Verify() returns expired token #26

Closed thoralmighty closed 4 years ago

thoralmighty commented 5 years ago

When I provide SSO.Verify(token) with a token aquired like so:

var token = await client.SSO.GetToken(GrantType.AuthorizationCode, authorizationCode);
auth_char = await client.SSO.Verify(token);

The method returns an object with the error "This token is not valid for any scopes," and all non-public requests made with it return objects where Data is null. When I check the properties of auth_char, the character name and everything is correct but the expiry date seems to be two hours in the past for some reason?

I figured it might be due to an expired OAuth token so I tried refreshing it before calling Verify:

var token = await client.SSO.GetToken(GrantType.AuthorizationCode, authorizationCode);
token = await client.SSO.GetToken(GrantType.AuthorizationCode, token.RefreshToken);
var auth_char = await client.SSO.Verify(token);

However, the second GetToken() call returns an empty object instead, where all properties are null. (Attempting to verify with that object naturally also results in a parse error.)

Is this to do with the v2 migration, or am I missing something to do with OAuth again? For the record, I don't persist my tokens in this project yet.

seraphx2 commented 5 years ago

When you are doing a refresh, the GrantType has to change to: GrantType.RefreshToken

thoralmighty commented 5 years ago

Ah, I believe that was a typo when copying over the snippet. When I change the above snippet so that the refresh uses GrantType.RefreshToken as you said, I get an exception on this line:

var auth_char = await client.SSO.Verify(token);

As follows:

Newtonsoft.Json.JsonReaderException: 'Unexpected character encountered while parsing value: <. Path '', line 0, position 0.'

When inspecting the token object all properties are either null or zero.

thoralmighty commented 5 years ago

Okay, fair enough. So am I right in assuming the procedure should be as follows:

var token = await client.SSO.GetToken(GrantType.AuthorizationCode, authorizationCode);
await client.SSO.GetToken(GrantType.RefreshToken, token.RefreshToken);
var auth_char = await client.SSO.Verify(token);

This will still result in an AuthorizedCharacterData object where the ExpiresOn property is almost 2 hours in the past, and the API request results in a v2 Forbidden error: "This token is not valid for any scopes."

seraphx2 commented 5 years ago

You do not need to run verify on Refresh Tokens.

seraphx2 commented 5 years ago

sorry..i deleted the message since i needed to verify my assumption

seraphx2 commented 5 years ago

I have a utility method in my application to update the database with the new token information:

public AuthorizedCharacterData RefreshTokenData(Character owner)
{
    var auth_char = JsonConvert.DeserializeObject<AuthorizedCharacterData>(owner.AuthorizedCharacter);

    if (auth_char.ExpiresOn < TimeZoneInfo.ConvertTimeToUtc(DateTime.Now))
    {
        var token = _client.SSO.GetToken(GrantType.RefreshToken, auth_char.RefreshToken).Result;
        auth_char.Token = token.AccessToken;
        auth_char.ExpiresOn = TimeZoneInfo.ConvertTimeToUtc(DateTime.Now.AddSeconds(token.ExpiresIn));
        auth_char.RefreshToken = token.RefreshToken;

        owner.AuthorizedCharacter = JsonConvert.SerializeObject(auth_char);
        _db.SaveChanges();
    }

    return auth_char;
}

you can obviously do that however you like, but I just have auth_char serialized into a column in my DB and whenever I need info from it, I just deserialize when I retrieve the character. It may not be the right way to do it, but the main point is, you just refresh the token, no other methods needed.

I wrote this method because whenever I know I might need to refresh the character data, i also need to know if the token is expired. so, by writing this utility, it takes the character row (maybe an auth_char row if you are more normalized than me) and does the refresh, then updates the character. when it gets out of the method, the rest of the code can continue on with the updated token info.