Guide

OAuth 2.0 answers "may this app act for this user?" It issues an access token that names the scopes the app may use. It was never designed to say who the user is. OpenID Connect (OIDC) adds that: an ID token that is a signed JWT naming the user, delivered through the same flow. "Sign in with Google" is OIDC built on OAuth.

Three roles appear in every flow: the user, the client app that wants access, and the authorization server (Google, Okta, Auth0, or your own identity provider) that issues the tokens.

The big idea

The safe flow is the authorization code flow with PKCE. The browser never sees an access token. The browser gets a short-lived code through a redirect, and the app's backend trades the code for tokens directly with the authorization server, over a back channel.

Why the detour? Anything in the browser's address bar can leak, including history, logs and other scripts. A code that can be used only once, by the app that started the flow, is much less useful to an attacker.

Walking through it

PKCE (Proof Key for Code Exchange) protects against a stolen code. Before the redirect, the app makes a random code_verifier and sends its hash, the code_challenge. At the token request, it sends the original verifier. An attacker who intercepts the code can't redeem it without the verifier.

code_verifier  = dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
code_challenge = BASE64URL(SHA256(code_verifier))
               = E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM

Those values are the example from RFC 7636. Your app generates a new verifier for every login.

Three checks guard the redirect:

  • state is a random value the app sends and then checks on return. It stops login CSRF, where an attacker finishes their login in your browser.
  • redirect_uri must match a registered value exactly. Partial or prefix matching lets an attacker send the code to their own server.
  • nonce goes into the ID token. The app checks that it matches, so a replayed ID token fails.

Then, before trusting the ID token, validate it as a JWT: the signature against the provider's published keys, iss, aud (your client ID), and exp. This is the same discipline covered in JWTs and token validation.

Which flows to avoid

The implicit flow returned tokens in the URL fragment, directly to the browser. Current guidance (RFC 9700) says to stop using it. The resource owner password grant asks the client to collect the user's password, which breaks the point of delegation. Use the code flow with PKCE for every client, including single-page apps and mobile apps.

Common mistakes

  • Using the implicit flow, or the password grant, in new code.
  • Accepting any redirect_uri that starts with your domain.
  • Treating an OAuth access token as proof of identity. Use the ID token for identity and the access token for APIs.
  • Skipping the state or nonce checks because the happy path works.

Check yourself

  • Why does PKCE stop a stolen authorization code from being useful? Answer: redeeming the code needs the original code_verifier, which never went through the browser redirect.
  • Why does OIDC add an ID token when OAuth already has an access token? Answer: the access token grants API access and isn't meant to identify the user. The ID token names the user and is meant for the client app.

Key takeaways

  • OAuth delegates access. OIDC adds login identity through an ID token.
  • Use the authorization code flow with PKCE for every client.
  • Check state, nonce, the exact redirect_uri, and the ID token's signature, issuer, audience and expiry.
  • Don't use the implicit flow or the password grant.

Further learning

The OAuth 2.0 Security Best Current Practice (RFC 9700) is the current list of required defenses.

RFC 9700: OAuth 2.0 Security Best Current PracticeThis document describes best current security practice for OAuth 2.0. It updates and extends the threat model and security advice given in RFCs 6749, 6750, and 6819 to incorporate practical experiences gathered since …rfc-editor.org

Next, see how the identity you now have is checked against permissions in authorization models and IDOR.