Skip to content

RFC 9101 — JWT-Secured Authorization Request (JAR)

Spec: datatracker.ietf.org/doc/html/rfc9101Status: Full

RFC 9101 (formerly OpenID Connect Core §6) lets a client pack the authorization request into a signed JWT — the Request Object — instead of sending parameters as plain query strings. This protects the request from tampering in transit and lets a client be authenticated at /authorize rather than only at /token.

Two delivery modes are supported:

  • request=<JWT> — the JWT is sent inline as a parameter (§6.1).
  • request_uri=<URL> — the JWT is hosted at a URL the OP fetches (§6.2).

WARNING

Earlier AuthHero versions decoded the request JWT without verifying its signature. Any caller could forge claims and have them merged into the authorization request. This page describes the post-fix behavior; that hole is now closed.

How verification works

request JWTs are signature-verified against the client's keys before any claim is applied to the authorization request. request_uri URLs are first fetched (with the SSRF guard described below) and the body runs through the same verifier.

Supported algorithms

FamilyAlgorithmsVerified against
AsymmetricRS256, RS384, RS512, ES256, ES384, ES512Client's registration_metadata.jwks (inline) or client_metadata.jwks_uri (fetched)
SymmetricHS256, HS384, HS512Client's stored client_secret

alg=none is rejected.

Claim validation

The verifier enforces:

  • iss — must equal client_id when present.
  • aud — must match the OP issuer.
  • exp — validated with 30 s leeway when present.
  • nbf — validated with 30 s leeway when present.

Sending both request and request_uri is rejected with HTTP 400 per the spec.

SSRF guard for request_uri

request_uri is a server-side fetch that the client controls, so it's exposed to SSRF. AuthHero applies these defaults:

  • https:// onlyhttp:// URLs are rejected.
  • No localhost / 127.0.0.0/8 / link-local / private IPv4 ranges.
  • No private IPv6 ranges (loopback, link-local, ULA).
  • 5-second timeout on the fetch.
  • 64 KiB body cap — larger bodies are truncated and rejected.

For local development and tests, set ALLOW_PRIVATE_OUTBOUND_FETCH: true on the env binding to relax this guard (allows http://, localhost, and private IPs).

Discovery

/.well-known/openid-configuration advertises:

json
{
  "request_parameter_supported": true,
  "request_uri_parameter_supported": true,
  "request_object_signing_alg_values_supported": [
    "RS256", "RS384", "RS512",
    "ES256", "ES384", "ES512",
    "HS256", "HS384", "HS512"
  ]
}

Partial / not yet implemented

  • request_uri pre-registration — the spec allows an OP to require clients to pre-register their request_uri values; AuthHero currently accepts any URL that passes the SSRF guard.
  • Encrypted request objects (JWE) — only signed JWTs are accepted.
  • require_signed_request_object per-client flag — there's no client-level setting yet to require request objects on every authorize call.

Released under the MIT License.