OAuth guides
Understanding the OAuth 2.0 authorization code flow
How browsers, your app, and an authorization server cooperate to let a user sign in without handing your app their password.
OAuth 2.0 is a framework for delegated access: a user can grant your application limited rights to act on their behalf, usually without sharing their password with you. The authorization code grant is the default choice for server-side web apps and many mobile or desktop apps that can open a system browser.
Roles in the system
Four parties show up in almost every integration:
- Resource owner — the person signing in.
- Client — your application (web app, API, mobile app).
- Authorization server — authenticates the user and issues tokens (often the same company as the identity provider).
- Resource server — APIs that accept access tokens (Google Calendar, your own backend, etc.).
Your client never sees the user's password for the authorization server. It only receives tokens after the user has approved access on a page the authorization server controls.
Step-by-step: authorization code
A typical login with redirect looks like this:
- Your app sends the user's browser to the authorization endpoint with parameters such as
client_id,redirect_uri,response_type=code,scope, and oftenstate(andcode_challengewhen using PKCE). - The user signs in and approves the requested scopes on the authorization server's UI.
- The server redirects back to your
redirect_uriwith a short-lived authorization code in the query string. - Your backend (or a confidential client) exchanges that code at the token endpoint, sending
client_id,client_secret(if applicable),redirect_uri, and the code. - The token endpoint returns an access token and often a refresh token. If you use OpenID Connect, you also receive an
id_token. - Your app calls APIs with the access token until it expires, then refreshes if you stored a refresh token.
The code is useless on its own to JavaScript on a public page: exchanging it requires your client secret (for confidential clients) or PKCE verification (for public clients). That split is intentional.
Access tokens vs refresh tokens
An access token is presented to resource servers, usually in an Authorization: Bearer header. It should be treated as a secret in logs and error pages. Lifetimes are often short (minutes to an hour).
A refresh token is only sent to the token endpoint to obtain new access tokens. Store it securely (encrypted at rest, never in localStorage for browser-only SPAs unless you accept the risk). Many providers rotate refresh tokens on each use.
Why redirect URI registration matters
You must pre-register every redirect URI with the authorization server. At redirect time, the server only sends the code to URIs on that allow list. That blocks an attacker who tricks a user into authorizing a code meant for the attacker's redirect endpoint.
Use HTTPS in production, avoid wildcards when the provider allows exact URIs, and give separate OAuth clients for local development and production rather than one client with dozens of redirect URIs.
Practical integration notes
Framework middleware (NextAuth, Auth.js, Passport, ASP.NET OpenIdConnect, etc.) implements this flow for you. When learning or debugging, it still helps to read the raw authorize and token requests in network tools.
Environment variables usually mirror production names: issuer or authority URL, client ID, client secret, and callback URL. Keeping those names stable while you point the issuer at a mock IdP during development (HTTPS issuer, localhost redirect) reduces surprise when you switch credentials for staging or production.
Related guides
OpenID Connect: identity on top of OAuth
What OIDC adds to OAuth 2.0, how ID tokens differ from access tokens, and how discovery documents tie it together.
PKCE, state, and redirect URIs for safer OAuth apps
Core defenses against authorization code interception, CSRF on the callback, and redirect manipulation.
Local OAuth development: localhost callbacks and mock issuers
Run your app on localhost while OAuth discovery, consent, and tokens come from a mock issuer. Loopback redirect URIs, stable env vars, and common pitfalls.