google-api-python-client: Not getting refresh token even though access_type is offline in step1
Hi,
I’m doing some work with Google Search Console API. Before I start using the API, I need to get oauth credentials of the client: authorize my app to get access tokens to call the Search Console API on his behalf and do modifications to his Search Console data.
I’m doing the needed step1 and step2 flow exchange calls, but still I’m not getting any refresh token whatsoever, even though access_type
is set to offline
in the authorize url generated in step1.
Am I missing something?
Here’s my code:
authorize_url = flow.step1_get_authorize_url()
And the url is like:
https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/webmasters&redirect_uri=http://mywebsite.com/google/scapi/callback/&response_type=code&client_id=redacted_client_id&access_type=offline
And then in my callback handler:
credentials = flow.step2_exchange(code)
Here’s the json I’m getting from the Credentials object:
{
"_module": "oauth2client.client",
"token_expiry": "2016-04-05T12:46:24Z",
"access_token": "ya29.. -- redacted --",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"invalid": false,
"token_response": {
"access_token": "ya29.. -- redacted --",
"token_type": "Bearer",
"expires_in": 3419
},
"client_id": " -- redacted --",
"id_token": null,
"client_secret": " -- redacted --",
"revoke_uri": "https://accounts.google.com/o/oauth2/revoke",
"_class": "OAuth2Credentials",
"refresh_token": null,
"user_agent": null
}
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 26
- Comments: 31 (3 by maintainers)
Commits related to this issue
- Automating release via Travis. Fixes #213. — committed to akrherz/google-api-python-client by dhermes 9 years ago
- Fix refreshing Google tokens dockstore/dockstore#2745 Problem was we weren't getting a refresh token back in the initial authentication dance. Looks like a dependabot update of ng2-ui-auth caused t... — committed to dockstore/dockstore-ui2 by coverbeck 5 years ago
- Fix refreshing Google tokens (#746) * Fix refreshing Google tokens dockstore/dockstore#2745 Problem was we weren't getting a refresh token back in the initial authentication dance. Looks li... — committed to dockstore/dockstore-ui2 by coverbeck 5 years ago
- bug fix: gmail don't return refresh token https://github.com/googleapis/google-api-python-client/issues/213 — committed to agent8/Mailspring by quanzs 4 years ago
- google_auth.py: Add prompt='consent' in order to get a refresh token every time See for details: https://github.com/googleapis/google-api-python-client/issues/213 — committed to szilard-nemeth/google-api-wrapper by szilard-nemeth 3 years ago
- Fix a deserialization bug due to Google not returning refresh_token. See https://github.com/googleapis/google-api-python-client/issues/213. — committed to Torus-Portals/portals-backend by iobtl 3 years ago
- Fix a deserialization bug due to Google not returning refresh_token. See https://github.com/googleapis/google-api-python-client/issues/213. — committed to Torus-Portals/portals-backend by iobtl 3 years ago
- Fix a deserialization bug due to Google not returning refresh_token. See https://github.com/googleapis/google-api-python-client/issues/213. — committed to Torus-Portals/portals-backend by iobtl 3 years ago
- Fix a deserialization bug due to Google not returning refresh_token. See https://github.com/googleapis/google-api-python-client/issues/213. — committed to Torus-Portals/portals-backend by iobtl 3 years ago
This can happen if you’ve already authorized the app once before. The oauth2 server will only ever mint one refresh token at a time, and if you request another access token via the flow it will operate as if you only asked for an access token.
You can try constructing the flow object with
prompt='consent'
and see if that fixes it.Related: google/oauth2client#453
Discovering this was painful.
@jonparrott I would not say that documents this issue: it makes no reference whatsoever to refresh tokens. The
access_type
description saysoffline
must be specified to get a refresh token, but no mention ofprompt
there. The Refresh Tokens section says “Be sure to store the refresh token safely and permanently, because you can only obtain a refresh token the first time that you perform the code exchange flow.” That kind of sort of describes the situation here, but “the first time” is vague, and makes no mention of theprompt=consent
workaround. Anyway, good luck.This is ridiculous. Why is this only documented in a blog post? Am I missing something?
5 years after the original post and this is still an issue. The documentation is not clear at all. I only found this out by finding this issue report.
I’m passing
access_type=offline&prompt=consent
and I still can’t get a new refresh token. This really kind of stinks if you have a crazy bug/issue where refresh token was lost and end users can’t even reconnect to fix it.@niemyjski Users have to deauthorize your application from their security dashboard and you’ll get a new refresh token the next time they authorize.
this is nonsense
To add on this earlier comment, how come Google still got the “OpenID certified” badge (on the upper-right of this page), while its OpenID implementation does not support
offline_access
scope? 😕Google… I expected better from you…
Setting
prompt=consent
along with deauthorizing the app from user’s security dashboard as @Sieabah mentioned was the complete solution for me to get refresh token.Stumbled upon this while looking for solutions but if you use a Google privileged scope the refresh tokens do expire at some point without you knowing, so you will need to regain consent every 2(?) months I believe.
Apologies for bumping this topic but might be useful for anyone who finds this issue.
Not sure, but I think not. I think the refresh token is only invalidated if the user revokes access through accounts.google.com
Not that I’m aware of.
November 2020, and I bumped into this documentation while trying to get a missing refresh_token. Thankful I found it, as it was what I needed, but I question why it’s not in the main documentation anywhere
I just tested with an old and new token/refresh_token combination and the old pair continues to be valid when refreshing.
@jonparrott does forcing a new login/consent by the user invalidate prior refresh tokens for that user even if the prior refresh token was not used?
In addition are there any plans to either move away from the
access_type=offline
parameter or add as an alternative the mechanism specified in the OpenID specification, which is adding the scopeoffline_access
to the authorization request?access_type
is not part of the OAuth or OpenID specifications as far as I know.http://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
This is documented here for now: https://developers.google.com/identity/protocols/OpenIDConnect#prompt
We are actively working to improve our auth docs, we are aware that it’s difficult to find documentation about both the high-level libraries and the low-level protocol. Stay tuned.
Bump on this issue thread – I just had to deal with this painful issue. It would be great if @google-admin could add this to their documentation!
const authUrl = oAuth2Client.generateAuthUrl({ access_type: ‘offline’, scope: SCOPES, prompt: ‘consent’, // this fixed });
Unfortunately,
prompt=consent&access_type=offline
no longer works. Does anyone know if there’s a workaround for this issue?It’s impressive that a company of the size of Google doesn’t care at all about this issue.
Golang solution for “golang.org/x/oauth2” and “golang.org/x/oauth2/google” is:
now url contains access_type=offline and prompt=consen
I’m using the recent java google oauth2 client. Same issue. No warning. No error. Nothing. Had to use the “force” thing. Interestingly enough, that only applied to Microsoft Edge. Same account on Firefox/Chrome had no issue at all… what are you guys doing?
If someone happens to come across this and is using the PHP client, then I fixed this issue by setting the prompt to force:
$client->setApprovalPrompt('force');
For some reason when the option is left at it’s default (auto) the refresh token is not returned, but when set to force, it is.