- Browser transaction: state binds the returning callback to its initiator
- Backend redemption: secret authenticates the project; S256 proves the verifier
- Application session: your backend owns permissions and invalidation
Take a callback URL copied from one browser and opened in another. The URL may have a plausible code and the right path. Your handler still needs to reject it if it can’t find the initiating browser’s saved transaction. A correctly formatted callback isn’t evidence that this browser started the login.
That is a useful review case because it names the missing evidence. Security review gets vague when it starts and ends with “OAuth is secure.” Instead, name the changed input, the actor validating it, and what must not happen after rejection. The cases below are acceptance criteria, not claimed test results.
State belongs to the initiating browser transaction
Your backend checks returned state against a fresh value saved for that browser’s login attempt. A state value embedded in the URL alone doesn’t establish browser ownership. Preserve the transaction in a short-lived server-side session, enforce its expiry, and consume it atomically, including denial callbacks.
AnyOAuth checks state and an initiating-browser cookie on the provider-side leg. Your app must still validate its own callback transaction. The service cannot replace the association between your browser session and your saved state. If an assistant-generated handler checks only that state exists, it hasn’t implemented the required check.
The full callback trace keeps those two legs separate. Provider authorization codes terminate at AnyOAuth; your app receives a distinct AnyOAuth handoff code.
PKCE and client authentication answer different questions
S256 binds code redemption to the original transaction’s verifier. The backend client secret authenticates the project. AnyOAuth requires both, even for a confidential backend. The challenge travels in the authorization request; the verifier remains on the backend until redemption. Reusing one fixed verifier across logins defeats the transaction-specific design.
RFC 7636 defines S256 as base64url encoding, without padding, of the SHA-256 hash of the ASCII verifier. Use the SDK’s transaction helper rather than improvising an encoding recipe. The PKCE reference covers the mechanism; the important review question here is whether the backend redeems with the exact original verifier.
- Observed redirect: an observer obtains a handoff code and the public S256 challenge. Those values do not reveal the original verifier.
- Attempted redemption: AnyOAuth requires the project’s backend client authentication and a verifier matching the stored challenge, bound to the original callback.
- Remaining exposure: a stolen bearer profile token can retrieve its scoped profile until expiry or revocation. PKCE protects code exchange, not every later credential.
A short lifetime limits exposure; it doesn’t make a leaked credential harmless. Keep profile tokens out of browser storage, callback URLs, analytics, and ordinary logs. AnyOAuth stores bearer credentials as hashes, but that doesn’t protect a live credential stolen from your running backend. Its security model distinguishes storage protections from runtime compromise.
Exact callbacks don’t validate post-login destinations
AnyOAuth accepts only the project’s exact registered callbacks. HTTPS is required except HTTP loopback for development; wildcards, embedded credentials, queries, and fragments aren’t permitted in registered callback URLs. Send the saved callback again at exchange. Don’t derive it from a visitor-controlled host or parameter.
After login, a destination such as /billing is a separate application choice. Save an allowlisted internal destination in the transaction. Reject absolute URLs and protocol-relative paths such as //attacker.example; don’t blindly forward a raw returnTo. RFC 9700 explains why open redirectors can undermine an otherwise protected redirect flow.
This separation also makes reviews easier. One setting says where authentication may return. Another says where a signed-in user may continue. Neither needs to become a wildcard because the other is dynamic.
Rejecting the handoff must prevent session creation
Every failed check must stop the user/session path. Use this review matrix against your actual handlers:
| Changed input | Required boundary | Application outcome |
|---|---|---|
| Missing or mismatched state | Backend transaction validation | No exchange or session |
| Wrong original verifier | AnyOAuth redemption | No profile token or session |
| Different callback | Registered callback and redemption binding | No successful handoff |
| Expired or replayed handoff code | Two-minute, single-use redemption | Start a fresh login |
| Same email, different provider identity | User lookup by stable subject | No automatic account merge |
The app’s own session is outside the profile-token lifecycle. Logout must invalidate that session. Revoking an AnyOAuth token won’t undo a session already created, and an authenticated profile doesn’t grant an app role. Keep those checks beside your user record and authorization logic.
AnyOAuth implementation choice
AnyOAuth, which we build, fits a backend that needs configured Google/GitHub identity handoffs and wants to retain its session system. Each project has a client ID and backend secret. Its opaque profile token expires after 15 minutes, has no refresh flow, and cannot proxy provider APIs. It supplies neither a customer-facing OIDC server nor SAML federation.
A direct-provider implementation with maintained libraries is the DIY alternative when you need your own consent identity or broader provider permissions. With AnyOAuth, users see the service’s provider application identity. Whichever route you choose, review provider identity validation as well as your callback; OAuth versus OIDC explains that distinction.
These safeguards are implementation boundaries, not compliance certification. Keep privacy decisions in the application’s data design, as explained in social sign-in without password handling.
Sources
Read or retrieved 2026-10-06:
- AnyOAuth, Quickstart, Security model, and Privacy & compliance considerations.
- IETF, RFC 7636, §§4.2–4.6, on S256 and verifier checking.
- IETF, RFC 9700, §§2.1 and 4.11, on redirect validation, transaction binding, and open redirectors.