Selecting a specific connected account when using Auth0 Token Vault token exchange

Hi everyone :waving_hand:,

We’ve run into a question while using Auth0 Token Vault and were hoping the community could help clarify what’s possible here.

Context

We’re using the Token Vault token exchange flow to exchange an Auth0 access token for a Google access token:

# 1) Token exchange: Auth0 access token -> Google access token (Token Vault)
exchange_payload = {
    "client_id": CUSTOM_CLIENT_ID,
    "client_secret": CUSTOM_CLIENT_SECRET,
    "subject_token": access_token,
    "grant_type": "urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token",
    "subject_token_type": "urn:ietf:params:oauth:token-type:access_token",
    "requested_token_type": "http://auth0.com/oauth/token-type/federated-connection-access-token",
    "connection": "google-oauth2"
    # Optional: "login_hint": "user@gmail.com"
}

exchange_res = requests.post(
    f"https://{AUTH0_DOMAIN}/oauth/token",
    json=exchange_payload
)

We can also successfully query the connected accounts for the current user:

accounts_res = requests.get(
    f"https://{AUTH0_DOMAIN}/me/v1/connected-accounts/accounts",
    headers=headers,
)

accounts_data = accounts_res.json()
accounts = accounts_data.get("accounts", [])

print("\nConnected accounts (parsed):")
if not accounts:
    print("No connected accounts found.")
else:
    for acc in accounts:
        print("-" * 40)
        print(f"ID:         {acc.get('id')}")
        print(f"Connection: {acc.get('connection')}")
        print(f"AccessType: {acc.get('access_type')}")
        print(f"Scopes:     {', '.join(acc.get('scopes', []))}")
        print(f"CreatedAt:  {acc.get('created_at')}")

Example output (simplified):

Connected accounts:
----------------------------------------
ID:         cac_ft8iPJvQHvFKr1ceabtktF
Connection: google-oauth2
AccessType: offline
CreatedAt:  2025-12-04T16:04:45.967Z
----------------------------------------
ID:         cac_tBV7uLn6NTAKgHPX1cw9DP
Connection: google-oauth2
AccessType: offline
CreatedAt:  2026-01-24T14:24:09.969Z

What we tried

We attempted to use the optional login_hint parameter in the token exchange request (with the correct Google account email corresponding to one of the connected accounts).

However, whenever we include login_hint, the exchange fails with the following error:

{
  "error": "federated_connection_refresh_token_not_found",
  "error_description": "Federated connection Refresh Token not found."
}

This happens even though:

  • The connected account exists

  • The account has offline access

  • The email in login_hint matches the linked Google account

Question

When a user has multiple google-oauth2 connected accounts, we’d like to explicitly choose which one is used during the token exchange.

Specifically:

  • Is login_hint expected to work in this scenario, or is it unsupported for Token Vault exchanges?

  • Is there another supported way to select a specific connected account (for example by connected account ID like cac_ft8iPJvQHvFKr1ceabtktF)?

  • Or is the behavior intentionally implicit when multiple accounts exist for the same connection?

Our goal is to reliably obtain a Google access token for a particular linked Google account, rather than letting Auth0 choose implicitly.

Any guidance, confirmation of support, or recommended patterns would be greatly appreciated :folded_hands:

Thanks in advance for your help!

2 Likes

Hi @laszlo.kovacs

Welcome to the Auth0 Community!

Indeed, the new Token Vault feature can be quite confusing, especially if the documentation is not clear enough about the API calls made for the specific exchange.

When making the Access Token exchange with Google, can you try passing in the user’s Google user_id which you want to select instead of their email address? As far as I am concerned, since you are making a request to Google to retrieve the necessary tokens, it required their Google account’s user_id instead of the email address in order to find and retrieve the correct ones. I believe when you are passing their email as the login_hint, it is unable to identify the correct users and provide their tokens.

Let me know if that will provide you with an refresh token for the specific user or if you receive a different error.

Kind Regards,
Nik

Hi Nik,

Thanks a lot for your help!

I tried using the provider account’s user_id to identify a specific account during the token exchange, and that approach does work :+1:
However, this solution isn’t entirely ideal for us, because it assumes that we already know the provider user_id.

In practice, we don’t have access to that value ahead of time. For testing your suggestion, I managed to collect the user_id in a somewhat hacky way, but that’s not something we can rely on in a real implementation.

Ideally, it would help a lot if the connected accounts endpoint returned the provider-specific user_id (for example, the sub claim). If the response from:

https://auth0.com/docs/secure/call-apis-on-users-behalf/token-vault/connected-accounts-for-token-vault#query-connected-accounts

included this identifier in a provider-agnostic way, then we could reliably select the correct connected account and pass that value to the access token exchange endpoint:

https://auth0.com/docs/secure/call-apis-on-users-behalf/token-vault/access-token-exchange-with-token-vault#step-3-backend-api-performs-access-token-exchange

At the moment, even though using user_id works for identification, we can’t use it in a convenient or supported way, because the /me/v1/connected-accounts/accounts endpoint doesn’t expose this information. This makes account selection difficult when multiple accounts are connected for the same provider.

Do you have any recommendations or best practices for handling this scenario?

Thanks again for your help — it’s much appreciated.

Best regards,
László

2 Likes

Hi again @laszlo.kovacs

Sorry for the delayed response as I was OOO.

The Token Vault is quite a new feature and I still do no grasp the concept fully, however, you should be able to query the user’s identities via the /api/v2/users/{user_id} endpoint to match their email with one of the Google identities in order to retrieve their user_id which you will be able to use for the Access Token Exchange flow.

Alternatively, if you reckon that a large amount of your users will have multiple identities under the same connection, you can use a PostLogin Action to set their respective IDP user ID’s as app metadata. An example action script would look like this:

exports.onExecutePostLogin = async (event, api) => {

  const googleIdentities = event.user.identities.filter(
    (identity) => identity.connection === 'google-oauth2'
  );

  if (googleIdentities.length === 0) {
    return;
  }

  const googleAccountMap = {};
  for (const identity of googleIdentities) {
    const email = identity.profileData.email;
    const providerUserId = identity.user_id;

    if (email && providerUserId) {
      googleAccountMap[email] = providerUserId;
    }
  }

  if (JSON.stringify(event.user.app_metadata.google_account_map) !== JSON.stringify(googleAccountMap)) {
    api.user.setAppMetadata('google_account_map', googleAccountMap);
  }
};

This way, your application will be able to access their google identities and the id’s associated with them in order to perform the exchange while it is being protected from the user.

Could you share how exactly are you retrieving the idp_user_id in your implementation and if the proposed solutions are a better alternative?

If you have any other questions, let me know!

Kind Regards,
Nik

Hi again @nik.baleca ,

Thanks for the reply — totally understandable after your OOO.

Regarding how we obtained the Auth0 user_id: that part was entirely manual. We collected it from the Auth0 Dashboard by inspecting the raw JSON view of the account after a successful login.

That said, it feels like we may be approaching the problem from slightly different angles, so I just wanted to clarify our use case a bit better.

What you’re describing seems closer to the account linking mechanism:
https://auth0.com/docs/manage-users/user-accounts/user-account-linking

Account linking works well when multiple identities should be treated as a single logical user. However, Token Vault behaves differently:
https://auth0.com/docs/secure/call-apis-on-users-behalf/token-vault

Token Vault stores tokens for each connected account, and these accounts are not necessarily identities registered in our system. They are simply links to social providers that the user can freely connect and disconnect. In our case, we explicitly do not want to merge them.

The connected accounts we are referring to are the same ones described here, including their cac_... identifiers returned by the API:
https://auth0.com/docs/secure/call-apis-on-users-behalf/token-vault/connected-accounts-for-token-vault#query-connected-accounts-for-a-given-connection

Because of this, a post-login Action that performs account linking isn’t a good fit for us, as it solves a different problem.

Our actual requirement is fairly straightforward: when calling the token exchange endpoint, we’d like to explicitly select which connected account should be used.

By “connected account,” we specifically mean the cac_... ID returned by the /me/v1/connected-accounts/accounts endpoint, as documented above — not the google-oauth2 connection name itself.

Conceptually, something like this (purely theoretical example to illustrate the desired behavior):

exchange_payload = {
    "client_id": CUSTOM_CLIENT_ID,
    "client_secret": CUSTOM_CLIENT_SECRET,
    "subject_token": access_token,
    "grant_type": "urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token",
    "subject_token_type": "urn:ietf:params:oauth:token-type:access_token",
    "requested_token_type": "http://auth0.com/oauth/token-type/federated-connection-access-token",
    # instead of:
    # "connection": "google-oauth2"
    # (this is not a documented field, just illustrating the intent)
    "connected_account_id": "cac_..."
}

exchange_res = requests.post(
    f"https://{AUTH0_DOMAIN}/oauth/token",
    json=exchange_payload
)

If something like this existed — or if there’s a supported alternative with similar semantics — it would fully solve our use case. Ideally, the cac_... connected account ID would act as the selector, rather than relying on the provider-side user_id.

Thanks again for your help and insights :folded_hands:

Best regards,
László

I see, you are indeed correct, I did not consider the fact that the account is not an identity inside Auth0.

I have reached out internally regarding a recommended/best approach on retrieving the idp_user_id for the Token Vault flow. I will come back with an update regarding the situation as soon as possible to provide more clarification.

Kind Regards,
Nik

Hi @nik.baleca ,

Thanks for checking this internally — I appreciate it.

Looking forward to your update on the recommended way to retrieve or select the appropriate connected account in the Token Vault token exchange flow.

Best regards,
László