Token Exchange is currently in preview. The API and behavior may change as we continue to refine the feature.
- Civic Token Exchange - Exchange an existing Civic access token for a new token with different scopes or extended validity
- Federated Token Exchange - Exchange tokens from external identity providers (Google, Auth0, Azure AD, etc.) for Civic access tokens
Use Cases
- Civic Token Exchange
- Federated Token Exchange
Best for: Delegating tokens with reduced permissions
- Scope Reduction: Create tokens with fewer permissions for specific tasks or services
- Agent Delegation: Issue constrained tokens to AI agents or automated systems that cannot exceed their granted permissions
- Least Privilege: Follow security best practices by granting only the minimum required access for each operation
Configuration
Token Exchange can be set up in the Civic Auth Dashboard under Setup > Token Exchange.
Quick Start
Prerequisites
- A Civic Auth application with client credentials (client ID and secret)
- For federated exchange: An external identity provider configured in the Civic Auth Dashboard
Request Parameters
| Parameter | Required | Description |
|---|---|---|
grant_type | Yes | Must be urn:ietf:params:oauth:grant-type:token-exchange |
client_id | Yes | Your Civic Auth client ID |
client_secret | Yes | Your Civic Auth client secret (or use Basic auth) |
subject_token | Yes | The JWT to exchange (Civic or external) |
subject_token_type | Yes | urn:ietf:params:oauth:token-type:access_token or urn:ietf:params:oauth:token-type:id_token |
scope | No | Space-delimited scopes to request |
expires_in | No | Requested token TTL in seconds |
Basic Token Exchange Request
Response
Civic Token Exchange
Exchange an existing Civic access token for a new token with different scopes or expiration. Thesub claim from the original token is preserved in the exchanged token.
Configuration
Enable Civic Token Exchange by toggling it on in the Dashboard under Setup > Token Exchange.Example: TypeScript
Federated Token Exchange
Exchange tokens from external identity providers for Civic access tokens. Unlike Civic token exchange, the external user’ssub is mapped to a new or existing Civic Auth user, and the resulting token contains the Civic user’s sub.
How It Works
Configuration
Add one or more external identity providers in the Dashboard under Setup > Token Exchange > Add Provider. Select a preset (Google, Auth0, Azure AD) or choose Custom, then configure the provider settings:| Setting | Description |
|---|---|
| Issuer URL | The iss claim value from the external provider’s tokens (e.g., https://accounts.google.com) |
| Audience | Expected aud claim value (usually your OAuth client ID at the external provider) |
| JWKS Endpoint | URL to fetch the provider’s public keys for signature verification |
| Static Public Key | Alternative to JWKS: paste the provider’s public key in PEM format |
| Default Scopes | Scopes to grant in the Civic token (space-separated) |
| Max Token TTL | Maximum token validity in seconds (60-86400) |
Example: React with Google OAuth
Using a Google ID token is recommended for federated exchange — it contains identity claims (email, name, picture) that Civic Auth can map to the new user account.
Google ID tokens include claims like
email, name, and picture by default. Civic Auth automatically maps these to the user account during federated exchange, so new users are created with their profile information already populated.Example: Node.js Backend
Provider Configuration Examples
Google
The
audience should be your Google OAuth client ID.Auth0
Auth0
Replace
YOUR_TENANT with your Auth0 tenant name. The audience should match your Auth0 API identifier.Azure AD / Microsoft Entra ID
Azure AD / Microsoft Entra ID
Replace
YOUR_TENANT_ID with your Azure AD tenant ID (GUID).Okta
Okta
Custom Provider
Custom Provider
For providers not listed above, you’ll need to obtain the following information from your provider’s documentation:
- Issuer URL: The value of the
issclaim in tokens issued by the provider - JWKS Endpoint: Usually at
/.well-known/jwks.jsonrelative to the issuer - Token Format: Ensure the provider issues JWTs (access tokens or ID tokens, not opaque tokens)
User Account Linking
When a user performs federated token exchange for the first time, Civic Auth automatically:- Creates a new user account if no existing account matches the external user
- Links to an existing account if a user with the same verified email exists (when email linking is enabled)
Default Claim Mapping
Civic Auth automatically maps the following claims from the external token to the Civic user account:| Claim | Type | Description |
|---|---|---|
sub | string | External user ID. Used as the principal identifier for user identity matching. |
email | string | Lowercased. Only mapped if present in the external token. |
email_verified | boolean | Only mapped if present in the external token. |
name | string | Only mapped if present in the external token. |
picture | string | Only mapped if present in the external token. |
sub claim combined with the provider’s issuer URL creates a stable identity mapping, ensuring:
- The same external user always maps to the same Civic account
- Different external users never collide
- Users can link multiple external providers to a single Civic account
API Reference
Token Exchange Endpoint
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
grant_type | string | Yes | urn:ietf:params:oauth:grant-type:token-exchange |
client_id | string | Yes | Your Civic client ID |
client_secret | string | Yes* | Your Civic client secret |
subject_token | string | Yes | The JWT to exchange |
subject_token_type | string | Yes | urn:ietf:params:oauth:token-type:access_token or urn:ietf:params:oauth:token-type:id_token |
scope | string | No | Space-delimited requested scopes |
expires_in | number | No | Requested token TTL in seconds |
Client secret can alternatively be provided via HTTP Basic authentication header.
Success Response
Error Response
| Error Code | Description |
|---|---|
invalid_request | Missing required parameter |
invalid_client | Client authentication failed |
invalid_grant | Subject token is invalid, expired, or from an unconfigured provider |
unauthorized_client | Client not authorized for token exchange |
unsupported_grant_type | Grant type not supported |
Security Considerations
Token Validation
Civic Auth performs comprehensive validation on all subject tokens:- Signature Verification: Tokens must be signed by the configured provider’s private key
- Issuer Validation: The
issclaim must exactly match the configured issuer URL - Audience Validation: If configured, the
audclaim must match - Expiration Check: Tokens must not be expired
- Algorithm Check: Only configured algorithms are accepted (default: RS256)
Best Practices
Use HTTPS
Always use HTTPS for token exchange requests
Secure Client Secrets
Store client secrets securely; never expose them in client-side code
Short Token TTLs
Request only the token validity you need
Minimal Scopes
Request only the scopes required for your use case
JWKS Caching
Civic Auth caches external provider JWKS for performance and reliability:- Cache TTL: 1 hour
- Automatic refresh on key rotation
- Graceful fallback during provider outages
Troubleshooting
"Token exchange config not found"
"Token exchange config not found"
- Ensure the external provider is configured in the Dashboard
- Verify the issuer URL exactly matches the
issclaim in the token
"Signature verification failed"
"Signature verification failed"
- Check that the JWKS endpoint is correct and accessible
- Verify the token hasn’t been tampered with
- Ensure the provider is using one of the configured algorithms
"Token expired"
"Token expired"
- External tokens have their own expiration; exchange them promptly
- Civic tokens respect both the external token’s remaining validity and the configured max TTL
"Missing user identifier claim"
"Missing user identifier claim"
- Ensure the external token contains a
subclaim with the user’s unique identifier

