OAuth and delegated identity
Every "Sign in with Google" button is a decision not to run a password system.
Your app doesn't check a password, doesn't verify the person, and never stores a credential. It asks a provider it already trusts, and takes the answer.
The protocol underneath is OAuth, and the flow has more moving parts than the button suggests.
Two different authorizations
One thing trips almost everyone up, and it's worth clearing up before the flow makes sense.
You've seen the consent screen: an app asking for your name, profile photo and email address. Sometimes more, sometimes a lot more, like contacts and calendar and every message in your inbox.
That screen is authorization, and it has nothing to do with your app's roles. It decides which parts of the user's Google account your app may reach, and those permissions are called scopes.
Your own admin and user roles are still decided inside your back end, after the login finishes, exactly as in the previous chapters.
OAuth authorizes, OIDC identifies
Strictly, OAuth grants delegated authorization: permission for your app to act on a resource. The identity layer built on top of it is OpenID Connect, or OIDC, and the "sign in with" buttons are OIDC.
The distinction matters in code. An OAuth access token says what your app may reach; an OIDC ID token says who the user is. Treating the first as proof of identity is a common and real mistake.
Google decides which of your Google details the app can see. Your app decides what that person can do once they're in. Different question, different place, different answer.
The round trip
Eight steps, and the user leaves your app in the middle of them:
- The user presses the button in your app.
- Your app redirects them to Google.
- The user logs in, directly with Google. Your app sees none of this.
- Google shows the consent screen for the scopes you asked for.
- If the user agrees, Google sends your server a one-time approval code. This code logs nobody in. It represents consent, nothing more.
- Your server sends that code straight back to Google, along with your app's own secret credentials.
- Those credentials prove to Google that the request came from your back end.
- Google replies with an ID token saying who the user is, and your app logs them in.
The two-step handover in five to eight is the part worth understanding. The code travels through the user's browser and is useless on its own; the token that actually matters is exchanged server to server, where a browser never sees it.
That's the appeal: you can't leak a credential you never received.
Three ways it goes wrong
Sending the user away and trusting the provider to send them back opens three quiet gaps.
| What goes wrong | Why it matters | The fix | |
|---|---|---|---|
| 1 | Asking for too much | Scopes beyond what the app uses mean a leak exposes far more than it should, and users lose trust in an app that looks nosy. Information disclosure | Request the smallest set your features actually use |
| 2 | The approval code leaking | The code turns up in the URL bar, browser history, analytics or server logs. Anyone who sees it before it expires can finish the login as that user. Spoofing | Keep it out of anything that gets logged, and exchange it immediately |
| 3 | Trusting the ID token unchecked | Anyone can write a token that looks right. Only Google can sign one. Accepting it unverified lets an attacker claim to be anybody. Spoofing | Verify the signature, the audience, and the expiry, before trusting anything in it |
Pitfall 3 is the one that undoes the whole model. The point of delegating was that a trusted party vouches for the user; skipping verification means you're trusting whoever sent the request instead.
Anyone can write a note. Checking the signature is how you know this one came from Google, and skipping that check is trusting handwriting you've never seen.
Try it
Five situations from a live OAuth integration. Name the pitfall, the risk, and the fix.
- Your app needs only an email address to log people in, and the OAuth request also asks for contacts, calendar and Drive files.
- The consent screen warns that your app wants permission to delete calendar events. Your app never touches Calendar.
- After login, the page briefly shows the short-lived approval code in the URL.
- Your back end receives the ID token and accepts it without checking the signature or who it was issued for.
- Your back end accepts an ID token that expired an hour ago.
Compare your answers
| # | Pitfall | Risk | Fix |
|---|---|---|---|
| 1 | Asking for too much | A leaked token exposes far more of the user's account than the app ever needed | Request only the scopes a feature uses |
| 2 | Asking for too much | Same exposure, plus a consent screen that makes the app look untrustworthy before anyone has used it | Drop the unused scope |
| 3 | Leaked approval code | URLs get logged, saved and shared. Whoever captures the code before it expires can complete the login | Keep codes out of the URL, exchange immediately |
| 4 | Trusting the ID token | Anyone can forge something token-shaped. Without signature and audience checks, an attacker can claim to be any user | Verify signature and audience before trusting it |
| 5 | Trusting the ID token | An old token can be replayed to log in as that user repeatedly | Check expiry and reject anything past it |
Two and one are the same pitfall with different consequences, which is why the list is worth reading as three problems rather than five. Four and five are also one pitfall: verification is a set of checks, and skipping any of them is skipping verification.
Where this goes next
Three models, each solving the same problem from a different direction. The server remembers, the client carries proof, or a provider vouches.
Choosing an identity model puts all three side by side and works through when each one is the right answer.

