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

Correlating requests and responses

The printer's callback endpoint is an address on the web. Someone can send a request to it without first selecting Connect. You could also have two tabs open, each connecting a different photo account.

The client needs more context than "a code arrived." It must know which attempt the response belongs to, which browser session started it, and which authorization server it expected to answer.

Keeping a pending transaction

When the printer starts the connection, it creates a record similar to this simplified example:

Pending attempt: demo-attempt-7
Initiating session: the current printer session
Expected issuer: https://auth.photos.example
Redirect URI: https://printer.example/oauth/callback
PKCE verifier: held privately on the backend
Return destination: a validated local photo-book page
Status: pending, with a limited lifetime

The actual state value should be fresh and unpredictable. The readable value here only helps us follow the example.

On return, the printer uses state to locate the pending attempt and verifies its binding to the current session. A matching value in a global list is not enough if the response can be attached to a different person's session.

That session binding is what stops cross-site request forgery, or CSRF. In a CSRF attack, another site causes your browser to send a request you did not intend, and the receiving application treats it as yours. Against the printer, an attacker starts a connection with their own photo account and stops before their browser returns to the printer. They then get your browser to open the callback address with their code, perhaps through a link, or a page you visit that sends your browser there. Without the check, the printer would attach the attacker's photo account to your printer session, and you would be choosing photos for your book from an account the attacker controls. With it, the response carries a state value that belongs to no pending attempt in your session, and the printer stops.

The attempt must still be valid and available for processing. The application should coordinate processing so two callbacks cannot both complete the same pending attempt. Separate records for separate attempts avoid overwriting one tab's verifier with another tab's value.

If the original session has expired, the printer should not guess which local account should receive the connection. It can explain that the attempt expired and let the person start again from an appropriate session.

Checking who answered

An application that connects to more than one authorization server must also know which one answered. Suppose the printer also imports photos from a second service, Pixel Vault, using the same callback address for both, and that Pixel Vault is malicious or has been compromised.

You choose Pixel Vault, so the printer records https://auth.pixelvault.example as the expected issuer and sends your browser there. Pixel Vault immediately forwards your browser to the genuine photo service, with a request that uses the printer's photo service client ID. That ID is public, so Pixel Vault knows it. You see the familiar photo service and approve. The code returns to the printer's callback, and the printer, still expecting Pixel Vault, sends it to Pixel Vault's token endpoint along with its PKCE verifier. Pixel Vault now holds a code for your genuine photo account. Nothing was wrong with the code itself. The printer sent it to the wrong server. This is called authorization server mix-up.

Our photo service supports the iss response parameter, which catches this. The response names https://auth.photos.example, but the issuer saved for this attempt is Pixel Vault. The printer compares the two exactly and stops before sending the code anywhere. It also stops if an expected iss parameter is missing, and it never uses a returned issuer value to select a new token endpoint.

Issuer identification is one mix-up defense. Other deployments may use a different supported design, such as distinct redirect URIs per issuer. Authorization server mix-up compares the choices, and Checking the response issuer covers the validation rules for iss.

Two distinct browser sessions have separate pending attempts. Session A returning with state-B finds attempt B but fails its session binding. Session A with state-A matches, but the backend must also check issuer, expiry and single processing. Readable state labels are fictional.
The response must belong to a pending attempt in the initiating session and name the expected issuer. The printer claims that attempt once, then uses its saved verifier and trusted token endpoint. View full-size illustration (opens in a new tab)

Keeping the protections distinct

In this example, state correlates the browser response with a session-bound transaction, PKCE binds code redemption to the verifier prepared for that transaction, and the issuer check verifies which authorization server answered.

PKCE can also stop the CSRF attack described earlier. The attacker's code is bound to the challenge from the attacker's own attempt, so when the printer redeems it with the verifier saved in your session, the exchange fails. Current guidance lets a client rely on that protection in place of state once it has confirmed that the authorization server enforces PKCE, because the protection depends on the server refusing a verifier that does not match. We retain explicit state in this teaching flow to make transaction handling clear. That does not make state, PKCE, and issuer validation interchangeable. As the Pixel Vault example showed, PKCE does nothing to stop a code being sent to the wrong server.

Finally, keep secrets and arbitrary return URLs out of state. An opaque random reference to server-side context is easier to control. Before returning the user to a page after connection, validate that destination independently so the callback cannot become an open redirect: an address on a trusted site that sends a browser wherever a link tells it to. Attackers use open redirects to make a link to their own page look as if it belongs to the trusted site.

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 2A returned state matches a pending record, but that record belongs to a different browser session. Should the printer continue?

QUESTION 2 OF 2The response names an unexpected issuer. What should the printer do in the design described here?

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