ADR 0008: Exact FAPI 2.0 Message Signing client profile¶
- Status: accepted
- Date: 2026-08-01
- Package:
litestar-auth[oauth]
Context¶
An OIDC authorization-code client handling financial data must not accept unsigned front-channel parameters, dynamically discovered request-time trust, or JWTs valid under a different protocol schema. The Authorization Server, client registration, signing key and verification keys are deployment-owned.
Decision¶
- Each client instance binds one exact HTTPS issuer, authorization endpoint, PAR endpoint, redirect URI, client ID, asymmetric signing key ID and allowlisted response algorithms. Redirects and request-time discovery are not supported.
- Authorization uses
response_type=code, PKCES256, OIDCnonce,response_mode=jwt, and a signed JAR with audience equal to the exact issuer. PAR contains only the request object and RFC 7523private_key_jwtclient-authentication fields. - Private keys stay behind an async signer. Verification keys come from an issuer-bound resolver; the library never selects an issuer or JWKS URI from unverified claims.
- JARM requires the selected
oauth-authz-resp+jwttype, exact issuer and audience, bounded lifetime,state,jti, and exactly one ofcodeorerror. A verified state is atomically consumed only after the complete response is valid. - ID tokens use a distinct
JWTschema and nonce namespace. Claims belonging to JARM or signed introspection are rejected before nonce consumption. - Token exchange and Resource Server sender-constraint verification remain separate profiles. The caller persists the returned state, nonce and PKCE verifier and supplies them back as exact expected values.
Consequences¶
- AS metadata and client registration are provisioned out of band and reviewed as one issuer profile.
- Replay-store and signer outages fail closed with typed unavailable errors.
- The profile is integration-ready after local negative tests; official FAPI conformance, partner registration and production key operations remain external readiness gates.
Alternatives considered¶
- Generic OAuth/JWT framework: rejected; it broadens accepted message shapes and algorithm policy.
- Front-channel request parameters: rejected; all authorization parameters are integrity-protected inside the PAR request object.
- Consume state before full validation: rejected; a malformed signed message must not burn a legitimate protocol run.
Evidence¶
- FAPI 2.0 Message Signing Final; FAPI 2.0 Security Profile Final; RFC 9101; RFC 9126; OpenID JARM Final.
tests/unit/test_fapi.pycovers the positive flow, negative JWT/schema/time matrix, replay/outage paths and bounded HTTPS transport.