Skip to content

JWT authentication

HTTP filter that validates JSON Web Tokens against configured providers and JWKS keys.

AttachmentType URL (@type)Per-route overrideBuild feature
HTTP filter chain (http_filters)type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthenticationdisabled onlyNone

What it does

The JWT filter extracts a token from the request, validates its signature against a provider's JWKS keys along with the issuer, audiences and expiry, and rejects failures with 401. Which provider applies is decided by rules matched against the request path; validated claims are attached to the request for later filters (Cedar, the MCP gateway) and can be copied into headers.

Minimal configuration

From arion-runtime-jwt.yaml; the fragment is an entry in http_filters, before the router:

- name: jwt_authn
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication
    providers:
      idp:
        issuer: https://idp.example.com
        audiences: [api.example.com]
        from_headers:
          - name: Authorization
            value_prefix: "Bearer "
        remote_jwks:
          http_uri:
            uri: https://idp.example.com/.well-known/jwks.json
            cluster: idp
            timeout: 5s
          cache_duration: 300s
    rules:
      - match: { prefix: "/" }
        requires: { provider_name: idp }

Fields

FieldTypeRequiredDefaultDescription
providersmapYes—Named providers; fields below.
ruleslistNoemptyOrdered rules. Each has an optional match (prefix, path, safe_regex, path_separated_prefix, plus headers and query_parameters) and requires: {provider_name}. The first matching rule picks the provider.

A request that resolves to no provider is rejected with 401 ("no provider found") — this includes requests matching no rule and rules written without requires. There is no allow_missing in Arion; requires_any, requires_all, allow_missing, allow_missing_or_failed and requirement_map names are unsupported and refused at startup.

Provider fields

FieldTypeRequiredDefaultDescription
issuerstringNo—Expected iss claim.
audienceslistNoemptyAccepted aud values; empty accepts any.
remote_jwks / local_jwksoneofYes—Key source. remote_jwks fetches http_uri (uri, cluster, timeout) and refreshes every cache_duration (default 60s), with an optional core retry_policy. local_jwks reads inline or from a file (inline_string, filename, inline_bytes, environment_variable).
from_headerslistNoemptyToken locations: header name plus value_prefix to strip. There is no implicit default — a provider with no token source rejects every request.
from_params / from_cookieslistNoemptyQuery parameters or cookies to read the token from.
forwardboolNofalseKeep the token on the forwarded request instead of stripping it.
claim_to_headerslistNoemptyCopy claims to headers: {header_name, claim_name}.
payload_in_metadata, header_in_metadatastringNo—Store the decoded payload or header under this dynamic-metadata key. payload_in_metadata is what makes claims visible to Cedar and MCP RBAC.
clock_skew_secondsuint32No0Tolerated clock skew for time-based claims.
clear_route_cacheboolNofalseRecompute the route after the filter runs.
subjects, forward_payload_header, failed_status_in_metadata—No—Accepted at load but not enforced at runtime today.

require_expiration, max_lifetime, jwt_cache_config and normalize_payload_in_metadata are unsupported and refused at startup.

Ordering and interactions

Run JWT after CORS (preflights carry no token) and before anything that consumes identity: RBAC on claim_to_headers output, Cedar policies over claims, per-user rate limits, and MCP tool authorization. Per route the filter supports only the generic FilterConfig override with disabled: true.

Continue

Start typing to search the site.

↑ ↓ to move Enter to open Esc to close