The flow in order
In the server-side flow the app creates a random state value, a nonce and a PKCE code verifier, stores all three in the user's session on the server, and redirects the browser to Google with the state, the nonce and the code challenge (the S256 hash of the verifier). Google returns the browser to your redirect address with a one-time code and the state. The app checks the state, exchanges the code with the client secret and the original verifier at the token endpoint, receives an ID token and validates it, then looks the person up and starts a session. Each step defends against something different, which is why leaving one out is not harmless.
- State protects against a forged callback made by another site.
- The nonce ties the ID token to this sign-in attempt and so resists replay.
- PKCE ties the authorisation code to the app that asked for it, so a code taken from the browser cannot be redeemed by someone else. RFC 9700 recommends it for confidential clients too; a confidential OpenID Connect client may rely on the nonce instead only with the extra precautions in section 4.5.3.2 of that RFC.
- The code is single-use. It reaches your app through the browser redirect, so exchange it at once on the server, where the tokens it returns never touch the browser.
- Use each stored value once and clear it, so a second callback with the same state is refused.
What to check in the ID token
Google's guide and the OpenID Connect standard list the checks: the signature verifies against Google's published keys, found through the discovery document; the issuer is Google's; the audience includes your own client ID; the current time is before the expiry; and the nonce, if you sent one, matches. Google says to validate all ID tokens on your server unless you know they came directly from Google, and calls validation critical before you rely on a token; the exception is a token received straight from the token endpoint over HTTPS in your own code exchange, and any other component that passes it on must still validate it. Checking is cheap, so do it every time.
- A token for another app's client ID must be refused.
- An expired token must be refused even if everything else matches.
- Use a maintained library for signature checks rather than writing your own.
Redirect addresses are a separate check
The redirect address must match a registered one exactly, and a mismatch is a different failure from the ones above: it stops the sign-in before it reaches your callback. The guide on exact redirect-address matching, linked below, covers the comparison, the environments and the causes in full, and the guide on state, verifier and callback errors covers a return that reaches your app and is refused. This guide covers the checks the callback must make once the return arrives.
- If the sign-in already exists and shows a redirect error, that is a repair job rather than an addition.
- Do not log the code, tokens, the verifier or the client secret.
A test for each failure
Write one test per row. Missing or altered state: no session. A state value used a second time: no session. A code exchange sent without the PKCE verifier, or with one that does not match the challenge: the stand-in token endpoint refuses it and no session starts. A code that is invalid or already used: the real authorisation server rejects it (Google reports an invalid code as invalid_grant) and the app, which cannot detect reuse itself, must handle that answer and start no session. A synthetic ID token signed with a test key but for another audience: refused. The same token expired: refused. A token whose nonce differs from the stored one: refused. A valid token: a session for the right account. Run them against the real callback code with the token endpoint replaced by a stand-in that checks S256 and answers invalid_grant, then run the happy path with your own test Google accounts on staging, which also shows that Google accepts the challenge and verifier for your client.
How the paid outcome is accepted
The Google sign-in outcome is accepted when the failure tests above pass alongside the account-linking cases. It is priced from £495 as an untested figure fixed after the linking rule is agreed; payment follows your sign-off. Brand verification of the consent screen and Google Cloud settings stay with your account holder. If you only need an existing, failing sign-in repaired, the fixed-price repair job for a redirect or callback error is the smaller scope.
Sources and limits
- Google: OpenID Connect Checked 2026-10-11.
- The server flow uses an anti-forgery state value (optional but strongly recommended) compared on return and a nonce for replay protection.
- ID token validation covers the signature against Google's published keys, issuer, audience and expiry.
- Google says to validate all ID tokens on your server unless you know they came directly from Google, and calls validation critical before using a token; a token received directly from Google in the code exchange is the exception.
- Its sample discovery document lists the code challenge methods plain and S256; the page does not mention PKCE.
- Google: OpenID discovery document Checked 2026-10-11.
- code_challenge_methods_supported lists plain and S256.
- Google: OAuth 2.0 for installed applications Checked 2026-10-11.
- Google documents the code_challenge and code_challenge_method parameters, recommends the S256 method (the Base64URL-encoded SHA256 hash of the verifier), and has the app send the code_verifier when it exchanges the code; the web-server guide does not list these parameters.
- RFC 9700: OAuth 2.0 Security Best Current Practice, section 2.1.1 Checked 2026-10-11.
- PKCE is required for public clients and recommended for confidential clients; confidential OpenID Connect clients may use the nonce instead if they validate it and disregard tokens until it passes (section 4.5.3.2).
- S256 is the only code challenge method that does not expose the verifier in the authorisation request.
- RFC 6749: authorization response Checked 2026-10-11.
- An authorization code must expire shortly after issue (10 minutes maximum recommended) and the client must not use it more than once; the server must deny a code used more than once.
- OpenID Connect Core 1.0, section 3.1.3.7 Checked 2026-10-11.
- The issuer must match, the audience must include the client ID, the current time must be before exp, and a sent nonce must match the claim.