About
An OAuth 2.0 Token Exchange (RFC 8693) HTTP filter for Envoy Proxy. It intercepts incoming requests and exchanges the bearer token for a new one connecting to an external Security Token Service (STS) before forwarding the request upstream.
Features
- RFC 8693 Compliant: Token exchange implementation with all optional parameters.
- Impersonation & Delegation: Supports both semantics - omit
actor_tokenfor impersonation, include it for delegation. - RFC Error Parsing: Parses and logs error descriptions optionally returned by the STS.
How It Works
The filter intercepts incoming HTTP requests, extracts the Bearer token from the
incoming Authorization header, and sends an HTTP callout to the configured STS endpoint.
The gateway authenticates via HTTP Basic auth using the configured client credentials and
exchanges the incoming token for a new one. On success, the original Authorization header is
replaced with the exchanged token before the request continues upstream.
Configuration
Required fields:
cluster: Envoy cluster name that routes to the STS endpointtoken_exchange_url: URL of the token exchange endpoint (e.g. "sts.example.com/oauth2/token").client_id: Client identifier for HTTP Basic authentication with the STSclient_secret: Client secret for HTTP Basic authentication with the STS
Optional fields:
audience: Logical name of the target service for the exchanged tokenresource: Absolute URI of the target resource (must not contain a fragment)scope: Space-delimited scope list for the requested tokenrequested_token_type: Desired output token type URIactor_token/actor_token_type: For delegation semantics (both required together)subject_token_type: Input token type (defaults tourn:ietf:params:oauth:token-type:access_token)timeout_ms: HTTP callout timeout in milliseconds (defaults to5000)
Metrics
The extension emits the following Envoy counters:
| Metric | Type | Tags | Description |
|---|---|---|---|
token_exchange_requests_total |
Counter | - | Incremented on each token exchange callout initiated |
token_exchange_results_total |
Counter | result (success / rejected / error) |
Incremented after the STS response is processed |
Usage Examples
Token Exchange with Keycloak
Exchange a user's token for one scoped to a target audience using Keycloak as the STS. It uses the impersonation semantics, the exchanged token still identifies the original user but targets the httpbin audience.
# Start Keycloak
curl -o docker-compose.yaml https://raw.githubusercontent.com/tetratelabs/built-on-envoy/refs/heads/main/extensions/composer/token-exchange/demo/docker-compose.yaml
curl -o keycloak-realm.json https://raw.githubusercontent.com/tetratelabs/built-on-envoy/refs/heads/main/extensions/composer/token-exchange/demo/keycloak-realm.json
curl -o setup.sh https://raw.githubusercontent.com/tetratelabs/built-on-envoy/refs/heads/main/extensions/composer/token-exchange/demo/setup.sh
chmod +x ./setup.sh
docker compose up --wait
# Configure Keycloak for token exchange
./setup.sh
# Run the extension
boe run --extension token-exchange \
--config '{
"cluster": "127.0.0.1:8080",
"token_exchange_url": "localhost:8080/realms/demo/protocol/openid-connect/token",
"client_id": "gateway",
"client_secret": "gateway-secret",
"audience": "httpbin"
}' \
--cluster-insecure 127.0.0.1:8080
# Get a token as testuser and send it through Envoy
TOKEN=$(curl -s http://localhost:8080/realms/demo/protocol/openid-connect/token \
-d grant_type=password -d client_id=my-app -d client_secret=my-app-secret \
-d username=testuser -d password=password | jq -r .access_token)
curl -s http://localhost:10000/headers -H "Authorization: Bearer $TOKEN" | jq .
# Or use the automated script
curl -o run.sh https://raw.githubusercontent.com/tetratelabs/built-on-envoy/refs/heads/main/extensions/composer/token-exchange/demo/run.sh
chmod +x ./run.sh
./run.sh