Guide

OAuth 2.0 and OpenID Connect explained

Every "Sign in with Google" button, every third-party API integration, and most modern single sign-on (SSO) stack sits on OAuth 2.0 — a framework for delegated authorization — and often OpenID Connect (OIDC), which adds standardized authentication and identity claims. This guide explains what each piece does, walks through the authorization code flow with PKCE (the default for browser and mobile apps today), and lists the mistakes that turn a convenience feature into a security hole.

Authorization is not authentication

The most common confusion: OAuth 2.0 answers "may this app act on my behalf?" It does not, by itself, answer "who is this person?" When you approve a photo editor to access your cloud drive, OAuth issues an access token the editor sends to the drive API. The token proves the drive owner granted a specific scope of access; it is not a general-purpose login badge.

OpenID Connect is a thin identity layer on top of OAuth 2.0. An OIDC provider (Google, Auth0, Okta, Azure AD, etc.) returns an ID token — a signed JWT containing claims like sub (subject user id), email, and iss (issuer). Your app verifies that JWT, then creates a local session. OIDC uses OAuth's transport; the openid scope triggers identity artifacts alongside (or instead of) API access tokens.

Wallet-based sign-in on blockchains is a different model: the user proves control of a keypair by signing a message, not by delegating API access through a central issuer. See connecting a Solana wallet to a dApp for that path. OAuth and wallets solve overlapping UX problems with different trust roots.

Roles in an OAuth deployment

Role Typical example Responsibility
Resource owner You, the human user Grants or denies access to your data
Client Web app, mobile app, CLI tool Requests authorization; stores tokens securely server-side or in OS vaults
Authorization server accounts.google.com, login.microsoftonline.com Shows consent UI; issues authorization codes and tokens
Resource server Gmail API, GitHub REST API Validates access tokens; returns protected resources

In pure OIDC login (no third-party API), the authorization server and the "resource" of identity claims are often the same provider. The ID token is the payload your app cares about.

The authorization code flow with PKCE

Early OAuth allowed public clients (SPAs, mobile apps without a confidential backend) to receive access tokens directly in the browser — a pattern now discouraged because any script on the page could steal them. Today's best practice for user-facing apps is the authorization code flow with PKCE (Proof Key for Code Exchange, RFC 7636).

  1. Generate PKCE verifier + challenge. The client creates a random code_verifier and sends a hashed code_challenge when starting the flow.
  2. Redirect to the authorization server. The user sees a consent screen listing requested scopes (e.g. openid profile email).
  3. User approves. The browser returns to your registered redirect URI with a short-lived authorization code in the query string — not the access token.
  4. Back-channel token exchange. Your server (or, for SPAs with caution, the client) POSTs the code plus the original code_verifier to the token endpoint. PKCE proves the same party that started the flow finishes it, blocking authorization code interception attacks.
  5. Receive tokens. Response includes an access token (for APIs), often a refresh token (for long-lived sessions, if allowed), and for OIDC an ID token JWT.

Implicit flow and password grant are legacy. Do not build new products on them. Machine-to-machine integrations use the client credentials grant: no user, just a confidential client id + secret exchanging for an access token scoped to service APIs.

Scopes, tokens, and what to verify

Scopes

Scopes are strings the client requests and the user approves. They should follow the principle of least privilege: ask only for what you need right now. OIDC baseline scopes:

Provider-specific API scopes (e.g. https://www.googleapis.com/auth/drive.readonly) gate access to resource servers. A login-only app should not request drive scopes "just in case."

Access tokens

Opaque or JWT-formatted bearer credentials sent in the Authorization header. Resource servers validate issuer, signature (if JWT), expiry, audience, and that the token includes the scope required for the endpoint. Treat access tokens like passwords in logs — never embed them in URLs or client-side analytics.

ID tokens

OIDC ID tokens are JWTs meant for the client application, not for calling arbitrary third-party APIs. Verify:

Do not use ID token claims alone for high-assurance authorization on your backend without also checking server-side session state or fetching userinfo with a valid access token when fresh attributes matter.

Refresh tokens

Refresh tokens obtain new access tokens without re-prompting the user. Store them server-side in encrypted storage, rotate on use when the provider supports it, and revoke on logout. Public clients (mobile) should use OS secure enclaves; browser SPAs generally should not hold long-lived refresh tokens without a backend-for-frontend (BFF) pattern.

Redirect URIs, clients, and common attacks

Misconfigured OAuth causes more breaches than novel cryptography breaks. Harden these surfaces:

Phishing clones fake consent screens. Train users to check the browser address bar on the real issuer domain — the same habit that matters for wallet security and approving transactions only on sites you trust.

OIDC discovery and userinfo

OIDC providers publish metadata at /.well-known/openid-configuration. That JSON document lists authorization_endpoint, token_endpoint, jwks_uri, supported scopes, and response types. Libraries (Auth.js, Passport, Spring Security, etc.) consume discovery so you do not hard-code vendor URLs.

The UserInfo endpoint returns claims about the authenticated user when called with a valid access token bearing the openid scope. ID tokens carry a snapshot at login time; userinfo can reflect changes (email verified later, name updated). For many apps, verifying the ID token once at callback plus storing sub in your session database is enough.

Passwordless alternatives are gaining ground: WebAuthn passkeys bind credentials to origin and device, reducing shared-password risk. Read passkeys and WebAuthn explained for how FIDO2 differs from bearer-token delegation — the two can coexist (OIDC for federation, passkeys for phishing-resistant factors).

Implementation checklist

Related reading