ML
Security

OAuth 2.0 vs OIDC: What Each Actually Does

OAuth gives you authorisation. OIDC gives you authentication. Treating them as the same is how 'Sign in with X' became a security punchline.

March 04, 20268 min readSecurityIdentity

If you have ever wired up "Sign in with Google" and copy-pasted from three different tutorials, you have probably mixed OAuth and OIDC concepts. They use the same endpoints, the same redirects, the same vocabulary — but they answer different questions.

1. The one-sentence difference

  • OAuth 2.0 answers "is this app allowed to access this resource on the user's behalf?"
  • OIDC answers "who is this user?"

OIDC is built on top of OAuth, but adds an identity layer. The big concrete difference: OIDC returns an id_token (a JWT describing the user). OAuth returns only an access_token (a credential to call APIs).

2. The OAuth flow you actually want

For public clients (SPAs, mobile apps), use Authorization Code + PKCE. The implicit flow is dead.

// 1. Client creates code_verifier (random) and code_challenge = SHA256(verifier)
// 2. Redirect user to:
GET /authorize?response_type=code
              &client_id=abc
              &redirect_uri=https://app/cb
              &scope=openid email profile
              &code_challenge=...&code_challenge_method=S256
              &state=<csrf>
              &nonce=<replay>

// 3. User authenticates with the IdP, IdP redirects back with ?code=xxx
// 4. Client exchanges code for tokens (one-shot, server-side):
POST /token
  grant_type=authorization_code
  code=xxx
  redirect_uri=https://app/cb
  code_verifier=...
  client_id=abc

3. Tokens you get and what to do with them

  • id_token (OIDC only) — JWT. Tells you who the user is. Verify the signature, then read sub, email, etc. Never send it to APIs; it is for your client to read.
  • access_token — credential for APIs. Often opaque, sometimes a JWT (depends on the provider). Treat it as a bearer secret.
  • refresh_token — long-lived. Server-side only. Exchange it for new access tokens.

4. The four claims that prevent 90% of bugs

On the id_token:

  • iss — must equal the IdP you trust.
  • aud — must contain your client_id.
  • exp — must be in the future.
  • nonce — must equal the nonce you sent. Prevents replay.

Skipping any of these is how the famous "I logged in as the wrong tenant" bugs happen. Every OIDC library checks them by default — do not turn that off.

5. Scopes are a contract, not an audit trail

When you ask for scope=email profile, the IdP shows the user "this app wants access to your email and profile." The IdP enforces that the access_token can only do those things, on its APIs. If you then use that token to call your own API, scope means whatever you make it mean.

Practical: define scopes for your own APIs (orders:read, orders:write) and validate them server-side on every request. Do not trust scopes on the client.

6. Common confusions

"We use OAuth to sign people in"

No, you use OIDC. OAuth alone gives you a permission to call an API — it does not tell you the user logged in just now. That is what id_token (with iat and nonce) is for. Without it, you are vulnerable to "logged in as different user" attacks where a stolen access_token is treated as a fresh login.

"We store the access_token in localStorage"

Don't. XSS exfiltration is a one-liner. Use HttpOnly cookies, or — if you must — keep tokens in memory and refresh on reload.

"We don't need PKCE, the redirect is HTTPS"

PKCE protects against the authorisation code being intercepted at any point — including by another app on the same device. It costs four lines of code. Enable it.

Rules of thumb

  • Identity → OIDC. Resource access → OAuth.
  • Authorization Code + PKCE for every public client. No exceptions.
  • Verify iss, aud, exp, nonce on every id_token. Always.
  • Refresh tokens never reach the browser's JavaScript.
SharePostLinkedIn

Reader Discussion

2 replies// weighed in

TopNewestAuthor
Add to the thread
Disagree, agree harder, or share your own experience…
Email instead →markdown okbe kind
  1. Rachel Gold· Staff SREAgrees

    the on-call framing throughout this piece is what makes it land. too many infra articles assume you never get paged. those are written by people who never got paged.

    Mar 07, 2026·3 days later
  2. Omar Khalil· Senior SWEKind words

    this is the third article from this blog I've sent to my team this month. you're cooking. don't switch to crypto.

    Mar 09, 2026·5 days later

Worked on something similar? Email ducminhldm@gmail.com — I read every one. The good ones become future posts.

Comments seeded · live discussion via email