Beta

Create a tenant

A new tenant starts with its own users, OAuth settings, audit history and logs. You are its first Tenant Admin.

BTL Admin

How FAPI combines OAuth mechanisms

FAPI 2.0 invents almost nothing. Nearly every requirement in it takes a mechanism that an earlier group explained and makes it mandatory, or takes an option and removes it. The clearest way to see the combination is to follow one payment from start to finish.

Suppose Lakeside Bank's ecosystem adopts FAPI 2.0. Much of the Pay by bank flow from Writing authorization details stays as it was: the pushed request, the payment_initiation details, and the printer's client assertion signed with its key printer-2026-10. The most visible change is the token. A bearer token like demo-bank-access-token-3 is no longer allowed, because the profile requires every access token to be sender-constrained. Lakeside chooses DPoP for that and keeps private_key_jwt for client authentication. FAPI 2.0 lets each deployment make those two choices independently: mutual TLS or private_key_jwt to authenticate clients, and mutual TLS or DPoP to bind tokens.

Pay by bank, step by step

All domains, identifiers, and values in these examples are fictional. You choose Pay by bank for your 42.50 EUR photo book and pick Lakeside Bank:

  1. Configuration. The printer reads Lakeside's metadata document from https://auth.lakeside-bank.example/.well-known/oauth-authorization-server, using an issuer it holds in its own trusted configuration, and checks that the document's issuer matches. Every endpoint below comes from that document.
  2. Push. The printer's backend sends the entire authorization request to Lakeside's PAR endpoint: response_type=code, its redirect URI https://printer.example/pay/callback, a PKCE challenge using S256, and the payment details. It authenticates with a client assertion whose aud is Lakeside's issuer identifier, https://auth.lakeside-bank.example, and attaches a DPoP proof so the authorization code will be bound to its DPoP key. Lakeside answers with a request_uri that expires in under ten minutes.
  3. Visit. Your browser goes to Lakeside's authorization endpoint carrying only the client ID and the request_uri. Lakeside refuses any authorization request that did not arrive this way.
  4. Approve. You sign in to Lakeside and approve paying 42.50 EUR to Photo Printer, once.
  5. Return. Lakeside redirects your browser back with a code and an iss parameter. The printer compares iss exactly with the issuer saved for this attempt before doing anything else. The code lasts at most 60 seconds and works only once.
  6. Exchange. The printer redeems the code with another client assertion, its PKCE verifier, and a fresh DPoP proof from the same key. The access token comes back with token_type DPoP, where the earlier example received Bearer.
  7. Pay. The printer calls Lakeside's payment API with the token in the Authorization header and a new proof. The API checks the token and its binding before acting.

The only part of this flow that passes through your browser on the way to Lakeside is short:

https://auth.lakeside-bank.example/authorize
  ?client_id=photo-printer-pay
  &request_uri=urn%3Aietf%3Aparams%3Aoauth%3Arequest_uri%3Ademo-lakeside-request-5

The amount, the payee, the redirect URI, and the PKCE challenge all traveled in the pushed request, directly from the printer's backend to Lakeside over an authenticated connection. Nothing the browser carries can change them.

The printer backend takes Lakeside Bank's endpoints from its metadata document. It pushes the payment authorization request, with a PKCE challenge, private_key_jwt client authentication and a DPoP proof, and receives a request URI. The browser visits the authorization endpoint with only the client ID and request URI, and you approve paying 42.50 EUR once. Lakeside returns a code with its issuer, which the printer checks against the pending attempt. The printer exchanges the code with client authentication, the PKCE verifier and a DPoP proof, receives a DPoP-bound access token, and uses it with a new proof at the payment API. The printer backend takes Lakeside Bank's endpoints from its metadata document. It pushes the payment authorization request, with a PKCE challenge, private_key_jwt client authentication and a DPoP proof, and receives a request URI. The browser visits the authorization endpoint with only the client ID and request URI, and you approve paying 42.50 EUR once. Lakeside returns a code with its issuer, which the printer checks against the pending attempt. The printer exchanges the code with client authentication, the PKCE verifier and a DPoP proof, receives a DPoP-bound access token, and uses it with a new proof at the payment API.
Every message that matters goes directly between the printer's backend and Lakeside, authenticated and bound to the printer's key. The browser carries only a reference and a code.

Where each requirement comes from

Behind those steps are FAPI 2.0 requirements, and each one builds on an earlier lesson or group:

FAPI 2.0 requiresExplained in
Confidential clients only. Public clients are outside the profile.Public and confidential clients
Endpoints from the metadata document, with its issuer checked against a trusted value.Authorization server metadata
Every authorization request pushed with client authentication and a redirect URI. The browser carries only client_id and request_uri, which expires in under 600 seconds.Pushed Authorization Requests (PAR)
PKCE with S256.Proof Key for Code Exchange (PKCE)
response_type=code and nothing else.The authorization request
iss in every authorization response, checked by the client.Authorization response issuer identification
Codes that work once and last no more than 60 seconds.Redirects and authorization codes
Client authentication by mutual TLS or private_key_jwt, with the issuer identifier as the assertion's aud, as a single string.Mutual TLS, and Secrets and signed assertions
Sender-constrained access tokens only, by mutual TLS or DPoP. Servers using DPoP must support binding the code to the DPoP key.Demonstrating Proof of Possession (DPoP), and Certificate-bound access tokens
Access tokens in the Authorization header, never in a URL query.Using access tokens
No password grant, and no open redirectors at the client or the server.Choosing a flow, Redirect URI validation, and OAuth security best practices
Signatures with PS256, ES256, or EdDSA using Ed25519, never none. RSA keys of at least 2048 bits and elliptic curve keys of at least 224 bits.Token formats and validation

Beneath all of it, every party uses TLS 1.2 or later and checks server certificates properly. Where scopes cannot express what the client needs, the profile recommends Rich Authorization Requests, which is why the payment already travels as payment_initiation details rather than a scope.

Where FAPI departs from habit

A few requirements go against what earlier lessons treated as normal, and each has a reason.

state is not the CSRF defense. Correlating requests and responses noted that a client may rely on PKCE instead of state once it knows the server enforces PKCE. Every FAPI server must, so the profile relies on PKCE alone, with the challenge generated for each request and bound to the browser session that started it. A client may still use state to carry its own application state.

Servers do not rotate refresh tokens. The refresh token grant presented rotation as a way to notice theft. FAPI 2.0 prohibits it except in extraordinary circumstances, such as a migration between systems. Its clients are confidential and its tokens are sender-constrained, so a stolen refresh token is already useless without the client's own credentials, and rotation adds only a failure mode: a client that loses the response carrying its new refresh token has no working one left. When a server must rotate, it has to let clients retry with the old refresh token for a limited time, and clients must be able to cope with rotation for those occasions.

Clocks get a stated tolerance. Even a fraction of a second of clock difference can make a JWT look as though it was issued in the future. Authorization servers must accept JWTs whose iat or nbf is up to 10 seconds ahead of their clock, and must reject any more than 60 seconds ahead, so that no one solves clock trouble by switching the check off.

Signed requests and responses are optional. Using JAR with PAR told the request half of this change: FAPI 1.0's advanced profile required signed request objects, and FAPI 2.0 replaced them with pushed requests that never travel through the browser. Protecting an authorization response told the response half: FAPI 1.0 Advanced protected responses with a signature, from JARM or from an ID token, while FAPI 2.0 reduces the response to a code protected by PKCE, iss, and a short lifetime. Ecosystems that also need non-repudiation, a record that the printer itself asked for this exact payment, can add FAPI 2.0 Message Signing, which signs the pushed request with JAR, the response with JARM, and introspection responses as JWTs.

None of this is a new protocol. It is the OAuth of the earlier lessons, with the choices made once and written down. Whether a particular authorization server or client actually makes every one of those choices is a separate question, and only testing can answer it.

Try it in the Lab

PUT IT INTO PRACTICE

Check your understanding

Try these questions before moving on. If an answer isn't right, use the feedback and try again.

0 of 2 answered correctly

Enable JavaScript to answer these questions and save progress in this browser.

QUESTION 1 OF 2Under FAPI 2.0, what does the printer send through your browser to Lakeside's authorization endpoint?

QUESTION 2 OF 2Why does FAPI 2.0 tell authorization servers not to rotate refresh tokens in normal operation?

We value your privacy

We use cookies and similar technologies to enhance your browsing experience, and analytics to understand our traffic. By clicking "Allow All", you consent to optional analytics. Cookie Policy

Learn identity