Auth Standards Deep Dive · 1 of 6

OAuth 2.0 — Delegated Authorization, Done Right

OAuth 2.0 is the protocol that lets one application act on a user's behalf at another — "let this app read my Google calendar," "let Slack post to my GitHub." It's not authentication ("who are you?") but authorization ("what is this app allowed to do for you?"). Get the flow right and you delegate cleanly; get it wrong and you build a phishing surface.

Authorization CodePKCEScopesRefresh Tokens
← Back to Security
Quick Facts

What OAuth Is — and Isn't

Basic Concepts

  • Authorization, not authentication. OAuth grants tokens that permit actions. "Logging in with Google" actually uses OpenID Connect, which sits on top of OAuth.
  • Four roles: resource owner (the user), client (the app asking for access), authorization server (issues tokens — Google, Auth0, Okta), resource server (the API the token grants access to).
  • Tokens, not credentials. The user's password never reaches the client. The client gets a scoped, time-limited access token.
  • Scopes: what the token is allowed to do. read:calendar, write:posts. Smaller scopes mean smaller blast radius if leaked.
  • OAuth 2.1 is a consolidation of best practices and deprecations. In 2026, build to OAuth 2.1 and treat it as the default.
The Flows

Which Grant Type to Use

Authorization Code + PKCE — The Default in 2026

The flow nearly everyone should use, for both web apps and mobile/SPA clients:

  1. Client redirects user to the auth server's authorize endpoint with a code_challenge (PKCE).
  2. User authenticates and consents at the auth server.
  3. Auth server redirects back to the client with a short-lived code.
  4. Client exchanges code + code_verifier for tokens at the token endpoint.

PKCE (Proof Key for Code Exchange) defends against code-interception attacks. Originally designed for mobile, now mandatory for all clients in OAuth 2.1, including server-side webapps.

Client Credentials

Service-to-service. No user involved. The client authenticates with its own ID and secret (or a private key / mTLS) and receives a token to act on its own behalf. The right grant for backend integrations, scheduled jobs, and machine-to-machine APIs.

Device Authorization

For input-constrained devices — TVs, CLI tools, IoT. The device shows a code; the user goes to a URL on a phone or laptop, enters the code, approves. The device polls until approved. How gh auth login, AWS CLI SSO, smart TV Netflix logins all work.

Deprecated Flows — Don't Use
  • Implicit Flow: tokens returned in the URL fragment. Vulnerable to leakage; PKCE removed the need. Removed in OAuth 2.1.
  • Resource Owner Password Credentials: client collects the user's password and sends it to the auth server. Defeats the entire point of OAuth. Removed.
The Pitfalls

Where Implementations Go Wrong

Skipping the state Parameter

The state param prevents CSRF on the redirect back. Generate a random per-request value, store it in a session, verify on callback. Skipping it is how attackers tie an attacker-controlled auth flow to the victim's session.

Lax Redirect URI Validation

The auth server must exactly match the redirect URI against a registered allow-list — no wildcards, no path-prefix matches. Open redirects in your callback URL are how attackers exfiltrate tokens to their own servers.

Storing Tokens in localStorage

localStorage is readable by any JavaScript on your origin — one XSS, every token leaks. Use HttpOnly cookies for browser-stored tokens, paired with a backend that exchanges them for the API. The "BFF (Backend-for-Frontend) pattern" is now the recommended browser architecture.

Treating Access Tokens as Identity

An access token says "this client may act for some user." It does not authoritatively identify the user. For login, use OIDC's ID token, which is signed and includes sub, iss, aud. Mixing the two is a common cause of authentication bugs.

Refresh Token Reuse Without Detection

Modern guidance: rotate refresh tokens on every use. Detect reuse (the same refresh token presented twice) as a sign of compromise and revoke the entire token family. Public clients (SPA, mobile) should always rotate; confidential clients can choose.

Over-broad Scopes

Asking for repo when you only need repo:read; asking for https://www.googleapis.com/auth/gmail.modify when gmail.readonly would do. Users notice. So do regulators. Ask for the minimum.

Beyond the Basics

Modern Patterns

  • DPoP (Demonstrating Proof of Possession): binds a token to a key the client holds. Stolen tokens become useless without the key. Increasingly required for high-value APIs.
  • mTLS-bound tokens: the token is only valid when presented over the same client TLS certificate that requested it. Common in fintech and banking.
  • Pushed Authorization Requests (PAR): the client posts the auth request server-to-server; the user is redirected with only a reference. Prevents tampering with parameters in the browser.
  • Fine-grained authorization beyond scopes: OAuth 2.0 Rich Authorization Requests (RAR) carry structured permissions — "transfer up to $500 from account X to account Y once."
Decision

What to Use

  • Public OAuth API for partners: Authorization Code + PKCE. Always.
  • Service-to-service calls: Client Credentials with a private-key JWT or mTLS — not a shared secret.
  • SPA login: Authorization Code + PKCE with a BFF pattern. Don't keep tokens in JS.
  • Mobile login: Authorization Code + PKCE in a system browser (not an embedded webview).
  • You probably don't want to build the auth server. Use Auth0, Okta, Cognito, Keycloak, Authentik, Hydra, or your IdP's offering. Implementing OAuth correctly from scratch is a multi-year project.
Continue

Other Auth Standards