Trying to get Google refresh token but end up with an Auth0 refresh token instead

I’m having some troubles retrieving a refresh token from Google. (using python 3)

I currently use Auth0 with a social connection to Google (with a scope including google contacts).

I specified “offline_access” while login:

var auth0 = new Auth0({
    domain: '**domain**.eu.auth0.com',
    clientID: CLIENT_ID,
    callbackURL: '{{ AUTH0_CALLBACK_URL }}'
});
// sign-in with social provider with plain redirect
$('.signin-google').on('click', function () {
    auth0.signin({
        connection: 'google-oauth2',
        scope: 'openid profile offline_access',
        device: 'Browser'
    }); // use connection identifier
});

my django login function :

if 'code' in request.GET:
    auth_code = request.GET'code']

    headers = {'content-type': 'application/json'}
    token_url = "https://{domain}/oauth/token".format(domain=my_domain)
    token_payload = {
        'client_id': CLIENT_ID,
        'client_secret': CLIENT_SECRET,
        'redirect_uri': getattr(settings, "AUTH0_CALLBACK_URL", None),
        'code': auth_code,
        'grant_type': 'authorization_code',
        'scope': 'openid read:user_idp_tokens offline_access'
    }
    token_info = requests.post(token_url, json=token_payload, headers=headers).json()

I then store the refresh_token as app_metadata in my auth0 user profile :

if 'refresh_token' in token_info:
    refresh_token = token_info'refresh_token']

    # Get management access token
    management_payload = {
        'client_id': CLIENT_ID,
        'client_secret': CLIENT_SECRET,
        'audience': 'https://**domain**.eu.auth0.com/api/v2/',
        'grant_type': 'client_credentials'
    }
    auth_management_infos = requests.post(token_url, json=management_payload, headers=headers).json()
    auth_management_access_token = auth_management_infos'access_token']

    # Save refresh token in user metadata
    payload = {
        'app_metadata': {
            'refresh_token': refresh_token
        }
    }
    headers = {
        'authorization': "Bearer " + auth_management_access_token,
        'content-type': "application/json"
    }
    user_url = "https://{domain}/api/v2/users/{user_id}".format(domain='**domain**.eu.auth0.com', user_id=user_id)

and retrieve it later using a backend call :

def get_user_idp_token(auth0_user_id):
    token_url = "https://{domain}/oauth/token".format(domain='**domain**.eu.auth0.com')

    headers = {'content-type': "application/json"}
    payload = {
        'client_id': AUTH0_MANAGEMENT_CLIENT_ID,
        'client_secret': AUTH0_MANAGEMENT_CLIENT_SECRET,
        'audience': 'https://**domain**.eu.auth0.com/api/v2/',
        'grant_type': 'client_credentials'
    }
    auth_management_infos = requests.post(token_url, json=payload, headers=headers).json()
    auth_management_access_token = auth_management_infos'access_token']

    headers = {'content-type': "application/json",
               'Authorization': 'Bearer {}'.format(auth_management_access_token)}

    users_url = "https://**domain**.eu.auth0.com/api/v2/users/{}".format(auth0_user_id)
    requests_cache.clear()
    response = requests.get(users_url, headers=headers).json()
    email = response'email']
    idp_access_token = response'identities'][0]'access_token']
    idp_refresh_token = response'app_metadata']'refresh_token']

The problem :
The refresh_token (idp_refresh_token) I get is not working when I try to refresh the gdata credentials, if I use another refresh token (“1/xxxTokenxxx”) obtained using a classic google oauth flow, I have no problem getting a new access…

Can you help me ? Is there something particular about the refresh_token I get from auth0 ? Why is it so different than the refresh_token I get from Google directly ? (the one from Google starts with “1/…” and the one from auth0 does not).

My guess is that the refresh_token I’m getting from auth0 is not the one issued by Google. But if that’s true, how do I get the refresh_token from google ? (I already have offline_access defined as a scope and I don’t see any ‘refresh_token’ in the identities array)

Thank you for any help you may provide, I already read multiple times the pages about calling the auth0 management api to get idp tokens and implemented it but it does not work…

1 Like

The root cause of the issue is that with the flow you’re using there are refresh tokens being issued by two entities (Auth0 and Google). You’re then trying to use an Auth0 refresh token with Google which will of course fail.

When you specify offline_access you’re signalling to Auth0 that you want to receive an Auth0 refresh token as part of the response. For all intended purposes your application authenticates with Auth0 and as such receives tokens valid for Auth0. Since the underlying authentication type you’re using is Google the way Auth0 authenticates the user is by delegating that process to Google which also implies that Google has to issue some tokens.

In summary you have:

  • Your client authentication authenticates the user against Auth0 service;
  • Auth0 service (since you selected to use Google) authenticates the user against Google;
  • Google issues tokens (Google valid tokens) to Auth0 service;
  • Auth0 services issues tokens (Auth0 valid tokens) to your client application.

In addition, given you know your users went through Google, Auth0 allows you to also obtain the Google tokens that were issued. The process to get those tokens is a special-purpose one (for reference information see Identity Provider Access Tokens).

When you perform the authorization code grant in your application you exchange the code for tokens, but the tokens you receive are valid within Auth0. However, you’re putting the Auth0 refresh token in app_metadata and then later you read it as if that was the Google (underlying IdP refresh token).

You need to stop storing refresh tokens (Auth0, Google or any other IdP) in app_metadata and instead get the Google refresh token as documented in the link above. More specifically, through a call to the Management API. You should then be able to read the Google refresh token with something similar to response'identities'][0]'refresh_token'] from the response to the Management API request.

Hi @jmangelo
thanks for the detailed and clear explaination, but it seems to me that a piece is still missing here and I can’t figure out how it is supposed to work the next flow…
After Auth0 stored the IDP access_token + refresh_token, I’ll use the Management API to retrieve them, so I’ll be able to call the Google’s /refresh endpoint using the refresh_token and will obtain a new access_token + new refresh_token.
But, at this point, I’ll be forced to store the new tokens on my own database?
Or is there a way to update the old tokens values contained in Auth0 identities[0] with the new values I got from Google?

3 Likes