---
topic: security
author: Crashtech Editorial
published: Nov 13, 2026 · read: 4 min
---

OAuth Redirects Need Exact Matching—and Codes Need a Verifier

OAuth security depends on matching registered redirect URIs and binding authorization codes to the client flow that started them.

– –

At an airport, a boarding pass for gate 27 should not be accepted at a side exit because the sign looks close enough. In OAuth, the callback URL is the gate: a loose match can send a valuable authorization response somewhere the user and client did not intend.

OAuth authorization code flows use browser redirects to connect a client to an authorization server and then return a code to the client. That code is exchanged for tokens. If a malicious party can redirect the response to a destination it controls, or inject a code from a different transaction, the login can be compromised.

The callback is a security boundary

RFC 9700, the IETF’s 2025 OAuth security best current practice, says authorization servers must use exact string matching for registered redirect URIs, with a narrow port-number exception for localhost native-app redirects. It also warns clients and authorization servers not to expose open redirectors that forward browsers to arbitrary query-provided destinations. RFC 9700

Exact matching means the registered callback should be compared as a complete URI according to the specification, not accepted because it shares a hostname or begins with a trusted-looking prefix. A callback such as https://app.example/callback.attacker.example is not the same endpoint as https://app.example/callback. Teams should avoid wildcard callback patterns unless a protocol profile explicitly defines a safe constrained form.

The callback page itself deserves care. Authorization responses can contain codes and state values. Avoid loading unnecessary third-party resources on that page, prevent codes from leaking into logs or referrer headers, and validate the response before continuing the application session.

PKCE binds the code to the starter

Proof Key for Code Exchange (PKCE) adds a verifier known to the client that begins the flow. The client derives a challenge from the verifier and sends the challenge in the authorization request. When exchanging the returned code, it sends the verifier. The server checks that the verifier matches the challenge attached to that code. A party that only intercepted the code cannot redeem it without the verifier. RFC 7636

RFC 9700 extends this advice beyond mobile apps: public clients must use PKCE, and confidential clients are recommended to use it as well. It identifies S256 as the method that does not expose the verifier in the authorization request. The verifier and challenge need to be unique to the transaction and securely bound to the client and user agent.

PKCE does not excuse a lax callback allowlist. Nor does it make every login implementation safe by itself. A client must still associate the response with the browser session that initiated it, handle errors correctly, and reject a transaction whose state or issuer does not match. OpenID Connect also defines nonce for binding an ID token to an authentication request. OpenID Connect Core

For related implementation examples, see Crashtech’s guides to GitHub CLI OAuth token refresh and OAuth eligibility for workspace MCP clients. The protocol safeguards remain the same even when the client type changes.

Review the whole flow

When auditing an OAuth login, trace the transaction from start to finish:

  1. How is the redirect URI selected, and is it an exact registered value?
  2. Is an authorization code used instead of issuing tokens directly in the redirect?
  3. Is PKCE enabled with a fresh verifier and S256 challenge?
  4. Does the token endpoint enforce the verifier for a challenged request?
  5. Is the callback bound to the same browser session and expected authorization server?
  6. Can a callback parameter or open redirect send the browser elsewhere?
  7. Are code, state and token values excluded from logs, analytics and referrers?

These are related checks, not interchangeable fixes. Exact redirect matching controls where the authorization response can go. PKCE limits who can redeem a code. Transaction binding helps prevent cross-site request forgery and mix-up errors. Security comes from preserving all those relationships across the complete flow.

A traveler checks a boarding card against the exact airport gate while an attendant verifies the route.
Conceptual illustration of callback matching and code verification; it does not depict a real airport or login product.
Advertisement

Frequently asked questions

Why should OAuth redirect URIs match exactly?

Exact matching reduces the chance that an authorization server sends a code or token to an attacker-controlled destination that only resembles the registered callback. RFC 9700 requires exact string matching for redirect URI comparison, with a limited localhost port exception for native applications.

What does PKCE protect in an OAuth flow?

PKCE binds an authorization code to a secret verifier held by the client that started the flow. The client sends a derived challenge first and later proves possession of the verifier at the token endpoint, so an intercepted code alone cannot be redeemed by another party.

Does PKCE replace OAuth state or OpenID Connect nonce?

RFC 9700 allows clients that have ensured server PKCE support to rely on its CSRF protection, while OpenID Connect nonce also provides protection in its flow. Implementations should follow the relevant profile, bind values to the user agent and transaction, and still use exact redirect matching and safe callback handling.

Sources & further reading

/* Comments */