Cerbos has native JWT support. It can ingest a bearer token from the authorization request, verify it against your keysets if you provide them, and expose the verified claims under request.auxData.jwt for use in policy conditions. This removes the need to parse tokens in each caller and copy claims into the request payload.
Cutting JWT parsing from app code reduces boilerplate and avoids subtle bugs around time skew, algorithm selection, and issuer confusion.
Cerbos checks the token signature using a configured key set, validates the expiry and not-before times, and rejects tokens that fail verification. You can load keys from a remote JWKS URL or from local files. The engine exposes claims to policies through request.auxData.jwt.
Using the standard JWT and JWK specifications ensures compatibility with common identity providers.
Why this matters: Credentials remain a leading initial action in breaches. The 2024 Verizon DBIR shows use of stolen credentials as the top initial action at 24 percent. Tight verification of issuer, audience, and signature helps reduce risk.
Add an auxData.jwt block to your Cerbos configuration. You can mix remote and local keysets. Cerbos respects HTTP cache headers from your JWKS endpoint, or you can set a refresh interval. (AuxData block documentation.)
# cerbos.yaml
auxData:
jwt:
cacheSize: 256
keySets:
- id: default
remote:
url: https://idp.example.com/.well-known/keys.jwks
refreshInterval: 1h
- id: backup
local:
file: /etc/cerbos/keys/backup.jwks
When multiple keysets are configured, you can indicate the keyset to use in your request. With only one key set, the id is optional.
Once enabled, claims are available through request.auxData.jwt in CEL expressions as long as the provided token is valid. You can combine them with principal and resource attributes.
# resource policy snippet
rules:
- actions: ["read"]
effect: EFFECT_ALLOW
condition:
match:
all:
of:
- expr: request.auxData.jwt.iss == "https://idp.example.com"
- expr: "payments-api" in request.auxData.jwt.aud
- expr: P.id == request.auxData.jwt.sub
This pattern enforces issuer, audience, and subject consistency without custom code in the application.
The application layer, which calls the Cerbos PDP provides the token in the request:
{
"principal": {
"id": "alice",
"roles": [
"employee"
],
"attr": {
"department": "accounting"
}
},
"resources": [
{
"resource": {
"id": "XX125",
"kind": "leave_request",
"attr": {
"department": "accounting",
}
},
"actions": [
"approve"
]
}
],
"auxData": {
"jwt": {
"token": "xxx.yyy.zzz",
"keySetId": "ks1"
}
}
}
Define several keysets and select the correct one per request. For providers that rotate signing keys, rely on the JWKS endpoint and either set a refresh interval or let Cerbos honor HTTP cache headers. This keeps keys fresh without restarts.
If you are migrating from legacy tokens with odd header fields, keep strict verification in production and only relax checks during controlled tests.
At the edge, your gateway can pass the original Authorization header through to downstream services. A sidecar or nearby Cerbos PDP reads the token, verifies it, and enforces route or resource rules consistently.
What we have seen: Teams standardize on OIDC, yet still duplicate JWT parsing in each service. Centralizing verification and using claims in policies reduces drift and simplifies audits.
aud or a custom claim in addition to the signature and expiry.
Cerbos allows disabling signature verification for migration or testing. Even then, expiry and not before are still evaluated. Keep verification enabled in production.
Verdict: Start with Cerbos native JWT for most cases. Add a PIP only when you need additional enrichment that cannot come from the token.
iss, aud, and sub can be enforced centrally in CEL expressions.Book a free Policy Workshop to discuss your requirements and get your first policy written by the Cerbos team
Join thousands of developers | Features and updates | 1x per month | No spam, just goodies.