- Browser visits the authorization endpoint
- Authorization server redirects a code to the registered callback
- Client redeems the code and verifier at the token endpoint
For a confidential web application, draw a line between browser navigation and backend HTTP calls. The authorization request and callback are browser-facing. Code redemption is a backend call containing credentials that don’t belong in frontend code.
OAuth also permits public clients that redeem codes without a backend secret. That isn’t AnyOAuth’s customer contract: its token exchange requires backend client authentication. A frontend SDK or a client ID alone doesn’t change that requirement.
Save the transaction before leaving the app
The client needs transaction-specific data before redirecting the browser. Save a random state value, a fresh PKCE verifier, the corresponding S256 challenge, the intended callback, and an expiry. Bind that transaction to the browser that initiated it.
The state returned at the callback must match the saved transaction, not merely have the right shape. The verifier stays with the transaction; only its derived challenge goes into the authorization request. The PKCE reference owns the generation and comparison details.
For a backend-owned flow, keeping the transaction server-side behind an initiating-browser cookie avoids handing the backend secret to browser code. Multiple login attempts need distinct transactions so one tab doesn’t overwrite another tab’s verifier or state.
RFC 9700 permits properly bound PKCE to provide CSRF protection under specified conditions. Don’t translate that into deleting state checks from an integration that requires them. AnyOAuth’s contract requires browser-bound state as well as S256 PKCE.
The authorization request defines the return path
The authorization server must know which registered client is asking and where it may return the response. In a standards-based code request, response_type=code selects code delivery, and the client ID identifies the registration. The client ID is public; it is not proof that the caller controls the app.
| Request value | What it establishes |
|---|---|
| Client ID | Which application registration is requesting access |
| Redirect URI | The registered destination for the browser response |
| State | The client transaction to correlate on return |
| Code challenge and S256 method | The proof required at redemption |
| Scope, where supported | The requested resource permissions or OIDC identity request |
Exact callback matching matters. A different scheme, host, path, or trailing slash can identify a different destination. Don’t fix a mismatch by allowing arbitrary redirect prefixes. RFC 9700 §2.1 requires exact matching with a narrowly defined native-loopback port exception.
The provider handles authentication and authorization under its own policy. When the flow succeeds, it redirects the browser with a code and the transaction’s state. The code is an exchange credential, not a user profile or an application session.
Validate the callback before redeeming the code
The client should reject callbacks that don’t belong to a live, browser-bound transaction. Check the saved destination, state, and expiry; reject ambiguous duplicate parameters and responses containing both a code and an error. An error response still needs correlation before it is treated as the result of this login attempt.
Then consume the saved transaction atomically, so concurrent callback handling can’t produce two accepted attempts. The authorization server separately enforces single-use code redemption. Those are complementary checks at two different places.
For a confidential client, backend redemption presents the code, the original verifier, the redirect URI where required, and the configured client authentication. The server verifies that the code belongs to that client and transaction before returning tokens. Don’t silently retry a single-use exchange: a lost response can leave you unsure whether the code was already consumed.
The token response’s contents depend on the contract. OAuth returns access credentials; OIDC code flow also returns identity evidence that needs validation. Neither response authorizes your backend to skip local account policy.
AnyOAuth has two different codes
AnyOAuth redeems the upstream provider code internally, then issues a separate customer handoff code. The two codes have different issuers, recipients, and redemption endpoints. Labeling both code is normal at their respective HTTP boundaries, but calling them the same credential in your mental model creates bad debugging assumptions.
- Google or GitHub sends its provider code to AnyOAuth’s provider callback.
- AnyOAuth completes provider identity checks and sends a new handoff code to the customer callback.
- Customer backend redeems that handoff code at AnyOAuth, then retrieves the normalized profile.
Each customer project has its own client ID and backend secret. The customer sequence is /authorize → /v1/token → /v1/profile, with required S256 PKCE and backend authentication. AnyOAuth’s token endpoint uses its documented JSON contract; don’t assume a generic OAuth library’s form-encoded request is an interchangeable implementation.
The provider transaction lasts 10 minutes, the customer handoff code lasts 2 minutes, and the opaque profile token lasts 15 minutes. Those are AnyOAuth limits, not universal OAuth defaults. Expirations are checked when credentials are used, regardless of cleanup timing.
A successful exchange still needs an app session
The customer backend uses the profile token to retrieve identity data, maps the project/provider/subject-scoped identity, applies local account policy, and creates its own session. Equal emails don’t merge identities. App permissions and session expiry belong to the app.
Once your app has resolved the identity, the profile token has done its job. Its lifetime doesn’t set the application session’s lifetime, and a provider token never enters this customer-facing flow.
When inspecting an integration, locate the actual session creation. It should happen after trusted identity retrieval and policy checks, not merely after parsing a code query parameter. A callback that returns to your homepage without a session can be a completed provider redirect and an incomplete application login.
Use the docs for the SDK implementation contract and the security model for credential boundaries. What OAuth does explains the underlying delegation model, while OAuth versus SAML covers a different federation choice. Keep those jobs distinct when asking an assistant to implement or review the callback.
Sources
Primary references retrieved 2026-10-06:
- IETF, RFC 6749: The OAuth 2.0 Authorization Framework, especially §§4.1.1–4.1.4 and 10.5.
- IETF, RFC 7636: Proof Key for Code Exchange, especially §§4.3–4.6.
- IETF, RFC 9700: Best Current Practice for OAuth 2.0 Security, especially §§2.1.1, 4.5, and 4.7.
- OpenID Foundation, OpenID Connect Core 1.0, especially §3.1.