Hi there,
I recognize this might not be the best place for this question, but I'm struggling to find answers elsewhere. We have a number of integrations against Google's APIs and every once in a while, we'll get large groups of invalid_grant errors when attempting to refresh a token. I can't seem to figure out why this is other than ask the user to reauthorize, but for thousands of people at a time, this doesn't seem ideal and feels like something else is wrong.
Any idea why we see large groups of these errors?
Here is our code:
def auth_client
Signet::OAuth2::Client.new(
token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
audience: 'https://accounts.google.com/o/oauth2/token',
scope: SCOPE,
access_token: connection.token,
refresh_token: connection.refresh_token,
person: connection.email,
client_id: ENV["GMAIL_CLIENT_ID"],
client_secret: ENV["GMAIL_CLIENT_SECRET"],
)
end
def refresh_token
new_token = auth_client.fetch_access_token!
connection.update!(
token: new_token["access_token"],
token_expiration: new_token["expires_in"].seconds.from_now
)
rescue ::Signet::AuthorizationError => e
if e.message =~ /token has been revoked/i
raise Providers::Gmail::Activity::TokenRevoked.new(e.message)
elsif e.message =~ /token has been expired or revoked/i
raise Providers::Gmail::Activity::TokenRevoked.new(e.message)
else
raise e
end
end
:+1: I have the same problem on refreshing, is it possible to use google-auth-library-ruby gem ? I tried this method for assigning access token, but it doesn't works, works only with Signet gem.
This is mostly like due to password resets. OAuth grants with the gmail scopes are revoked when a user changes their password.
See https://support.google.com/a/answer/6328616
In general, users can revoke grants at any time. You should be able to handle that case gracefully and alert the user that they need to reauthorize if they wish to continue using the functionality provided.
Hi - I just ran into this during development and I know for a fact that the user's password _has not changed_ as it was my own account.
> creds.expired?
=> true
> creds.refresh!
Signet::AuthorizationError: Authorization failed. Server message:
{
"error": "invalid_grant",
"error_description": "Token has been expired or revoked."
}
One important detail is that this (google) account was authorized twice with _separate_ OAuth2 client secrets, with _separate_ token stores - but with the _same_ project_id.
Looking on the google account page for third party apps, I only saw a _single_ entry despite having two authorizations with separate client secrets. Both authorization_token worked, but when they expired only the most recent refresh_token worked.
I suspect that Google only stores a single refresh_token per project_id. The problem I am unable to solve is how do I determine _which google account_ a user has selected? We could work around this case if we had a token store that knew which google account a token was associated with, and shared that between our application instances, but I can't seem to determine this from the OAuth2 flow?
@noahhaon what did you end up doing to solve this issue? I'm running into what appears to be the same problem.
I never got to the bottom of it. I suspect there was some event that invalidated those users' tokens due to some server-side stuff.
@brandonhilkert thanks for letting me know.
The issue for me was around the fact that we had 3 environments - dev, staging, and production - and one project in the Google console. I had previously created credentials for each env under 1 project. When I (or any user) integrated on one environment, it would then trigger the invalid_grant error on the other environments. I ended up creating 3 different projects in the google console, one for each environment, and it's been working properly now.
Most helpful comment
Hi - I just ran into this during development and I know for a fact that the user's password _has not changed_ as it was my own account.
One important detail is that this (google) account was authorized twice with _separate_ OAuth2 client secrets, with _separate_ token stores - but with the _same_
project_id.Looking on the google account page for third party apps, I only saw a _single_ entry despite having two authorizations with separate client secrets. Both
authorization_tokenworked, but when they expired only the most recentrefresh_tokenworked.I suspect that Google only stores a single
refresh_tokenperproject_id. The problem I am unable to solve is how do I determine _which google account_ a user has selected? We could work around this case if we had a token store that knew which google account a token was associated with, and shared that between our application instances, but I can't seem to determine this from the OAuth2 flow?