Search code examples
pythonpython-3.xgoogle-apigoogle-calendar-apigoogle-oauth

Google API flow requires consent for refresh-token every time


Using python 3.6, requests==2.22.0

Trying to use the Google API, in particular mobile and desktop apps flow

I am able to generate an auth code by using this url:

url = (
    'https://accounts.google.com/o/oauth2/v2/auth?'
    'scope={scope}'
    'response_type=code&'
    'redirect_uri={redirect_uri}&'
    'client_id={client_id}&'
    'access_type=offline'.format(
        redirect_uri=redirect_uri,
        client_id=client_id,
        scope=scope,
    )
)

The redirect_uri I am using (for now) is simply https://google.com, and it is registered in the developer app I generated, in the Authorized redirect URIs section and in the Authorized domains section under the OAuth consent settings page/tab.

Once I paste the produced url in the browser - I get a code that I can extract and use to make the next call:

data = {
    'client_id': client_id,
    'client_secret': client_secret,
    'grant_type': 'authorization_code',
    'code': code,
    'redirect_uri': redirect_uri,
}
url = 'https://oauth2.googleapis.com/token'

response = requests.post(
    url,
    data=data,
    headers={
        'Content-Type': 'application/x-www-form-urlencoded',
    },

print(response)
print(response.json())

The output:

<Response [200]>
{tokens dictionary} <-- more downstream

Here is the question:
In the beginning I was experimenting with the basic scopes from the various examples available everywhere: email+profile, and the result I got was this:

{'access_token': '******', 'expires_in': 3594, 'scope': 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile openid', 'token_type': 'Bearer', 'id_token': '******'} <-- id_token is a JWT

Next, I added the actual scopes I am interested in (and made sure to add them within the developer app):

https://www.googleapis.com/auth/calendar.events+https://www.googleapis.com/auth/calendar.readonly

The result I am getting is this:

{'access_token': '******', 'expires_in': 3595, 'scope': 'https://www.googleapis.com/auth/calendar.events https://www.googleapis.com/auth/calendar.readonly', 'token_type': 'Bearer'}

No refresh token? (I specifically require it to refresh the access token)

I then read this stack overflow post and added "&prompt=consent" to the code grant URL above: (https://accounts.google.com/o/oauth2/v2/auth)

Now I am getting:

{'access_token': '******', 'expires_in': 3600, 'refresh_token': '******', 'scope': 'https://www.googleapis.com/auth/calendar.events https://www.googleapis.com/auth/calendar.readonly', 'token_type': 'Bearer'}

I have the refresh token now, but is that the only way? If the user will end up going through the flow again it will force another consent page flow - which should not be required after an initial consent was already given.

Is there any way to get the refresh token without an explicit consent every time?


Solution

  • Is there any way to get the refresh token without an explicit consent every time?

    No. The refresh token is returned the first time with the user consent to off line access. Google assumes that you have saved it and there for dont need another one. Revoke the users access and request access again you should get a refresh token. or sending request prompt will request that the user grant you off line access again and you will again get a new refresh token.