Safeguard
Application Security•

ASVS 5.0 V10.1.2: Bind the OAuth Flow to the Starting Session

ASVS V10.1.2 (Level 2): accept authorization responses only for flows this browser session started. Use unguessable state, nonce and PKCE verifier bound to the session.

Safeguard Security Team
2 min read

ASVS 5.0 V10.1.2 requires the OAuth client to accept values from the authorization server, such as an authorization code or ID token, only when they result from a flow started by the same user agent session and transaction. The client secrets involved (code_verifier, state, nonce) must be unguessable, specific to the transaction and bound to the client and the session. It is a Level 2 requirement.

Why it matters

Without that binding, an attacker can start a flow, get a code for their own account and trick a victim's browser into completing it. The victim ends up logged in as the attacker, or the attacker's code gets attached to the victim's session.

How to implement it

Generate fresh random values per login attempt, store them server side in the session, and check them on return.

import { randomBytes, createHash } from "node:crypto";

const b64url = (b: Buffer) => b.toString("base64url");

app.get("/login", (req, res) => {
  const state = b64url(randomBytes(32));
  const nonce = b64url(randomBytes(32));
  const verifier = b64url(randomBytes(32));
  const challenge = b64url(createHash("sha256").update(verifier).digest());
  req.session.oauth = { state, nonce, verifier };
  res.redirect(AUTH_URL + "?" + new URLSearchParams({
    response_type: "code", client_id: CLIENT_ID, redirect_uri: REDIRECT_URI,
    scope: "openid", state, nonce,
    code_challenge: challenge, code_challenge_method: "S256",
  }));
});

On the callback, compare state with the stored value, send the stored verifier in the token request, check nonce inside the ID token, then delete all three so they cannot be reused.

How to test it

Start a login in one browser and replay the callback URL in another. Expect a rejection. Replay the same callback twice. Expect the second to fail. Change state by one character and expect failure.

Common mistakes

  • A constant or timestamp based state.
  • Storing the verifier in a cookie the attacker can set.
  • Never deleting the values after use.

Quick check

Why store the values server side?

So the callback can be matched to the exact browser session and transaction that began the flow.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.