Skip to main content

Token Exchange Flow

The Token Exchange flow (defined in RFC 8693) enables applications to exchange one type of token for another. This is particularly useful in scenarios like delegation, impersonation, or when you need to convert tokens between different formats or security contexts.

Overview

This flow allows a client to request a new security token by presenting an existing token. Common use cases include:
  • Delegation: Acting on behalf of another user or service
  • Impersonation: Assuming the identity of another entity
  • Token translation: Converting tokens between different formats or contexts
  • Service-to-service communication: Exchanging user tokens for service-specific tokens

Flow Steps

  1. Obtain Initial Token: Acquire a subject token (e.g., from another OAuth flow)
  2. Token Exchange Request: Submit the subject token to the token endpoint with the token exchange grant type
  3. Receive New Token: Obtain the exchanged token with appropriate scope and audience
  4. Access Resources: Use the new token to access protected resources

Implementation

Prerequisites

Before starting, you’ll need:
  • Your application’s client_id and client_secret
  • A valid subject token (the token you want to exchange)
  • The token endpoint: https://login.camall.io/oauth/token
  • Target audience or resource identifier (optional)
  • Requested token type (optional)

Step 1: Obtain a Subject Token

First, acquire a token using another OAuth flow (e.g., Authorization Code, Client Credentials, or Device Code flow). This will be your subject token.
# Example: Get initial token via Client Credentials flow
CLIENT_ID="your_client_id"
CLIENT_SECRET="your_client_secret"

SUBJECT_TOKEN_RESPONSE=$(curl -s -X POST https://login.camall.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=${CLIENT_ID}" \
  -d "client_secret=${CLIENT_SECRET}" \
  -d "scope=openapi")

SUBJECT_TOKEN=$(echo "$SUBJECT_TOKEN_RESPONSE" | jq -r '.access_token')

echo "Subject Token: $SUBJECT_TOKEN"

Step 2: Initiate Token Exchange

Exchange the subject token for a new token with different properties:
curl -X POST https://login.camall.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
  -d "subject_token=${SUBJECT_TOKEN}" \
  -d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
  -d "client_id=${CLIENT_ID}" \
  -d "client_secret=${CLIENT_SECRET}" \
  -d "scope=openapi"
Parameters:
  • grant_type=urn:ietf:params:oauth:grant-type:token-exchange: Specifies token exchange flow
  • subject_token: The token being exchanged (required)
  • subject_token_type: Type of the subject token (required)
    • urn:ietf:params:oauth:token-type:access_token - OAuth 2.0 access token
    • urn:ietf:params:oauth:token-type:refresh_token - OAuth 2.0 refresh token
    • urn:ietf:params:oauth:token-type:id_token - OpenID Connect ID token
    • urn:ietf:params:oauth:token-type:jwt - JSON Web Token
  • client_id: Your application’s client ID
  • client_secret: Your application’s client secret
  • scope: Requested permissions for the new token (optional)
  • resource: URI of the target service (optional)
  • audience: Logical identifier of the target resource (optional)
  • requested_token_type: Type of token being requested (optional)

Step 3: Exchange with Actor Token (Delegation)

For delegation scenarios where one entity acts on behalf of another:
curl -X POST https://login.camall.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
  -d "subject_token=${SUBJECT_TOKEN}" \
  -d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
  -d "actor_token=${ACTOR_TOKEN}" \
  -d "actor_token_type=urn:ietf:params:oauth:token-type:access_token" \
  -d "client_id=${CLIENT_ID}" \
  -d "client_secret=${CLIENT_SECRET}" \
  -d "audience=https://api.camall.io" \
  -d "scope=openapi"
Additional Parameters:
  • actor_token: Token representing the entity acting on behalf of the subject
  • actor_token_type: Type of the actor token
Response:
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openapi"
}

Step 4: Exchange with Specific Audience

Request a token for a specific target service:
curl -X POST https://login.camall.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
  -d "subject_token=${SUBJECT_TOKEN}" \
  -d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
  -d "client_id=${CLIENT_ID}" \
  -d "client_secret=${CLIENT_SECRET}" \
  -d "audience=https://api.camall.io" \
  -d "resource=https://api.camall.io/v1/resource" \
  -d "scope=openapi"
Parameters:
  • audience: Logical name of the target service
  • resource: Physical location of the target resource

Step 5: Use the Exchanged Token

Make authenticated API requests using the exchanged token:
curl -X GET https://api.camall.io/v1/resource \
  -H "Authorization: Bearer EXCHANGED_ACCESS_TOKEN" \
  -H "Content-Type: application/json"

Complete Example Script

Here’s a complete bash script demonstrating the token exchange flow:
#!/bin/bash

# Configuration
CLIENT_ID="your_client_id"
CLIENT_SECRET="your_client_secret"
AUDIENCE="https://api.camall.io"
SCOPE="openapi"

echo "Token Exchange Flow Example"
echo "============================"
echo ""

# Step 1: Obtain initial subject token
echo "Step 1: Obtaining subject token via Client Credentials flow..."
SUBJECT_TOKEN_RESPONSE=$(curl -s -X POST https://login.camall.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=${CLIENT_ID}" \
  -d "client_secret=${CLIENT_SECRET}" \
  -d "scope=${SCOPE}")

SUBJECT_TOKEN=$(echo "$SUBJECT_TOKEN_RESPONSE" | jq -r '.access_token')

if [ "$SUBJECT_TOKEN" = "null" ] || [ -z "$SUBJECT_TOKEN" ]; then
  echo "Error: Failed to obtain subject token"
  echo "$SUBJECT_TOKEN_RESPONSE" | jq '.'
  exit 1
fi

echo "Subject token obtained successfully"
echo "Subject Token (truncated): ${SUBJECT_TOKEN:0:50}..."
echo ""

# Step 2: Exchange the token
echo "Step 2: Exchanging token with new audience and scope..."
EXCHANGE_RESPONSE=$(curl -s -X POST https://login.camall.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
  -d "subject_token=${SUBJECT_TOKEN}" \
  -d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
  -d "client_id=${CLIENT_ID}" \
  -d "client_secret=${CLIENT_SECRET}" \
  -d "audience=${AUDIENCE}" \
  -d "scope=${SCOPE}")

echo "Exchange Response:"
echo "$EXCHANGE_RESPONSE" | jq '.'

# Extract exchanged access token
EXCHANGED_TOKEN=$(echo "$EXCHANGE_RESPONSE" | jq -r '.access_token')

if [ "$EXCHANGED_TOKEN" = "null" ] || [ -z "$EXCHANGED_TOKEN" ]; then
  echo "Error: Token exchange failed"
  exit 1
fi

echo ""
echo "Step 3: Exchanged token obtained successfully"
echo "Exchanged Token (truncated): ${EXCHANGED_TOKEN:0:50}..."
echo ""
echo "You can now use this token to make API requests:"
echo "curl -H \"Authorization: Bearer $EXCHANGED_TOKEN\" https://api.camall.io/v1/resource"

Token Type Reference

Subject Token Types

  • urn:ietf:params:oauth:token-type:access_token - OAuth 2.0 access token
  • urn:ietf:params:oauth:token-type:refresh_token - OAuth 2.0 refresh token
  • urn:ietf:params:oauth:token-type:id_token - OpenID Connect ID token
  • urn:ietf:params:oauth:token-type:saml1 - SAML 1.1 assertion
  • urn:ietf:params:oauth:token-type:saml2 - SAML 2.0 assertion
  • urn:ietf:params:oauth:token-type:jwt - JSON Web Token (JWT)

Requested Token Types

  • urn:ietf:params:oauth:token-type:access_token - OAuth 2.0 access token (default)
  • urn:ietf:params:oauth:token-type:refresh_token - OAuth 2.0 refresh token
  • urn:ietf:params:oauth:token-type:jwt - JSON Web Token (JWT)

Use Cases

Delegation Scenario

A user delegates access to a service to act on their behalf:
# User's token (subject)
USER_TOKEN="user_access_token"

# Service's token (actor)
SERVICE_TOKEN="service_access_token"

# Exchange with actor token
curl -X POST https://login.camall.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
  -d "subject_token=${USER_TOKEN}" \
  -d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
  -d "actor_token=${SERVICE_TOKEN}" \
  -d "actor_token_type=urn:ietf:params:oauth:token-type:access_token" \
  -d "client_id=${CLIENT_ID}" \
  -d "client_secret=${CLIENT_SECRET}"

Service-to-Service Communication

Exchange a user token for a service-specific token:
curl -X POST https://login.camall.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
  -d "subject_token=${USER_TOKEN}" \
  -d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
  -d "client_id=${CLIENT_ID}" \
  -d "client_secret=${CLIENT_SECRET}" \
  -d "audience=https://backend-service.camall.io" \
  -d "resource=https://backend-service.camall.io/api"

Security Best Practices

  1. Validate tokens: Always validate subject and actor tokens before exchange
  2. Limit scope: Request only the minimum necessary permissions
  3. Specify audience: Use the audience parameter to limit token usage to specific services
  4. Short-lived tokens: Exchanged tokens should have limited lifetime
  5. Use HTTPS: All endpoints must use HTTPS in production
  6. Secure credentials: Never expose client secrets in client-side code
  7. Audit exchanges: Log token exchange operations for security monitoring
  8. Validate token types: Ensure subject_token_type matches the actual token format

HTTP Request Examples

Raw HTTP Request - Basic Token Exchange

POST /oauth/token HTTP/1.1
Host: login.camall.io
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:token-exchange&subject_token=SUBJECT_TOKEN&subject_token_type=urn:ietf:params:oauth:token-type:access_token&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&scope=openapi

Raw HTTP Request - Token Exchange with Audience

POST /oauth/token HTTP/1.1
Host: login.camall.io
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:token-exchange&subject_token=SUBJECT_TOKEN&subject_token_type=urn:ietf:params:oauth:token-type:access_token&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&audience=https://api.camall.io&scope=openapi

Raw HTTP Request - Delegation with Actor Token

POST /oauth/token HTTP/1.1
Host: login.camall.io
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:token-exchange&subject_token=SUBJECT_TOKEN&subject_token_type=urn:ietf:params:oauth:token-type:access_token&actor_token=ACTOR_TOKEN&actor_token_type=urn:ietf:params:oauth:token-type:access_token&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&audience=https://api.camall.io

Raw HTTP Request - API Call with Exchanged Token

GET /v1/resource HTTP/1.1
Host: api.camall.io
Authorization: Bearer EXCHANGED_ACCESS_TOKEN
Content-Type: application/json

Error Responses

Common errors you might encounter:

Invalid Subject Token

{
  "error": "invalid_grant",
  "error_description": "The provided subject token is invalid or expired"
}

Unsupported Token Type

{
  "error": "invalid_request",
  "error_description": "The subject_token_type is not supported"
}

Invalid Audience

{
  "error": "invalid_target",
  "error_description": "The requested audience is not valid for this client"
}

Additional Resources