Skip to content

Latest commit

 

History

History
95 lines (72 loc) · 3.13 KB

File metadata and controls

95 lines (72 loc) · 3.13 KB

jwt — Self-contained signed bearer tokens

Verifies JWT bearers (OIDC ID tokens, OAuth 2.0 self-contained access tokens) using a JWKS endpoint. The cheapest identifier on the hot path because verification is a single signature check against an in-memory keyset.

Source: pkg/identity/jwt — registered as jwt.

When to use

  • IdP issues signed JWTs (Keycloak, Auth0, Okta, Azure AD, Cognito).
  • You want stateless verification (no per-request IdP round-trip).
  • The token has a JWKS URI you can reach.

Don't use for opaque tokens — reach for oauth2-introspection instead.

Configuration

identifiers:
  - name: bearer
    type: jwt
    config:
      # REQUIRED. There is no discovery -- jwksUrl must point directly
      # at the IdP's JWKS document.
      jwksUrl:   https://idp.example.com/.well-known/jwks.json

      # Optional. Independent of jwksUrl: if set, pins the `iss` claim
      # (via jwtlib.WithIssuer) rather than fetching anything from it.
      # issuerUrl: https://idp.example.com

      audiences:                  # optional; verify `aud` claim
        - my-api
        - my-api.internal

      header: Authorization       # default
      scheme: Bearer              # default; "" to accept raw token

      # JWKS rotation: the keyset is fetched lazily on first kid miss,
      # but never more often than minRefreshInterval (default 15m). The
      # cache itself never expires — keys disappear when the IdP rotates.
      minRefreshInterval: 15m

Verifies (in order): signature against JWKS → aud (if configured) → exp / nbf with default 0s leeway. Failed checks return module.ErrNoMatch so the next identifier is tried.

Helm wiring

File mode (default chart):

# values.yaml
config:
  inline: |
    identifiers:
      - name: bearer
        type: jwt
        config:
          jwksUrl: https://idp.example.com/.well-known/jwks.json
          audiences: ["my-api"]
    authorizers:
      - { name: gate, type: rbac, config: { rolesFrom: "claim:roles", allow: ["admin"] } }

CRD mode adds nothing extra — the same YAML lives under spec.identifiers of an AuthConfig CR.

Worked example

GET /things HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

After identification: Identity{Subject: "alice", Source: "bearer", Claims: {sub, aud, roles, exp, ...}}. Subsequent authorizers (rbac, cel, opa) read Identity.Claims to decide.

Composition

  • Pair with dpop (dpop.inner.type: jwt) for sender-constrained bearers.
  • Pair with mtls under firstMatch so service-to-service callers without a JWT fall through to mutual TLS.
  • Use jwt-issue on the response side to mint a fresh internal JWT for the upstream — keeps user-facing tokens out of east-west traffic.

References