Last reviewed: October 30, 2026 (UTC).
Direct answer
Review an agent-written OAuth integration as a chain of bindings, not as a successful login screen. Require the authorization-code flow, use PKCE with a fresh high-entropy verifier for every authorization request, register and compare an exact redirect URI, generate and validate a state value against the user session, and exchange the code only at the intended token endpoint. Validate the returned issuer, audience, scope, and token type before creating an application session. Keep tokens out of URLs, logs, browser storage, and agent transcripts unless the protocol and threat model explicitly require a narrowly controlled location.
The OAuth 2.0 framework defines the authorization request, redirect response, and code-to-token exchange, but it does not choose the policy for your client. PKCE binds the authorization code to the client instance that started the flow. OAuth for native apps adds platform guidance such as using an external user-agent and requiring PKCE for public native clients. The OWASP OAuth2 Cheat Sheet brings these controls together with state, nonce, redirect, and token-storage recommendations.
A patch can pass a happy-path test while remaining unsafe. It may accept any callback URL, reuse one verifier across users, skip state checks, send a code to a client-controlled endpoint, or treat an ID token as an access token. Make each acceptance decision visible in code, configuration, tests, and the review handoff.
Who this is for
Use this guide when a coding agent changes login callbacks, OAuth client libraries, identity-provider configuration, mobile deep links, desktop sign-in, single-page application authentication, account linking, refresh-token handling, or gateway middleware. It applies to new integrations and to refactors that move any part of the flow between a browser, native app, backend, reverse proxy, or identity provider.
This review covers the authorization-code flow. It does not replace provider-specific OpenID Connect validation, access-token introspection, consent policy, or account-linking decisions. When the patch handles an ID token, review its issuer, audience, nonce, signature, and key-discovery policy separately. For browser transport and response-sharing changes, pair this article with Prevent CORS Data Leaks in Coding-Agent API Patches . For long-lived authenticated channels, use Secure WebSocket Channels in Coding-Agent Patches .
Key takeaways
- Prefer authorization code plus PKCE. Do not approve a new implicit-flow implementation merely because it is shorter.
- Generate a fresh verifier and challenge for every authorization attempt, store the verifier only for that attempt, and require the S256 challenge method unless a documented provider constraint says otherwise.
- Register one exact redirect URI per client and environment. Compare the complete scheme, host, port, path, and registered query policy; do not accept a caller-supplied callback URL.
- Generate unpredictable state, bind it to the initiating browser session and expected provider, and consume it once before processing the code.
- Keep the code exchange server-side when the client can keep a secret. Public native clients still need PKCE and must use the platform redirect pattern intended for that app.
- Validate token purpose, issuer, audience, expiry, scope, and key or introspection results before creating a local session or authorizing an API request.
- Keep authorization codes, verifiers, tokens, cookies, and authorization headers out of logs, analytics, URLs, screenshots, and agent transcripts.
- Test the complete browser or native boundary, including callback tampering, state replay, verifier mismatch, provider mix-up, expired codes, and token substitution.
Sources checked
The OAuth 2.0 Authorization Framework defines the roles, authorization-code grant, redirect URI, authorization response, state parameter, and token request. RFC 7636 defines the code verifier and challenge exchange used to stop an intercepted authorization code from being redeemed by another client. RFC 8252 describes native-app redirect choices and requires PKCE for public native clients. OWASP’s OAuth 2.0 Cheat Sheet provides implementation guidance for redirect registration, state, nonce, PKCE, and token handling. RFC 9449 documents DPoP as an optional sender-constrained token mechanism.
These are stable, maintained protocol references fetched on October 30, 2026 UTC. They support an evergreen engineering review; they do not prove that a particular repository, identity provider, or deployment is correctly configured.
Contract details to verify
Write the client contract before reading helper code. Record the client type, provider identity, redirect URI, scopes, token audience, storage boundary, and session outcome. If the agent cannot state those values, the patch is not ready for a security review.
Bind the authorization request to the client
Use the authorization-code grant and create a new transaction record for each attempt. The record should contain a cryptographically random state value, a PKCE verifier, the expected provider, the registered redirect URI, the requested scopes, the initiating session or device identifier, an expiry, and a one-time-use marker. Store it in a server-side session or protected short-lived store. A browser cookie or native-app transaction store may hold a reference, but do not place the verifier or tokens in a URL.
The verifier must be generated locally and kept secret until the token request. Derive the S256 challenge from that verifier and send only the challenge in the authorization request. At the token endpoint, send the original verifier. Reject a response if the verifier is missing, already consumed, expired, or associated with another provider or redirect URI. Never accept a verifier supplied by the callback query or by a page script.
Make redirect handling exact
A redirect URI is a security boundary. Register the complete URI with the provider and keep the value in code-owned configuration for each environment. Compare the callback request against the transaction’s registered value before accepting the code. A path prefix, hostname suffix, wildcard subdomain, or decoded string comparison is not an exact policy. Reject scheme changes, alternate ports, user information, fragments, unexpected query parameters, and parser disagreement.
Do not add a general returnTo or next URL to an OAuth callback. If the application needs post-login navigation, store an opaque route identifier in the transaction and map it to a fixed local path after authentication. The redirect from the provider should land on one controlled callback endpoint; the later local navigation should have its own allowlist. Review this boundary alongside the site’s open-redirect review workflow
.
Validate state and provider identity
The state value prevents an attacker from causing a victim’s browser to complete a transaction that the victim did not initiate. Generate it with a secure random source, bind it to the initiating session, compare it in constant time where the platform supports that operation, and consume it before exchanging the code. A missing, mismatched, expired, or repeated state must stop the flow without creating a session or linking an account.
Bind state to the provider and client configuration as well. A valid response from one configured provider must not be accepted by another callback path merely because both use OAuth-shaped parameters. Keep the provider identifier, authorization endpoint, token endpoint, client identifier, redirect URI, and expected issuer together in a reviewed configuration object. Do not infer the provider from an unverified iss, host, or query parameter.
If the flow also uses OpenID Connect, generate and validate a nonce in the identity token and apply the provider’s signature, issuer, audience, time, and key-rotation rules. An ID token proves an identity assertion under its profile; it is not automatically an API access token.
Keep the code exchange and session boundary narrow
Exchange the code only at the configured token endpoint over authenticated TLS. Use the client authentication method required by the provider. Public clients must not pretend that a bundled secret is confidential. On the response, validate the token type, issuer or introspection result, audience, scope, expiry, and any provider-specific claims before granting local permissions.
Store refresh and access tokens in the narrowest protected location the platform supports. For a server-backed web session, keep provider tokens server-side and issue a short-lived, secure, HttpOnly, SameSite session cookie. For a native app, use the operating system’s protected credential store and follow the provider’s logout and revocation behavior. Do not put bearer tokens in query strings, fragment links copied into analytics, local storage without a documented threat model, or routine logs.
The app’s local session must have its own expiry, rotation, revocation, and account-linking policy. Do not use an external subject identifier as an authorization decision until the issuer and audience have been validated and the account has been mapped under the intended tenant or provider. If a stolen access token needs stronger replay resistance, evaluate a sender-constrained design such as DPoP ; treat it as an explicit protocol decision rather than a hidden library option.
Use the correct native-app redirect pattern
For mobile and desktop clients, open authorization in the system external user-agent rather than an embedded web view unless the platform and provider have a reviewed exception. RFC 8252 describes claimed HTTPS links, app-claimed universal links, and loopback redirects. A custom scheme can be intercepted by another application, so it needs the platform controls and registration model appropriate to that risk.
PKCE remains necessary for a public native client because the app cannot protect a static client secret. Bind the verifier to the exact authorization attempt and clear it after success or failure. Test a second app or process attempting to claim the redirect, a user cancelling in the external browser, and a callback arriving after the transaction expired.
Build an adversarial test matrix
Use a disposable provider tenant, synthetic accounts, controlled origins, and a recording callback server. Assert both the server decision and the absence of session or account side effects.
| Fixture | Required observation |
|---|---|
| Valid code, matching state, matching S256 verifier | One token exchange and one local session |
| Code with missing or altered state | Rejected before token exchange; no session |
| Code with a verifier from another transaction | Token endpoint rejects or local client stops; no session |
| Callback with an unregistered host, port, path, or scheme | Rejected before code redemption |
| Callback with an extra query, fragment, encoded delimiter, or user-info component | Rejected under exact redirect policy |
| Code issued by provider A sent to provider B callback | Provider or issuer binding rejects it |
| Replayed state or already-used code | No second exchange or account mutation |
| Expired transaction or expired code | Controlled failure and cleanup |
| Valid ID token with wrong audience or issuer | No authenticated principal |
| Access token with insufficient scope | Request is denied without privilege escalation |
| Native callback claimed by an untrusted local app | The app does not accept an unbound transaction |
| Provider error or user cancellation | No partial session, token log, or account link |
| Stolen-token replay in a sender-constrained profile | DPoP or equivalent proof check rejects the wrong sender |
Run the matrix through the real proxy, callback route, browser, or native external-user-agent path. A unit test that calls a token parser directly does not prove that middleware, redirects, cookies, caches, or analytics preserve the policy. Keep raw codes, verifiers, tokens, and cookies out of test output.
Happy-path operator workflow
- Freeze the agent revision and inventory every authorization endpoint, callback route, token client, session writer, and logout path.
- Record provider, client type, exact redirect URI, scopes, audience, PKCE method, state storage, token storage, and local-session policy.
- Trace every value from configuration or request input to the authorization URL, callback, token request, and session creation.
- Replace caller-selected callback and post-login URLs with code-owned values or opaque local route identifiers.
- Add a fresh transaction record for each attempt and enforce one-time state, verifier, code, and session transitions.
- Validate token purpose, issuer, audience, scope, expiry, and key or introspection results before creating a principal.
- Run the positive and negative matrix through the externally reachable flow, including the provider’s error and cancellation responses.
- Inspect logs, tracing, analytics, browser history, and agent transcripts for codes, verifiers, tokens, cookies, and authorization headers.
- Retain the provider configuration revision, test fixture IDs, callback decisions, token-validation result classes, and cleanup outcome.
- Ask the human reviewer to approve the trust-boundary decisions separately from the agent’s claim that the login screen works.
Error-path operator workflow
Treat state mismatch, verifier mismatch, redirect mismatch, provider mix-up, token validation failure, and missing configuration as controlled authentication failures. Clear the transaction, create no local session, avoid account-linking side effects, and return a fixed local error page or documented error code. Do not reflect the rejected callback values into HTML, headers, logs, or telemetry.
If a legitimate client fails, separate the causes: provider cancellation, callback routing, expired state, missing verifier, wrong client configuration, token audience mismatch, cookie policy, or local session creation. Repair the named contract and rerun the entire matrix. Do not solve a redirect problem by accepting arbitrary URLs, solve a PKCE problem by disabling verification, or solve a state problem by making the value optional.
If the provider is unavailable after the user has authenticated, preserve no half-created session. If the token exchange succeeds but local account mapping fails, revoke or securely discard the provider token according to the provider contract and record only a sanitized failure class.
Failure modes
The patch uses an implicit flow because it is shorter. New work should use authorization code plus PKCE and a reviewed client profile.
The callback accepts any redirect_uri. The caller must not choose where authorization responses land. Use a registered, exact value.
The verifier is stored in the callback URL. URLs leak through history, referrers, logs, and screenshots. Keep it in the transaction store.
State is generated but never checked. A random value has no protection until it is bound to the initiating transaction and consumed once.
A valid signature is treated as the right token. Validate issuer, audience, purpose, scope, expiry, and client binding before authorizing an API call.
A bundled client secret is trusted in a mobile app. Public clients cannot keep a static secret. Use PKCE and the platform redirect pattern.
The login succeeds but account linking is automatic. Bind provider, issuer, subject, tenant, and user-consent policy before linking identities.
Tests use only a mocked callback. Mock tests miss proxy rewriting, browser cookie rules, app-claim conflicts, and real redirect behavior.
Logs capture the complete query string. Authorization codes and error details can become credentials or personal data. Record a stable result class instead.
FAQ
Is PKCE a replacement for state?
No. PKCE binds the code redemption to the client transaction. State binds the authorization response to the browser or app transaction that initiated it. Use both where the client and provider flow support them.
Can a redirect URI contain a wildcard?
Avoid wildcards for authentication callbacks. Register exact values per client and environment. If a platform requires a constrained pattern, document its parser, ownership, and test cases rather than accepting arbitrary subdomains or paths.
Can a single verifier be reused for several tabs?
Do not reuse it across transactions. Give each attempt its own state, verifier, redirect binding, expiry, and one-time consumption record. This also makes cancellation and replay behavior observable.
Should the browser receive the provider access token?
A server-backed web application can usually keep provider tokens server-side and give the browser a protected local session. A public native client may need a token, but it should use the platform’s protected store and the provider’s documented lifetime and revocation behavior.
Does a successful token exchange prove the login is safe?
No. It proves only that the provider accepted the code and client authentication under its contract. The application still needs token-purpose, issuer, audience, scope, account-mapping, session, and authorization checks.
Reader next step
Choose one agent-written authentication patch and write its contract before approving the diff: client type, provider, exact redirect URI, PKCE method, state transaction, token audience, storage boundary, local session, and account-linking rule. Run the full matrix with a disposable identity and controlled callback endpoints. Keep the patch blocked until altered state, verifier, redirect, provider, token, and native-app ownership cases fail before any session or account side effect, and until logs contain no reusable credentials.