Google Refresh token not returning in user profile's identities section

Hi all

This is my use case:

  1. User signs in to my app via social login (google). The app is a SPA, using this code:
import { Auth0Client, User } from "@auth0/auth0-spa-js";

// init auth0 client
this.auth0 = new Auth0Client({
            domain: domain,
            client_id: clientId,
            audience: `https://${domain}/userinfo`,
            cacheLocation: "localstorage",
            useRefreshTokens: true,
            responseType: "id_token",
            scope: "openid profile email offline_access",
            connection: "google-oauth2",
            redirect_uri: `${window.location.origin}/home`,
            accessType: "offline",
            approvalPrompt: "consent",
            access_type: "offline",
            connection_scope:
                "https://www.googleapis.com/auth/calendar",
        });

...

// login which calls auth0 authorize endpoint
        await this.auth0.loginWithRedirect({
            redirect_uri: `${window.location.origin}/setup`,
            accessType: "offline",
            approvalPrompt: "consent",
            access_type: "offline",
            connection_scope:
                "https://www.googleapis.com/auth/calendar",
        });

  1. All scopes required for my app are requested (calendar, profile, email)
  2. Backend job tries to access google calendar API on behalf of user:
    a. Get user profile from Auth0
    b. Get the user’s google access/refresh token from user.identities[0]
    c. Refresh token if expired (using the user.identities[0].refresh_token from above
    d. Call Calendar API

However, when I get the user profile from mgmt API: /api/v2/users, I do not get the google refresh token, though documentation says otherwise. Only the access_token (which is short lived) is returned

How can I get Google refresh token returned from Auth0 user profile if my app is an SPA?

I have since changed the code to use PKCE flow, still does not work:

        this.auth0 = new Auth0Client({
            domain: domain,
            client_id: clientId,
            audience: `https://${domain}/userinfo`,
            cacheLocation: "localstorage",
            useRefreshTokens: true,
            responseType: "code",
            code_challenge_method: "S256",
            scope: "openid profile email offline_access",
            connection: "google-oauth2",
            redirect_uri: `${window.location.origin}/home`,
            approvalPrompt: "force",
            access_type: "offline",
            connection_scope: "https://www.googleapis.com/auth/calendar",
        });
// ... 

        const challenge = await this.getAuthChallenge();
        console.log({ challenge });
        await this.auth0.loginWithRedirect({
            redirect_uri: `${window.location.origin}`,
            approvalPrompt: "force",
            access_type: "offline",
            connection_scope: "https://www.googleapis.com/auth/calendar",
            code_challenge: challenge,
        });

Though Google does show “offline access” as requested:

image

Adding reply to bump this to the top

I just tested with the following snippets and it seemed to work (using auth0-spa-js 1.22):


auth0 = await createAuth0Client({
  domain: DOMAIN,
  client_id: CLIENT_ID,
  redirect_uri: URL,
  scope: 'openid email profile offline_access',
  audience: 'https://example.com'
})

await auth0.loginWithRedirect({
  access_type: 'offline',
  connection_scope: 'https://www.googleapis.com/auth/calendar',
  connection: 'google-oauth2'
})

The user should have both an access_token and arefresh_token in the identities array.

Also, make sure that the access token you call Management API (GET /api/v2/users) with has the read:user_idp_tokens scope. If not, it will return the identities array without those two attributes.