JWT Claim Mappings
JWT Authentication can bind one external identity to one caller API key through its jwt_provider and jwt_subject fields. That works for a fixed fleet of agents. It does not scale to 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 matches verified JWT claims, such as a department name or group membership, and resolves the request to an existing caller API key. Everyone the rule admits inherits that key's model access, tool access, and rate limits. In AISIX Cloud, its budget also applies. Usage events remain attributed to the individual identity from the token.
Claims only select a key that an operator already created. No claim value becomes configuration: a token cannot name an upstream, widen a model allowlist, or create a budget. AISIX rejects a token that matches no direct binding or claim mapping.
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 through
jwt_subjectandjwt_provider, the request runs as that key. The direct binding is authoritative for its subject, including when the key is disabled and the request is rejected. Mappings never override it. - Otherwise, AISIX evaluates the enabled mappings whose
jwt_providernames the matched trust provider. Lowerpriorityvalues run first, with ties broken byname. 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, which must all hold:
| Field | Description |
|---|---|
claim | Non-empty claim path. Dots traverse nested objects (for example realm_access.roles). A missing claim never matches. |
op | With exact, the claim must be a string equal to one of values. With 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 | One or more accepted strings. The condition holds when any one matches. |
The trust provider named by jwt_provider must verify the token before AISIX considers any mapping. In AISIX Cloud, the mapped key must continue to exist in the same environment, so deleting a key that a mapping resolves to is refused — repoint or delete the mapping first. In a declarative resources file, an unknown provider or key reference prevents the configuration from loading.
Prerequisites
Before starting, prepare the following:
- A registered OIDC trust provider and model configured as described in JWT Authentication.
- A caller API key to act as the shared policy key. Configure the model access, tool access, and rate limits that admitted identities should inherit. In AISIX Cloud, matching budgets also apply.
- One configuration path:
- AISIX Cloud with an environment, an attached gateway, and an admin token with
writescope. Record the policy key's ID. - An open-source AISIX gateway with a complete
resources.yamlfile and access to the gateway process environment.
- AISIX Cloud with an environment, an attached gateway, and an admin token with
curlandjqfor the AISIX Cloud workflow. The open-source path uses OpenSSL to generate the policy-key value shown below.
Review Trust Provider Requirements
AISIX evaluates trust-provider requirements before claim mappings. Remove or update any bound_claims condition whose decision the mapping should make. For this workflow, remove the JWT Authentication example's department: ai-lab condition because the mapping decides which departments to admit.
Keep required_scopes requirements that admitted identities satisfy. The examples retain ai.access, so mapped tokens must carry that scope. Apply any provider changes as described in Update or Remove a Trust Provider.
Configure Claim Mappings
Configure claim mappings using the management path for your deployment.
AISIX Cloud
Export the AISIX Cloud Admin API connection details, environment ID, and policy-key ID:
# AISIX_CP includes /api and has no trailing slash.
# The local On-Premises quickstart uses http://localhost:8080/api.
export AISIX_CP="YOUR_AISIX_CLOUD_ADMIN_API_URL"
export AISIX_TOKEN="YOUR_ADMIN_TOKEN"
export ENV_ID="YOUR_ENVIRONMENT_ID"
export POLICY_KEY_ID="YOUR_API_KEY_ID"
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 | Non-negative evaluation order among the provider's mappings. Lower values are evaluated first. Defaults to 0. |
match | From 1 to 16 claim conditions, which must all 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. |
The AISIX Cloud Admin API accepts mapping names and provider names up to 120 characters. A mapping contains from 1 to 16 conditions. Each condition has a claim path up to 256 characters and from 1 to 64 accepted values, each up to 256 characters.
Update a mapping with PATCH .../claim_mappings/{claim_mapping_id}. The name is fixed, so delete and recreate the mapping to rename it. Remove a mapping 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.
Open-Source AISIX Gateway
This configuration extends the complete resources file from JWT Authentication. Keep its _format_version, provider-key, and model entries. Set the new policy-key value in the gateway process environment:
export FINANCE_POLICY_KEY="$(openssl rand -hex 32)"
Replace corp-keycloak with this entry; do not add another provider with the same name. It keeps the ai.access scope while moving the department decision to the mapping:
oidc_providers:
- name: corp-keycloak
issuer: https://sso.example.com/realms/agents
audiences: ["aisix-gateway"]
required_scopes: ["ai.access"]
Add finance-policy-key to the existing api_keys list. Add finance-dept to claim_mappings, creating the top-level claim_mappings collection once if it is absent. Preserve existing entries in both collections, and do not create a second copy of either top-level collection key.
In the assembled resources.yaml, resolve.api_key references the policy key by its display_name:
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 name, jwt_provider, priority, match, and enabled fields have the same evaluation behavior as their AISIX Cloud counterparts. The open-source schema validates non-empty names, providers, condition lists, claim paths, and value lists. It does not apply the AISIX Cloud Admin API's maximum lengths or list sizes.
The file loader resolves the reference at load time and rejects unknown provider or key names, an empty condition list, or a mapping whose jwt_provider names no provider in the file. A typo fails the load instead of silently never matching.
Validate and apply the complete file by following Reload a Resources File. If FINANCE_POLICY_KEY is new, use Add New Environment Variables to recreate the gateway with it.
Exporting the variable on the host and sending SIGHUP cannot add it to an already-running container.
To disable the mapping without removing its configuration, set enabled: false and reload the file. To remove the mapping, delete its entry and reload. Before deleting a policy key, remove or repoint every mapping that references it. You can apply those changes in an earlier reload or in the same resources file update. The file loader rejects any configuration in which a mapping references a missing key.
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.
For AISIX Cloud, export the ID of a second policy key, then create the narrower mapping:
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": "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 at priority 50 rather than finance-dept at priority 100. Identities that need different effective access controls should resolve to different policy keys. Multiple mappings can resolve to the same key when they should grant the same access.
For the open-source gateway, Keycloak Integration shows the complete two-rule resources file for the same priority relationship.
Verify Claim Mapping
Obtain a JWT whose verified claims should match the mapping and whose subject has no direct jwt_subject and jwt_provider binding. Then request a model that only the expected policy key allows, or confirm that the expected jwt_claim_mapping appears in AISIX Cloud logs or a configured exporter. This distinguishes a mapping match from access through another key.
Also test the default-deny and priority paths that apply to your rules:
- Send a valid JWT that matches no direct binding or mapping. On an OpenAI-style proxy route, the gateway should return
401witherror.code: jwt_identity_unmapped. - If mappings overlap, use a subject with no direct binding and send a JWT that matches both. Request a model allowed only by the policy key selected by the higher-priority mapping, or confirm the expected
jwt_claim_mappingattribution. This verifies that the lowerpriorityvalue won.
For a reproducible Keycloak setup and verification matrix covering both configuration paths, see Keycloak Integration.
Per-Identity Usage Attribution
Usage events for requests admitted through a mapping include three attribution fields. Configured observability exporters receive the same fields:
| Field | Description |
|---|---|
jwt_subject | The token's identity-claim value, identifying who ran the request even when 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 through jwt_subject and jwt_provider. |
In AISIX Cloud, the fields are also visible in the environment's Logs and included in CSV exports. The Logs page filters by JWT subject, while the jwt_subject and jwt_claim_mapping query parameters on the usage-events API narrow programmatic reads.
The subject is personal data when the identity claim contains names or email addresses. Review where exporters deliver usage data before enabling them alongside claim mappings.
Rejection Behavior
Mapped authentication extends the JWT rejection table. AISIX rejects a verified token before it reaches a provider, MCP server, or vector store when the identity cannot resolve safely.
Each failure below returns HTTP 401. OpenAI-style routes return jwt_identity_unmapped as error.code, while the reason label on aisix_auth_decisions_total identifies the operator-side cause:
reason | Meaning |
|---|---|
jwt_identity_unmapped | The verified identity has no direct key binding, and no enabled mapping for its trust provider matches all claim conditions. |
claim_mapping_target_missing | A mapping matched, but its target caller API key is missing from the active gateway configuration. AISIX Cloud and resources-file validation ordinarily prevent this state. If it occurs, check configuration propagation and the gateway's active resources. |
jwt_binding_ambiguous | More than one caller API key has the same jwt_provider and jwt_subject binding, so the gateway fails closed instead of evaluating mappings. Normal configuration validation prevents this state. |
Next Steps
- Keycloak Integration: a verified end-to-end walkthrough from a Keycloak realm to mapped, attributed requests.
- JWT Authentication: the trust-provider verification every mapping builds on.
- Caller API Keys: the model access, tool access, rate limits, and, in AISIX Cloud, budgets that a policy key carries.
- API Key and Model Rate Limits: shape the shared key's traffic.