Configure Auth0 for agent IAM
Enterprise
Appropriate Vault Enterprise license or HCP Vault Dedicated cluster required.
Configure users, OIDC applications, and Actions in Auth0 to issue tokens with Rich Authorization Request (RAR) claims for Vault Enterprise agentic IAM.
Before you start
You must have an Auth0 tenant with an Auth0 Enterprise license. Vault agentic IAM uses Auth0 Custom Token Exchange to add RAR claims to access tokens, and Custom Token Exchange requires a paid Enterprise license.
You must have sufficient permission for your Auth0 tenant. You must have permission to create resources (applications, APIs, users, and Actions) and to use the Auth0 Management API.
You must have the
oauth2cCLI installed and in your systemPATHto interact with OAuth 2.0 authorization servers.You must have
curlandjqinstalled to call the Auth0 Management API and the Auth0 token endpoint.
Step 1: Create a custom API
Navigate to Applications > APIs.
Click Create API.
In the Name field, enter a name value (for example,
vault-api).In the Identifier field, provide the API address of your Vault cluster. The identifier value eventually becomes the JWT
audclaim.For JSON Web Token (JWT) Profile, select RFC 9068.
Scroll down and click Create.
Click the Settings tab.
Scroll down to RBAC Settings.
Click the toggle to switch on Enable RBAC.
Click the toggle to switch on Add Permissions in the Access Token.
Click Save.
Step 2: Create an Auth0 application
Navigate to Applications > Applications, then click Create Application.
Enter a custom name for the Name field.
Select Regular Web Application for the application type.
Click Create.
Open the Settings page and note the Client ID, Domain, and Client Secret for your application.
Scroll to Application URIs section.
Add the default callback for
oauth2cto the Allowed Callback URLs list,http://localhost:9876/callback, sooauth2ccan receive the authorization code when you sign in.Click Advanced Settings.
Click OAuth.
In the Allowed APPs/APIs field, enter your Vault API address (for example,
https://<vault-addr>:8200/v1).Open the Grant Types tab and check Authorization Code.
Open the Endpoints tab and record both the OAuth Authorization URL and the JSON Web Key Set URL values for use later.
Click Save.
Step 3: Grant application access
With the API and application both created, you need to grant client access from the API to the application.
Navigate to Auth0 Dashboard > Applications > APIs.
Select the API you created.
Click Application Access.
Find your application, and click Edit.
Click Grant Access.
Click the Always grant all permissions checkbox.
Click Save.
Step 4: Create an agent user
Navigate to User Management > Users.
Click Create User.
Select Create via UI or Create via JSON.
Enter the appropriate information for Email and Password.
Click Create.
On the user page, click Edit next to Email, and mark the email as verified.
Step 5: Obtain a Management API token
The Auth0 Management API lets you manage your tenant programmatically. You need a Management API access token to enable Custom Token Exchange and to create a token exchange profile.
Navigate to Applications > APIs and click Auth0 Management API.
Open the Advanced tab.
Click Create and Authorize Test Application. Auth0 creates an application that can connect to your tenant through the Management API.
Open the API Explorer tab and copy the token.
Save the application domain and the Management API token as environment variables:
$ export \ APP_DOMAIN="<your_tenant>.<region>.auth0.com" \ MGMT_TOKEN="<management_api_token>"
The Management API token expires after one day (86400 seconds). Repeat this step to get a new token if you continue configuring your tenant after the token expires.
Step 6: Enable Custom Token Exchange for the application
Save the client ID of the application you created as an environment variable:
$ export APP_ID="<application_client_id>"Use the Management API to allow your application to use Custom Token Exchange profiles:
$ curl --request PATCH \ --url "https://${APP_DOMAIN}/api/v2/clients/${APP_ID}" \ --header "Authorization: Bearer ${MGMT_TOKEN}" \ --header "Content-Type: application/json" \ --data '{ "token_exchange": { "allow_any_profile_of_type": ["custom_authentication"] } }'Confirm that the response includes the
token_exchangesetting and that bothis_first_partyandoidc_conformantaretrue. Token exchange fails unless both values aretrue. For example:{ "name": "HashiCups Vault", "is_first_party": true, "oidc_conformant": true, "token_exchange": { "allow_any_profile_of_type": [ "custom_authentication" ] }, "app_type": "regular_web", ... }
Step 7: Create a Custom Token Exchange Action
Auth0 Actions extend Auth0 at key
points in the authentication lifecycle. The Custom Token Exchange Action
verifies the incoming access token, sets the user for the new token, and passes
the requested authorization_details to the post-login Action you create in
the next step.
Navigate to Actions > Library.
Click Create Action and choose Create Custom Action.
Enter
process_rar_token_ctefor Name.Select Custom Token Exchange as the trigger.
Keep Node 22 as the runtime and click Create.
Replace the Action code with the following code. Replace
<your_tenant>.<region>.auth0.comwith your application domain and<application_client_id>with your application client ID:const { createRemoteJWKSet, jwtVerify } = require('jose'); // Initialize the JWKS set outside the handler to cache public keys // across warm executions const AUTH0_DOMAIN = '<your_tenant>.<region>.auth0.com'; const EXPECTED_ISSUER = `https://${AUTH0_DOMAIN}/`; const JWKS_URL = new URL(`https://${AUTH0_DOMAIN}/.well-known/jwks.json`); const JWKS = createRemoteJWKSet(JWKS_URL); // Applications allowed to call the token exchange const ALLOWED_CLIENT_IDS = new Set([ '<application_client_id>' ]); // Validate the signature, issuer, expiration, and RS256 algorithm async function verifyTenantJwt(token) { const { payload } = await jwtVerify(token, JWKS, { issuer: EXPECTED_ISSUER, algorithms: ['RS256'], clockTolerance: '10s' }); return payload; } exports.onExecuteCustomTokenExchange = async (event, api) => { // 1. Authorize the calling application if (!ALLOWED_CLIENT_IDS.has(event.client.client_id)) { return api.access.deny('invalid_client', 'Client is not authorized for token exchange.'); } // 2. Verify the subject token const subjectToken = event.transaction?.subject_token; if (!subjectToken) { return api.access.rejectInvalidSubjectToken('Missing required subject_token.'); } let subjectPayload; try { subjectPayload = await verifyTenantJwt(subjectToken); } catch (err) { return api.access.rejectInvalidSubjectToken(`Subject token verification failed: ${err.message}`); } if (!subjectPayload.sub) { return api.access.rejectInvalidSubjectToken('Subject token does not contain a "sub" claim.'); } // Set the user for the issued token api.authentication.setUserById(subjectPayload.sub); // 3. Pass the RAR details to the post-login Action const rawRar = event.request.body?.authorization_details; if (rawRar) { let parsedRar = rawRar; if (typeof rawRar === 'string') { try { parsedRar = JSON.parse(rawRar); } catch { return api.access.deny('invalid_request', 'Invalid JSON in authorization_details.'); } } api.user.setAppMetadata('transient_rar_details', parsedRar); } };Click Deploy.
Copy the Action ID from the end of the Action URL in your browser. For example, the Action ID for
https://manage.auth0.com/dashboard/us/<tenant>/actions/library/details/90f84643-f983-43f5-93a8-56c19f7dd7f4is90f84643-f983-43f5-93a8-56c19f7dd7f4.Save the Action ID as an environment variable:
$ export CTE_ACTION_ID="<action_id>"
Step 8: Create a Custom Token Exchange profile
Auth0 runs a Custom Token Exchange Action when a token request uses the
subject_token_type linked to that Action in a
token exchange profile.
Use the Management API to link the
urn:vault:params:oauth:token-type:access_token token type to your Action:
$ curl --request POST \
--url "https://${APP_DOMAIN}/api/v2/token-exchange-profiles" \
--header "Authorization: Bearer ${MGMT_TOKEN}" \
--header "Content-Type: application/json" \
--data '{
"name": "custom_profile",
"subject_token_type": "urn:vault:params:oauth:token-type:access_token",
"action_id": "'"${CTE_ACTION_ID}"'",
"type": "custom_authentication"
}'
Step 9: Add RAR claims with a post-login Action
The post-login Action reads the authorization_details that the Custom Token
Exchange Action stored, and adds them to the access token as a custom claim.
Navigate to Actions > Library.
Click Create Action and choose Create Custom Action.
Enter
inject_rar_claims_post_loginfor Name.Select Login / Post Login as the trigger.
Keep Node 22 as the runtime and click Create.
Replace the Action code with the following code:
exports.onExecutePostLogin = async (event, api) => { const rarDetails = event.user.app_metadata?.transient_rar_details; const rarClaimNamespace = 'https://vault.hashicups.com'; if (rarDetails) { // Add the custom claim under an HTTP/HTTPS namespace const CLAIM_KEY = rarClaimNamespace + '/authorization_details'; api.accessToken.setCustomClaim(CLAIM_KEY, rarDetails); // Remove the transient user metadata api.user.setAppMetadata('transient_rar_details', null); } }; exports.onContinuePostLogin = async (event, api) => { // Safe to leave empty if no external redirect is performed };Click Deploy.
Navigate to Actions > Triggers and choose post-login.
Drag the
inject_rar_claims_post_loginAction from the Add Action pane between the Start and Complete nodes.Click Apply.
Auth0 adds the RAR details to the access token under the
https://vault.hashicups.com/authorization_details claim rather than
authorization_details. When you create the OAuth resource server profile in
Vault, set
authorization_details_claim
to the custom claim name.
Step 10: Capture a baseline token
Export the Auth0 values you recorded, then run oauth2c to confirm the trust
and to capture the iss and sub claims you need for the Vault profile and
entity aliases.
Save the Auth0 configuration values as environment variables. Replace the placeholders with your actual values from the Auth0 application settings:
$ export \ APP_AUTH_URL="https://<your_tenant>.<region>.auth0.com/authorize" \ SECRET_VALUE="<application_client_secret>" \ JWKS_URI="https://<your_tenant>.<region>.auth0.com/.well-known/jwks.json"Save the Vault server address as
AUDIENCE_ADDRso you can easily request the correct audience in the access token:$ export AUDIENCE_ADDR=${VAULT_ADDR}/v1Request an access token using the OAuth 2.0 authorization code flow with PKCE:
$ oauth2c "${APP_AUTH_URL}" \ --pkce \ --client-id "${APP_ID}" \ --client-secret "${SECRET_VALUE}" \ --response-types code \ --response-mode query \ --grant-type authorization_code \ --audience "${AUDIENCE_ADDR}" \ --auth-method client_secret_basic \ --no-browserCopy the URL that
oauth2cprints under Go to the following URL and open it in a new private or incognito browser window. Sign in with the email address and password you set for the agent user, then close the private window after authentication succeeds.After a successful sign-in, the terminal prints a success message along with a JSON object with the decoded access token. For example:
{ "aud": "https://localhost:8200/v1", "client_id": "EBYeLHzuLqSeyUYLY7EC3hqRgvaiS9Dv", "exp": 1782402664, "iat": 1782316264, "iss": "https://hashicups-dev.us.auth0.com/", "jti": "baFC3mowpruhSr1JdDRhbJ", "permissions": [], "sub": "auth0|c0ff33a2dc1f8e9de0dd95d7" }Save the
issandsubvalues to environment variables. Thesubvalue contains a pipe character, so make sure to wrap the string in quotes:$ export \ ISSUER="https://hashicups-dev.us.auth0.com/" \ EXTERNAL_ID='auth0|c0ff33a2dc1f8e9de0dd95d7'Save the encoded
access_tokenvalue from theoauth2cresponse as an environment variable:$ export SUBJECT_TOKEN="<access_token>"
Step 11: Request a token with RAR claims
Exchange the baseline token for a token that carries ephemeral permissions as
RAR claims. The subject_token_type must match the token type in your token
exchange profile.
Request the token from the Auth0 token endpoint:
$ export AGENT_TOKEN=$(curl --silent --request POST \ --url "https://${APP_DOMAIN}/oauth/token" \ --header "content-type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \ --data-urlencode "client_id=${APP_ID}" \ --data-urlencode "client_secret=${SECRET_VALUE}" \ --data-urlencode "audience=${AUDIENCE_ADDR}" \ --data-urlencode "subject_token=${SUBJECT_TOKEN}" \ --data-urlencode "subject_token_type=urn:vault:params:oauth:token-type:access_token" \ --data-urlencode 'authorization_details=[{"type":"vault:path_access","path":"secret-v2/data/shared-secret/foo","capabilities":["create"]}]' \ | jq -r '.access_token')Decode
AGENT_TOKENwith your preferred JWT tool and confirm the token includes the RAR details under the custom claim. For example:{ "aud": "http://127.0.0.1:8200/v1", "client_id": "V6WXP35RRHFXilt8wrAiB4wgLgyuhIyY", "exp": 1787331677, "https://vault.hashicups.com/authorization_details": [ { "capabilities": [ "create" ], "path": "secret-v2/data/shared-secret/foo", "type": "vault:path_access" } ], "iat": 1787245277, "iss": "https://dev-h3k61byz2u2pze7d.us.auth0.com/", "jti": "3s5dRVumBBQzeoEnvbfmLN", "permissions": [], "sub": "auth0|6a86ee5c8ab798838f14110c" }
If the token exchange fails, check Monitoring > Logs in the Auth0
dashboard for tenant events, and the Action Logs for console.log output
from your Actions.
Step 12: Save your provider information
Confirm your subject and actor details from Auth0. To configure agentic IAM, you must have the following information:
| Value | Environment variable | Description |
|---|---|---|
| Authorization URL | APP_AUTH_URL | Authorization URL of the application |
| Domain address | APP_DOMAIN | Domain of the application you registered in Auth0 |
| Auth0 client ID | APP_ID | Client ID of the application you registered in Auth0 |
| Issuer URL | ISSUER | iss URL for your application |
| JWT subject ID | EXTERNAL_ID | JWT claim for the agent user from your application (sub) |
| Key endpoint | JWKS_URI | URL where clients can download public keys (JWKS) from your application |
| Client secret value | SECRET_VALUE | Client secret associated with your application |
| Audience address | AUDIENCE_ADDR | Vault cluster address including the API version, /v1 |
| RAR claim name | none | Custom claim that holds the RAR details (https://vault.hashicups.com/authorization_details) |
If you have not already, we recommend setting the following environment variables to make Vault configuration more convenient:
$ export \
VAULT_ADDR="<your_vault_server_url>" \
APP_AUTH_URL="<app_auth_url>" \
APP_DOMAIN="<app_domain>" \
APP_ID="<app_client_id>" \
ISSUER="<app_iss_url>" \
EXTERNAL_ID="<app_jwt_sub>" \
JWKS_URI="<app_jwks_url>" \
SECRET_VALUE="<app_client_secret>" \
AUDIENCE_ADDR="<aud_address>"
For example:
$ export \
VAULT_ADDR="https://127.0.0.1:8200" \
APP_AUTH_URL="https://abc-123if67e.us.auth0.com/authorize" \
APP_DOMAIN="abc-123if67e.us.auth0.com" \
APP_ID="ABCD...abc" \
ISSUER="https://abc-123if67e.us.auth0.com/" \
EXTERNAL_ID="auth0|c0ff33a2dc1f8e9de0dd95d7" \
JWKS_URI="https://abc-123if67e.us.auth0.com/.well-known/jwks.json" \
SECRET_VALUE="000000...000000" \
AUDIENCE_ADDR="https://127.0.0.1:8200/v1"
Next steps
- Configure Vault to secure agentic workflows
- Configure Vault to secure OBO workflows
- Configure additional identity providers and authentication servers: