Map identities to Virtual Keys
JWT authentication requires a LiteLLM Enterprise license. Start a free 30-day trial or book a demo. See what Enterprise includes.
Let a client authenticate with an identity-provider JWT in place of a static LiteLLM credential. Map a stable JWT claim to an existing virtual key, then send the JWT as the bearer token.
How it works
LiteLLM validates the JWT using your configured signing keys, issuer, and audience, then looks up the configured claim. A matching mapping identifies the virtual key used for authorization and usage tracking. Configure that key's permissions through Access Management.
If no mapping exists, unregistered_jwt_client_behavior determines whether to reject the request, use standard JWT authorization, create a key, or link an existing one.
Prerequisites
Complete identity provider setup and connect your proxy to a database. Have an existing virtual key ready with the caller's required LLM, MCP, or agent access. See Virtual Keys for key creation and settings.
The test request below uses a configured model named my-model that the key can access.
Choose a stable claim that identifies the caller. Use sub for a person or a provider's application identifier for a workload. A shared application client_id does not distinguish individual users.
Mapping administration requires a proxy admin or admin viewer, depending on the operation.
Setup
1. Configure the mapping claim
Add these settings to your existing proxy configuration. This example requires an administrator to register each mapping.
general_settings:
master_key: os.environ/LITELLM_MASTER_KEY
database_url: os.environ/DATABASE_URL
enable_jwt_auth: true
litellm_jwtauth:
user_id_jwt_field: sub
virtual_key_claim_field: sub
unregistered_jwt_client_behavior: reject
2. Register a mapping
Register the existing key against the caller's claim value. The claim name must match virtual_key_claim_field, and the value must match the caller's JWT.
curl -X POST 'http://localhost:4000/jwt/key/mapping/new' \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H 'Content-Type: application/json' \
-d '{
"jwt_claim_name": "sub",
"jwt_claim_value": "user-123",
"key": "<existing-virtual-key>",
"description": "user-123"
}'
Creating a mapping does not create or change the key.
Map with the key's hash instead of the plaintext key
Since v1.104.0If you no longer have the plaintext key, pass its SHA-256 hash as token. Use the token returned by /key/generate, or the key hash returned by /key/list. Send exactly one of key or token; sending a hash as key hashes it again and returns The provided key does not match an existing virtual key.
curl -X POST 'http://localhost:4000/jwt/key/mapping/new' \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H 'Content-Type: application/json' \
-d '{
"jwt_claim_name": "sub",
"jwt_claim_value": "user-123",
"token": "<64-character lowercase hex key hash>",
"description": "user-123"
}'
Test authentication
Obtain a JWT from your identity provider with sub: user-123, and send it as the bearer token.
export JWT_TOKEN="<identity-provider-jwt>"
curl 'http://localhost:4000/v1/chat/completions' \
-H "Authorization: Bearer $JWT_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"model": "my-model",
"messages": [{"role": "user", "content": "Hello"}]
}'
The request uses the linked key's existing permissions. View its usage in the dashboard or through /key/info.
Configuration reference
Mapping claim and cache
virtual_key_claim_field supports nested claims such as user.sub. Its previous name, jwt_client_id_field, remains an alias. virtual_key_mapping_cache_ttl sets the mapping cache lifetime in seconds and defaults to 300.
unregistered_jwt_client_behavior | When no mapping exists |
|---|---|
fallback_team_mapping | Use standard JWT authorization. This is the default. |
reject | Return 403. |
auto_register | Create a virtual key and mapping after JWT authorization. Optionally try an existing key first. |
link_only | Link an eligible existing user key, or return 403. Never create a key. |
A missing claim also returns 403 in reject, auto_register, and link_only modes.
Create mappings automatically
Set unregistered_jwt_client_behavior: auto_register to create a key and mapping on the first request from an unmapped identity. Configure the user, team, and organization claims you want the new key to inherit using the identity claim settings.
Before creating anything, LiteLLM runs JWT authorization, including configured role and scope checks, custom_validate, and email-domain restrictions. The new key inherits the resolved identity. Use manual mapping when administrators must configure the key before its first request; see Access Management.
Set auto_register_map_existing_key: true to reuse an eligible user key first and create a new one only if none exists. For reuse, virtual_key_claim_field must match user_id_jwt_field or user_email_jwt_field. A database connection is required. Unmapped proxy-admin JWTs authenticate directly without creating a mapping.
Link to existing keys
Since v1.109.0Use link_only when administrators provision keys ahead of time. The first authorized request links the identity to its newest eligible key. If no key qualifies, LiteLLM returns 403 with JWT Key Mapping: No eligible existing virtual key for this identity. Access denied.
general_settings:
master_key: os.environ/LITELLM_MASTER_KEY
database_url: os.environ/DATABASE_URL
enable_jwt_auth: true
litellm_jwtauth:
user_id_jwt_field: sub
virtual_key_claim_field: sub
user_id_upsert: false
unregistered_jwt_client_behavior: link_only
The mapping claim must match user_id_jwt_field or user_email_jwt_field, and the database must be connected. This mode never creates keys. user_id_upsert: true can still create users, so keep it false if users must already exist. Unmapped proxy-admin JWTs authenticate directly without linking a key.
Which existing key is selected
For link_only and auto_register_map_existing_key, the key must belong to the resolved user and team, or have no team when the JWT resolves none. It must never expire, must not be blocked, and must not be an Admin UI session key or an auto-registered key. The newest eligible key wins. Later requests keep that mapping even if a newer key is created.
allowed_routes does not affect selection. Review the existing key's permissions before enabling automatic linking, including any management-route permissions it grants.
Multiple identity providers
Configure issuer-bound validation with litellm_jwtauth.issuers in the per-issuer configuration, then set jwt_issuer on each mapping to the corresponding configured issuer. Issuer-specific virtual_key_claim_field and unregistered_jwt_client_behavior settings can override the global values.
{
"jwt_issuer": "https://idp.example.com",
"jwt_claim_name": "sub",
"jwt_claim_value": "user-123",
"key": "<existing-virtual-key>"
}
LiteLLM first looks for an issuer-scoped mapping, then falls back to a global mapping without jwt_issuer. Global mappings share the same claim namespace across providers. Use issuer-scoped mappings to distinguish identical claim values from different providers.
Manage mappings
Mapping responses include an id, claim name and value, issuer, description, active flag, and audit metadata. They never return the linked key or its hash. Manage the key itself through Virtual Keys.
| Operation | Endpoint | Input |
|---|---|---|
| List | GET /jwt/key/mapping/list | page and size query parameters |
| Inspect | GET /jwt/key/mapping/info | id query parameter |
| Update | POST /jwt/key/mapping/update | JSON id plus key or token, description, is_active, or jwt_issuer |
| Delete | POST /jwt/key/mapping/delete | JSON id |
For example, list mappings to find an ID, then point that mapping to a replacement key.
curl 'http://localhost:4000/jwt/key/mapping/list?page=1&size=50' \
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
curl -X POST 'http://localhost:4000/jwt/key/mapping/update' \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H 'Content-Type: application/json' \
-d '{"id": "<mapping-id>", "key": "<replacement-key>"}'
See mapping-management permissions for the roles allowed to perform each operation.
Troubleshooting
| Code | Meaning |
|---|---|
| 400 | Invalid key identifier or key not found. |
| 403 | Unmapped identity denied by policy, no eligible existing key, or insufficient mapping-management permissions. |
| 404 | Mapping not found. |
| 409 | A mapping already exists for that issuer, claim name, and claim value. |
For signature, issuer, audience, or signing-key errors, see JWT troubleshooting. If a mapped credential is accepted but its resource request is denied, check the linked key's permissions in Access Management.
Related guides
Use identity provider setup to configure token verification, or map identities to users and teams to resolve the caller directly. See Virtual Keys to manage the linked credential and JWT permissions for mapping-management roles and policy behavior.
For Claude Code client configuration, see Use an identity-provider token.