OAuth 2.0 & OpenID Connect

Chapter 8
OAuth 2.0 & OpenID Connect
Delegated authorization vs authentication, social login, and the flows

"Sign in with Google." "Allow this app to access your calendar." These are OAuth 2.0 and OpenID Connect at work — the protocols for letting one app act on your behalf at another without giving it your password. They're widely used and widely misunderstood, starting with the most important distinction: OAuth is about authorization, OIDC adds authentication.

The Problem OAuth Solves: Delegated Access Without Sharing Passwords

Suppose a photo-printing app wants to access your Google Photos. The naive (terrible) way: you give the app your Google password. Now it can do anything as you, forever, and you can't revoke it without changing your password. OAuth 2.0 solves this: it lets you grant the app limited, revocable access to specific resources, without ever sharing your credentials. The app gets an access token scoped to just what you approved.

The Four Roles

RoleWhoExample
Resource Owneryou (the user)you, who owns the photos
Clientthe app wanting accessthe photo-printing app
Authorization Serverissues tokens after you consentGoogle's OAuth server
Resource Serverholds the protected datathe Google Photos API

OAuth Is Authorization — OIDC Adds Authentication

The #1 OAuth misconception: OAuth alone does NOT authenticate the user
OAuth 2.0 was designed for delegated authorization — "this app may access these resources" — not for proving who the user is. People bolted "login with OAuth" on top anyway, which led to real vulnerabilities, because an access token says what the bearer may do, not who they are. OpenID Connect (OIDC) is the thin layer added on top of OAuth 2.0 to do authentication properly: it adds an ID token (a JWT, Chapter 7) containing verified identity claims (who the user is, issued by the provider, for your app). Rule of thumb: OAuth = authorization (access), OIDC = authentication (identity). If you're doing "Sign in with…", you want OIDC, not raw OAuth.

The Authorization Code Flow (with PKCE)

The recommended flow for web and mobile apps. The key idea: the app never sees your password, and the powerful token is exchanged through a back channel, not exposed in the browser. Step by step:

You click "Sign in with Google." The app redirects you to Google's authorization server (with its client ID, requested scopes, and a state value).
You authenticate directly with Google (password + MFA) and approve the requested scopes. The app never sees your credentials.
Google redirects you back to the app with a short-lived authorization code (not a token).
The app's server exchanges that code (plus its client secret) with Google's token endpoint — a back-channel request — for the access token (and an ID token, if OIDC).
The app uses the access token to call the API on your behalf, limited to the approved scopes.
Why a code first, and why PKCE — defeating interception
Why not hand the app the token directly? Because the redirect goes through the browser, where URLs can leak (history, referrer, logs). So the browser only ever carries a short-lived, single-use authorization code; the actual token is fetched server-to-server. PKCE (Proof Key for Code Exchange) hardens this further for public clients (SPAs, mobile apps that can't keep a secret): the app generates a random code_verifier, sends its hash (code_challenge) when starting the flow, and must present the original verifier when exchanging the code. So even if an attacker intercepts the authorization code, they can't exchange it without the verifier — which never left the legitimate app. PKCE is now recommended for all clients, not just public ones.

Scopes: Least Privilege for Delegated Access

A scope is a named permission the client requests (photos.read, calendar.write, email). The consent screen shows you exactly what you're granting, and the token is limited to those scopes. This is least privilege applied to delegation: request only the scopes you actually need, and users (rightly) distrust apps asking for more than they should. Over-broad scope requests are both a security risk and a conversion killer.

Common Misconfigurations

Where OAuth/OIDC integrations go wrong
(1) Missing state → CSRF on the callback. The state parameter is a random value the app sends and verifies on return; without it, an attacker can splice their own authorization code into your session (a login-CSRF). Always generate, store, and check state. (2) Loose redirect_uri validation. If the authorization server allows wildcard or unregistered redirect URIs, an attacker can redirect the code/token to a site they control. Register exact redirect URIs. (3) Using the implicit flow. The old "implicit" flow returned tokens directly in the browser URL fragment — exposed and unrevocable; it's deprecated. Use authorization code + PKCE. (4) Treating an OAuth access token as proof of identity. An access token isn't an ID token — verify the OIDC ID token's signature, issuer (iss), audience (aud), and expiry before trusting its identity claims. (5) Not verifying the ID token at all — accepting it without checking the signature/issuer lets an attacker forge identities.

Social Login: What You Gain and Give Up

"Sign in with Google/Apple/GitHub" (OIDC) means users don't create yet another password, you don't store credentials, and you inherit the provider's strong authentication (their MFA, their anti-abuse). The trade-offs: you depend on the provider (outages, account bans, policy changes cascade to your users), you share some user data with them, and you must still handle account linking (same person, multiple providers) and the case where a user loses access to their provider account. For many apps it's a net win; weigh it deliberately rather than adding every provider button reflexively.

Hands-On Exercises

Exercise 1

Explain the difference between OAuth 2.0 and OpenID Connect in terms of authorization vs authentication, and why using a raw OAuth access token as "proof of who the user is" is a security mistake. State what OIDC adds and which token carries identity.

📄 View solution
Exercise 2

Walk through the authorization code flow and explain why the browser only carries a short-lived code (not the token), and why the token is exchanged server-to-server. Then explain what PKCE adds and how it defeats authorization-code interception.

📄 View solution
Exercise 3

Identify the security problem in each: (a) the app omits the state parameter; (b) the authorization server accepts any redirect_uri under the domain; (c) the app uses the implicit flow; (d) the app trusts the ID token's claims without verifying its signature/issuer/audience. Give the fix for each.

📄 View solution

Chapter 8 Quick Reference

  • OAuth 2.0 = delegated authorization — grant an app limited, revocable access to resources without sharing your password
  • Four roles: Resource Owner (you), Client (app), Authorization Server (issues tokens), Resource Server (holds data)
  • OAuth = authorization; OIDC = authentication — OIDC adds an ID token (JWT) with verified identity claims; use OIDC for "Sign in with…"
  • Authorization Code flow: browser carries a short-lived code; the token is exchanged server-to-server (back channel)
  • PKCE — verifier/challenge so an intercepted code can't be exchanged; recommended for all clients
  • Scopes = least-privilege permissions shown on the consent screen; request only what you need
  • Misconfigs: missing state (callback CSRF) · loose redirect_uri · implicit flow (deprecated) · trusting an access token as identity · unverified ID token
  • Social login: no passwords to store + inherit provider's MFA, but you depend on the provider & share data — weigh deliberately
  • Next chapter: account recovery & verification — secure password reset, email verification, the #1 takeover vector