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

Most upvoted comments

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 says offline must be specified to get a refresh token, but no mention of prompt 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 the prompt=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.

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?

Not sure, but I think not. I think the refresh token is only invalidated if the user revokes access through accounts.google.com

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 scope offline_access to the authorization request? access_type is not part of the OAuth or OpenID specifications as far as I know.

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

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?

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 scope offline_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:

url := googleConfig.AuthCodeURL("your random string", oauth2.AccessTypeOffline, oauth2.ApprovalForce)

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.