🔓 Login with Google, step by step
You click “Sign in with Google” and, without giving the app your password, you’re in. Behind it is a dance of redirects and a code that gets exchanged for tokens — with two key defenses: PKCE and state. Walk through it step by step (Authorization Code + PKCE).
The flow, step by step
-
01 PKCE in your app
Your app prepares PKCE
First, the app generates a random secret, the code_verifier, and its fingerprint code_challenge = SHA-256(verifier). The verifier stays in the app; it will only leave at the very end.
The fingerprint travels now; the secret, at the end. Anyone intercepting the path can’t reconstruct it. -
02 Authorization Request message
Redirect to Google
The browser goes to Google with client_id, redirect_uri, scope (what it asks for), a random state and the code_challenge. This is the front channel (through the browser).
No password or token travels here: just a request framed by the app’s configuration. -
03 Login + consent at Google
You authenticate and consent
On Google’s screen (not the app) you sign in and approve the permissions the app requests: “Allow this app to see your profile and email?”.
Your password is seen by Google only, never the app. That’s the point of OAuth: delegate without sharing credentials. -
04 Redirect + code message
Back with the “code”
Google redirects the browser back to your redirect_uri with a single-use authorization code and the same state.
The code goes through the URL, but on its own it’s useless: it must be exchanged with the verifier. It is not the token. -
05 state check in your app
The app verifies the “state”
The app checks that the returned state is exactly the one it sent at the start.
This binds the response to your request: it’s the defense against CSRF (being fed someone else’s authorization). -
06 Token Request 🔒 message
Exchange the code for tokens
Now over the back channel (server to server, or directly with PKCE): the app POSTs to the token endpoint with the code and —finally— the original code_verifier.
It’s the first time the verifier leaves. If the app has a client_secret, it goes here too, never through the browser. -
07 PKCE verify at Google
Google validates PKCE
Google computes SHA-256(code_verifier) and checks it matches the code_challenge from step 2. It only matches if whoever redeems is who requested.
Even if an attacker stole the code, without the verifier they can’t redeem it. That’s what PKCE adds. -
08 Tokens 🔒 message
Tokens: it now knows who you are 🔒
Google returns an access_token (to call its API) and, in “Login with Google” (OpenID Connect), an id_token (a JWT with your identity). Session started.
The id_token says who you are; the access_token, what the app may do. Don’t confuse them.
Without JavaScript you get the full flow as an article. With JS, step through it (arrow keys ← → work too).
🔑 PKCE: the secret that arrives late
The app sends the fingerprint (code_challenge) first and keeps the secret (code_verifier) until the end. Even if the code is stolen via the browser, without the verifier it can’t be redeemed. Essential for mobile apps and SPAs (no client_secret).
🛡️ state: against CSRF
A random value that goes out and must come back unchanged. It binds the response to your request: without it, an attacker could make you log into their account (or vice versa).
🎫 id_token ≠ access_token
The id_token (OpenID Connect) says who you are — a JWT you validate. The access_token says what the app may do against the API. That’s why the implicit flow (token straight in the URL) is discouraged: always use the code.
Sources: RFC 6749 (OAuth 2.0) · RFC 7636 (PKCE). Got an id_token? Open and audit it in the JWT auditor.