
OAuth
OAuth is about delegated access: one application gets limited permission to act on behalf of a user without ever seeing the user’s password. Most production failures are flow-selection and validation problems, not crypto problems. Use the authorization code flow with PKCE by default. Everything else starts from needing a very good reason.
Minimal Example
const authUrl = new URL("https://auth.example.com/authorize");
authUrl.search = new URLSearchParams({
client_id: env.CLIENT_ID,
response_type: "code",
redirect_uri: env.REDIRECT_URI,
scope: "openid profile payments.read",
state,
code_challenge,
code_challenge_method: "S256",
}).toString(); What It Solves
- Delegates user-approved access without sharing passwords with third-party clients.
- Constrains tokens by scope, audience, expiry, and refresh behavior.
- Separates authentication and authorization concerns across identity providers and APIs.
Failure Modes
- Using the wrong grant type because it looks simpler in a tutorial.
- Skipping state and redirect URI validation, opening the door to code interception or login CSRF.
- Treating refresh tokens like static secrets with no rotation or storage discipline.
Production Checklist
- Default to authorization code plus PKCE and keep redirect URIs exact.
- Validate state, issuer, audience, and token expiry on every callback and API call.
- Rotate refresh tokens where supported and store them with stronger controls than access tokens.
Closing
OAuth is manageable when the flow is narrow and the validation is strict. Complexity usually comes from optionality, not from the core protocol.









