Token Vault is not working as expected

I followed the steps in the documentation. However, after the login process, I am not seeing the tokens stored in the Token Vault. When I execute the API call below, I receive an empty array as a response:

curl --location 'https://REDACTED_TENANT/api/v2/users/REDACTED_USER_ID/federated-connections-tokensets' \
--header 'Authorization: REDACTED_ACCESS_TOKEN'

If I try to execute the access token exchange with token vault with the curl command below:

# The client ID refers to the resource `auth0_client.willianantunes_api_inner_app_onsen`
curl --location 'https://REDACTED_TENANT/oauth/token' \
--header 'Content-Type: application/json' \
--data '{
	"client_id": "CLIENT_ID",
	"client_secret": "REDACTED_CLIENT_SECRET",
	"subject_token": "REDACTED_USER_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-social-connection"
}'

I receive a 401 with the following response:

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

The response for the previous command is expected given the token vault is empty. However, I was expecting the token vault to be populated after the login process. What am I missing?

Below is the Terraform code I used to create the resources in Auth0. It’s worth mentioning that I had to set the Token Vault option manually in the Auth0 Dashboard for the connection google-social-connection.

resource "auth0_resource_server" "willianantunes_api" {
  name                                            = "WA API"
  identifier                                      = "https://willianantunes.com/"
  signing_alg                                     = "RS256"
  skip_consent_for_verifiable_first_party_clients = true
  token_dialect                                   = "access_token"
  consent_policy                                  = null
  allow_offline_access                            = true
  subject_type_authorization {
    client {
      policy = "require_client_grant"
    }
    user {
      # The policy `require_client_grant` does not seem to work 🤔
      # If you use it, you get an error message below even though the client has the right grant:
      # - Client "YOUR_CLIENT_ID" is not authorized to access resource server "https://willianantunes.com/".
      # So we use `allow_all` for now.
      policy = "allow_all"
    }
  }
  token_encryption {
    disable = true
    format  = null
  }
}

resource "auth0_client" "willianantunes_api_inner_app_onsen" {
  # This has been created following the instructions at:
  # https://auth0.com/docs/secure/tokens/token-vault/configure-access-token-exchange-with-token-vault
  name                       = "Onsen"
  app_type                   = "resource_server"
  resource_server_identifier = auth0_resource_server.willianantunes_api.identifier
  grant_types = [
    "urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token"
  ]
}

resource "auth0_connection" "google_social_connection" {
  # I had to set TOKEN VAULT manually in the Auth0 Dashboard
  name     = "google-social-connection"
  strategy = "google-oauth2"
  options {
    client_id     = "REDACTED_CLIENT_ID"
    client_secret = "REDACTED_CLIENT_SECRET"
    allowed_audiences = [
      auth0_resource_server.willianantunes_api.identifier,
    ]
    scopes = [
      "calendar",
      "adsense_management",
      "email",
      "profile"
    ]
    set_user_root_attributes = "on_each_login"
    non_persistent_attrs     = []
    debug                    = true
    disable_cache            = true
  }
}

resource "auth0_connection_clients" "google_social_connection" {
  connection_id = auth0_connection.google_social_connection.id
  enabled_clients = [
    auth0_client.onsen_regular_web_app.id,
    auth0_client.willianantunes_api_inner_app_onsen.id,
  ]
}

resource "auth0_client" "onsen_regular_web_app" {
  name     = "Onsen"
  app_type = "regular_web"
  grant_types = [
    "client_credentials",
    "authorization_code",
    "refresh_token",
    "urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token",
  ]
  # ...
}
3 Likes

I was able to make it work, though I still think something is wrong. What I did:

  • I revoked all accesses granted to the Google Application used in auth0_connection.google_social_connection. You can do that by going to Manage your Google AccountData & privacyData from apps and services you useThird-party apps & services → selecting the app and clicking Delete all connections you have with <YOUR_GAPP_NAME>.
  • Then, I logged in again in my application. During the login process, I granted access to my Google account again.
  • After that, the request to POST /oauth/token for token exchange started working as expected. It returned a body like this:
{
    "access_token": "ya29.REDACTED_ACCESS_TOKEN",
    "scope": "https://www.googleapis.com/auth/adsense https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile openid",
    "expires_in": 3293,
    "issued_token_type": "http://auth0.com/oauth/token-type/federated-connection-access-token",
    "token_type": "Bearer"
}

What I perceived from my tests is:

  • If your user has already granted access to your App and then you activate Token Vault, the tokens are not going to be stored in the Token Vault until the user re-grants access to your App from scratch.
  • Auth0 does not store the refresh token even if you activate Access Type: Offline Access in the connection settings. It doesn’t matter if the user logins after you enable it. Auth0 will only have the access token.

I found a number of issues reporting that Google only returns a refresh token during the first user login or authorization for an application. There is a way to force Google to return a refresh token every time by adding the parameter prompt=consent to the authorization request.

1 Like