JWT Claim Mappings
JWT Authentication binds one external identity to one caller API key through the jwt_subject field. That fits a fixed fleet of agents, but not an enterprise identity provider asserting departments, groups, or applications for hundreds of people: registering a key per person duplicates the IdP's directory, and changes there never propagate.
Claim mappings close that gap. A mapping is a rule that matches the verified claims of a JWT — a department name, membership in a group — and resolves the request to an existing caller API key. Everyone the rule admits runs under that key's model and tool access, rate limits, and budget, while each request's usage is still attributed to the individual identity from the token.
Claims only ever select a key an operator already created. No claim value becomes configuration: a token cannot name an upstream, widen a model allowlist, or mint a budget. A token matching no mapping is rejected.
How Mappings Are Evaluated
For each request with a JWT bearer, after the full trust-provider verification described in JWT Authentication:
- If a caller API key binds the token's subject directly (
jwt_subject+jwt_provider), the request runs as that key. The direct binding is authoritative for its subject — including a disabled key, which stays a rejection. Mappings never override it. - Otherwise, the enabled mappings whose
jwt_providernames the matched trust provider are evaluated inpriorityorder — lower values first, ties broken byname— and the first mapping whosematchconditions all hold selects the key. - If no mapping matches, the request is rejected with
jwt_identity_unmapped. There is no default admission.
The match field of a mapping is a list of conditions, all of which must hold:
| Field | Description |
|---|---|
claim | Claim to inspect. Dots traverse nested objects (for example realm_access.roles). A missing claim never matches. |
op | exact — the claim must be a string equal to one of values. contains — the claim must be an array containing one of values among its string items (non-string items are ignored). A claim whose type does not fit the operator never matches. |
values | Accepted values; the condition holds when any one matches. |
The mapped key must exist in the same environment: a mapping whose key was deleted rejects matching tokens instead of admitting them, and the trust provider named by jwt_provider must verify the token before any rule is considered.
Prerequisites
The workflow below extends the JWT Authentication setup: a registered OIDC trust provider, a model, and curl + jq. You also need a caller API key to act as the shared policy key — the carrier of the model access, rate limits, and budget the admitted identities inherit.
Export the base URL, admin token, environment ID, and the policy key's ID:
export AISIX_CP="http://localhost:8080/api"
export AISIX_TOKEN="YOUR_ADMIN_TOKEN"
export ENV_ID="YOUR_ENVIRONMENT_ID"
export POLICY_KEY_ID="YOUR_API_KEY_ID"
Create a Claim Mapping
Admit everyone whose department claim equals finance under the policy key:
curl -sS -X POST "$AISIX_CP/environments/$ENV_ID/claim_mappings" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "finance-dept",
"jwt_provider": "corp-keycloak",
"priority": 100,
"match": [
{"claim": "department", "op": "exact", "values": ["finance"]}
],
"resolve": {"api_key_id": "'"$POLICY_KEY_ID"'"}
}' | jq
The response echoes the created mapping. When some gateways in the environment run a release that predates claim mappings, it also carries a warnings array — those gateways skip the rule entirely and keep rejecting the identities it should admit, so upgrade them before relying on it.
Mapping Fields
| Field | Description |
|---|---|
name | Mapping name, unique within the environment and fixed at creation. Also the evaluation tie-break for equal priorities. |
jwt_provider | Name of the OIDC provider whose tokens this mapping applies to. A mapping never matches a token verified by a different provider, so two providers cannot select each other's keys. |
priority | Evaluation order among the provider's mappings: lower values are evaluated first. Defaults to 0. |
match | Claim conditions, all of which must hold (see the table above). |
resolve.api_key_id | ID of the caller API key matching requests run as. |
enabled | Whether the mapping participates in evaluation. Defaults to true; a disabled mapping is kept but skipped. |
Update a mapping with PATCH .../claim_mappings/{claim_mapping_id} (the name is fixed — delete and recreate to rename) and remove it with DELETE. Changes take effect on new requests without a gateway restart; identities admitted only by a deleted or disabled mapping stop authenticating as soon as the gateway picks up the change.
In the Dashboard, the environment's Claim mappings page manages the same rules and lists them in evaluation order.
Layer Mappings with Priorities
Rules compose by priority. Give a narrower rule a lower value so it wins for the identities it describes, and let a broader rule catch the rest. The example below resolves to a second policy key — export its ID first:
export ADMIN_KEY_ID="YOUR_ADMIN_API_KEY_ID"
curl -sS -X POST "$AISIX_CP/environments/$ENV_ID/claim_mappings" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "platform-admins",
"jwt_provider": "corp-keycloak",
"priority": 50,
"match": [
{"claim": "realm_access.groups", "op": "contains", "values": ["platform-admin"]}
],
"resolve": {"api_key_id": "'"$ADMIN_KEY_ID"'"}
}' | jq
A token carrying both department: finance and the platform-admin group now resolves through platform-admins (priority 50) rather than finance-dept (priority 100). Different teams needing different policies get their own policy keys — one rule each.
Per-Identity Usage Attribution
Every request admitted through a mapping records three attribution fields on its usage event, visible in the environment's Logs, filterable, and included in the CSV export:
| Field | Description |
|---|---|
jwt_subject | The token's identity-claim value — who ran the request, even though many identities share the policy key. |
jwt_provider | The trust provider that verified the token. |
jwt_claim_mapping | The mapping that admitted the request. Empty for identities bound directly via jwt_subject. |
The Logs page filters by JWT subject, and the jwt_subject / jwt_claim_mapping query parameters on the usage-events API narrow programmatic reads the same way. Configured observability exporters receive the same fields — note that the subject is personal data when your identity claim carries names or emails, so review where exporters deliver before enabling them alongside claim mappings.
Open-Source AISIX Gateway Configuration
In a declarative resources.yaml, reference the policy key by its display_name with the resolve.api_key shorthand:
oidc_providers:
- name: corp-keycloak
issuer: https://sso.example.com/realms/agents
audiences: ["aisix-gateway"]
api_keys:
- display_name: finance-policy-key
key_env: FINANCE_POLICY_KEY
allowed_models: ["gpt-4o-prod"]
claim_mappings:
- name: finance-dept
jwt_provider: corp-keycloak
priority: 100
match:
- claim: department
op: exact
values: ["finance"]
resolve:
api_key: finance-policy-key
The file loader resolves the reference at load time and rejects unknown provider or key names, a condition list that is empty, or a mapping whose jwt_provider names no provider in the file — a typo fails the load instead of silently never matching.
Rejection Behavior
Mapped authentication extends the JWT rejection table. A verified token that matches no mapping — or matches one whose key no longer exists — is rejected with jwt_identity_unmapped (HTTP 401) before any provider, MCP server, or vector store is reached. The gateway's aisix_auth_decisions_total metric distinguishes the finer-grained reasons (jwt_identity_unmapped, claim_mapping_target_missing, jwt_binding_ambiguous) for operators.
Next Steps
- JWT Authentication: the trust-provider verification every mapping builds on.
- Caller API Keys: the model and tool access, rate limits, and budgets a policy key carries.
- API Key and Model Rate Limits: shape the shared key's traffic.