← Payment Reference GuidesAPI Security Reference

FAPI: the Financial-grade API Security Profiles

FAPI is the OpenID Foundation's hardened profile of OAuth 2.0 and OpenID Connect for APIs where a stolen token means a stolen payment. This guide covers FAPI 1.0 Baseline and Advanced, the FAPI 2.0 Security Profile and its attacker model, Message Signing and CIBA — what each one requires, the authorization-code flow message by message, which open-banking ecosystems mandate which profile, and a checklist for shipping it.

Shipping FAPI 2.0: a checklist

If you build the client (TPP, fintech, agent)

  • Discover endpoints from /.well-known/openid-configuration only; pin the issuer URL in config and assert issuer equals it.
  • Keep a signing key pair in an HSM or KMS; publish it via your jwks_uri; rotate by adding the new kid before removing the old.
  • PAR first, always; generate a new PKCE verifier per flow and store it in the user's server-side session, never in a cookie the browser can read.
  • On the callback: verify iss, match state to the session, then exchange the code within seconds.
  • For DPoP: one key per client instance, implement the use_dpop_nonce retry (401 / 400 with DPoP-Nonce), include ath on resource calls.
  • For mTLS: honour mtls_endpoint_aliases; expect a different hostname for the token and resource endpoints.
  • Store refresh tokens as long-lived credentials; be ready for rotation even though FAPI forbids it in normal operation.
  • Send a fresh x-fapi-interaction-id (UUID) on every API call and log it with the response's echo.

If you run the authorization server (bank, IdP)

  • Start from a certified product or library; configure, do not fork.
  • Disable everything FAPI forbids: implicit and hybrid flows, password grant, public clients, client_secret_* authentication, RS256/HS256, bearer-only tokens, 307 redirects.
  • PAR required, authenticated, request_uri single-use at authorization time, expiry under 10 minutes.
  • Codes: ≤ 60 s, single use, replay detection that revokes the whole grant on reuse.
  • Clock skew: accept iat/nbf up to 10 s ahead (30 s is common), reject beyond 60 s. Reject aud arrays in client assertions.
  • Emit iss on every authorization response, including error responses.
  • Support dpop_jkt at PAR and the server-nonce mechanism; publish dpop_signing_alg_values_supported.
  • Consent UI shows the client's registered name and the authorization_details in human terms — the regulator reads this screen as the SCA "what you see is what you sign".

If you run the resource server (the API)

  • Reject tokens in query strings; accept the Bearer scheme only for mTLS-bound tokens and DPoP for DPoP-bound ones — never a DPoP-bound token presented as Bearer.
  • Verify the binding on every call: certificate thumbprint vs cnf.x5t#S256, or DPoP proof signature, htm, htu, iat, ath, jti vs cnf.jkt.
  • Check scope / authorization_details against the operation, not just token validity.
  • Introspect or validate locally with revocation awareness; prefer short access-token lifetimes (minutes) with refresh tokens for long grants.
  • For payment endpoints consider idempotency keys plus signed requests, so a replayed proof cannot create a second payment.

Mistakes the conformance suite catches most often

  • Client never checks iss; server never sends it.
  • PKCE verifier reused, or plain accepted.
  • Authorization request accepted directly, without PAR.
  • Access token accepted without its binding (a plain Bearer presentation works).
  • Refresh-token rotation left on by default.
  • JWTs with iat 2 s in the future rejected — clock-skew handling missing.
  • Client assertion aud set to the token endpoint URL (FAPI 1.0 habit) or sent as an array.
  • Duplicate kid in the JWKS, or alg missing from keys.
  • CORS enabled on the authorization endpoint; HTTP 307 used for the redirect.

Migrating an ecosystem from FAPI 1.0 Advanced to 2.0

  1. Inventory the 1.0-only mechanisms in use: hybrid flow and s_hash, ID-token-as-detached-signature, encrypted front-channel ID tokens, request objects with exp/nbf sent directly to the authorization endpoint.
  2. Turn on the 2.0 mechanisms side by side: PAR (already common in 1.0 ecosystems), iss in responses, DPoP as an alternative to mTLS, response_type=code.
  3. Decide the non-repudiation story: keep JAR and JARM via Message Signing where payments need stored proof; drop them for read-only data.
  4. Run a dual-profile period during which the authorization server passes both the FAPI 1.0 Advanced and FAPI 2.0 conformance suites, with per-client profile selection.
  5. Retire 1.0: remove hybrid flow and code id_token, reject non-PAR requests, and re-certify on 2.0 only.
Standards you will quote in design reviews. FAPI 2.0 Security Profile and Attacker Model (OIDF, Final 2025); FAPI 2.0 Message Signing (OIDF, Final 2025); RFC 9700 OAuth 2.0 Security BCP; RFC 9126 PAR; RFC 7636 PKCE; RFC 9207; RFC 8705 mTLS; RFC 9449 DPoP; RFC 9101 JAR; JARM (OIDF); RFC 9701; RFC 9396 RAR; RFC 8725 JWT BCP; RFC 9421 HTTP Message Signatures; OpenID Connect Core 1.0 and CIBA Core 1.0.

Related tools: HTTP Signature Decoder · ASN.1 / DER Decoder · Base64URL · Webhook Signature Verifier · IBAN Validator

Put it into practice

Next steps

Keep reading

Related articles