Skip to main content
JWT-Bearer (RFC 7523) lets a client or workload prove its identity using a signed JWT instead of a shared secret. theauth-go v2.4 adds two related features:
  1. JWT-Bearer client authentication: substitute a signed JWT for client_secret in any token request.
  2. JWT-Bearer grant: exchange an external JWT (e.g., a Kubernetes ServiceAccount token) for a theauth access token directly, without an interactive authorization code flow.

The headline use case: Kubernetes workload identity

A Kubernetes Pod running in a namespace has a projected ServiceAccount token automatically mounted at /var/run/secrets/kubernetes.io/serviceaccount/token. This token is signed by the cluster’s OIDC issuer. With JWT-Bearer:
No secrets need to be distributed to the Pod. The Kubernetes OIDC issuer serves as the root of trust.

TrustedJWTIssuer and SubjectMapper

Configure one or more trusted external JWT issuers:
SubjectMapper is an interface (Resolve(claims map[string]any) (ULID, error)), not a plain function value. It receives the full claims map and returns the local user ULID the assertion should authenticate as. Return theauth.ErrStorageNotFound to deny without error detail leakage. Return any other non-nil error to surface an invalid_grant error response. Use the built-in theauth.SubMapper{} (parses sub as a ULID directly) or theauth.EmailMapper{Lookup: ...} (looks up by the email claim) when a custom mapper isn’t needed.

JWT-Bearer grant

The AS:
  1. Decodes the assertion JWT (no verification yet).
  2. Looks up the iss claim in TrustedIssuers.
  3. Fetches (or uses the cached) JWKS from JWKSU and verifies the signature.
  4. Calls SubjectMapper to resolve a client_id.
  5. Looks up the resolved client and applies its allowed scopes and resources.
  6. Mints a theauth access token bound to the resolved resource.
The response follows the standard token response shape:

JWT-Bearer client authentication

Instead of (or in addition to) the grant flow, a client can use a JWT to authenticate on any grant type:
The JWT must be signed with the private key corresponding to the public key registered for my-client (via jwks_uri or inline jwks on the client registration). The AS verifies the signature, iss == client_id, aud == AS issuer, exp, iat, nbf, and a one-time jti (replay prevention within the AccessTokenTTL window). This replaces client_secret entirely. No shared secret needs to be distributed or rotated.

Combining with PAR + JAR (FAPI 2.0)

When JWT-Bearer client authentication is combined with PAR and JAR:
  • The client pushes a signed authorization request to /oauth/par (JAR inside PAR).
  • The client authenticates on the token endpoint using a JWT client assertion (JWT-Bearer client auth).
  • PKCE S256 is enforced (default in theauth-go).
This combination meets the FAPI 2.0 Security Profile baseline. See PAR + JAR for the flow diagrams.

Configuration reference

Set AuthorizationServerConfig.JWTBearer to enable; nil disables the feature.

See also

Last modified on October 7, 2026