caduh

Authentication vs. Authorization — what’s the difference and how they fit together

4 min read

A clear, practical comparison: authN proves who you are; authZ decides what you can do. Learn identities, sessions/tokens (OIDC/OAuth2), roles/scopes/permissions, and common pitfalls.

TL;DR

  • Authentication (authN): prove who the user or client is (passwords, MFA, WebAuthn, SSO/OIDC). Produces a session or token that asserts identity.
  • Authorization (authZ): decide what the authenticated principal may do (roles/scopes/permissions/policies). Evaluated per request/action.
  • OAuth = delegated authorization (access to APIs). OIDC = OAuth + an identity layer (ID Token) for login.
  • For web apps: prefer HTTP‑only secure cookies for sessions; for APIs: Bearer tokens (JWT or opaque) with audience & scope checks.
  • Separate identity from policy. Enforce authZ at the edge (gateway/middleware) and in service (defense in depth).

The mental model (in one minute)

User → (Authentication) → Identity established (session / tokens)
                      ↓
                 (Authorization) → Is this principal allowed to DO X on RESOURCE Y?
  • AuthN answers: “Are you really Alice?” (and keeps you logged in).
  • AuthZ answers: “Given you are Alice, can you DELETE /posts/42?”

Authentication (prove identity)

Factors & methods

  • Something you know (password, passphrase) — protect with hashing (bcrypt/argon2) & rate limits.
  • Something you have (TOTP app, security key, device) — MFA.
  • Something you are (biometrics) — usually as a device unlock factor.
  • WebAuthn / Passkeys: phishing‑resistant public‑key login.
  • SSO with OIDC/SAML: delegate login to an Identity Provider (IdP).

Sessions & tokens

  • Cookie session (server‑side or signed): best for browser apps; set HttpOnly; Secure; SameSite=Lax/None. Protect from CSRF (SameSite + anti‑CSRF tokens) and XSS (no JS access).
  • Bearer tokens for APIs:
    • Access Token (often JWT) → presented to API.
    • ID Token (OIDC JWT) → for the client app to learn who logged in.
    • Validate issuer (iss), audience (aud), expiry (exp), signature (JWKS).
    • Prefer Authorization Code + PKCE for web/mobile; avoid deprecated implicit flow.

Minimal login flow (OIDC)

  1. App redirects to IdP (Auth Code + PKCE).
  2. User authenticates; IdP redirects back with code.
  3. App exchanges code for Access Token (+ ID Token).
  4. App stores session (cookie) or token; calls APIs with Authorization: Bearer <access_token>.

Authorization (enforce permissions)

Building blocks

  • Roles (RBAC): admin, editor, viewer.
  • Scopes (OAuth): capabilities granted to a client (e.g., payments:write).
  • Permissions/Policies: fine‑grained rules; may include attributes (ABAC) or relationships (ReBAC).

Where to enforce

  • Edge (gateway/middleware): reject obviously unauthorized calls early (no/invalid token; missing scopes).
  • Inside services: enforce resource‑level rules (ownership, tenant). Defense in depth.

Tiny examples

Node/Express: verify JWT + scopes

import jwksRsa from "jwks-rsa";
import jwt from "express-jwt";

const checkJwt = jwt.expressjwt({
  secret: jwksRsa.expressJwtSecret({ jwksUri: "https://idp.example.com/.well-known/jwks.json" }),
  audience: "api://orders",
  issuer: "https://idp.example.com/",
  algorithms: ["RS256"]
});

function requireScope(s) {
  return (req,res,next) => req.auth?.scope?.split(" ").includes(s)
    ? next()
    : res.status(403).json({ error: "insufficient_scope" });
}

app.get("/orders/:id", checkJwt, requireScope("orders:read"), handler);

Policy example (OPA/Rego) — owner or admin

package authz

default allow = false
allow {
  input.user.role == "admin"
}
allow {
  input.resource.owner_id == input.user.sub
  input.action == "read"
}

OAuth vs OIDC (often confused)

  • OAuth 2.x / 2.1: framework for delegated authorization to APIs. Access Tokens are for resource servers.
  • OpenID Connect (OIDC): identity layer on top of OAuth providing an ID Token and a standard user info endpoint → used for login.
  • Don’t use OAuth alone for login. If you need “who is the user?”, use OIDC.

Roles, scopes, and permissions together

  • Client → API: check scopes on the token (e.g., invoices:read).
  • User → Resource: check permissions/policies (e.g., “user is owner of invoice 123” or “has role billing-admin”).
  • Keep role inflation in check; favor small, composable permissions grouped into roles.

Common mistakes & fast fixes

| Mistake | Why it’s bad | Fix | |---|---|---| | Treating OAuth as login | No stable identity; phishing risk | Use OIDC; validate ID Token | | Storing tokens in localStorage | XSS steals tokens | Prefer HTTP‑only cookies or secure memory; never expose refresh tokens to JS in SPAs—use a BFF pattern | | Not checking aud/iss | Token reuse against other APIs | Validate issuer and audience at the API | | Using wildcard CORS with credentials | Browser blocks; leaks | Use specific origins + Allow‑Credentials: true | | Missing CSRF protection with cookies | Cross‑site requests abuse sessions | Use SameSite, anti‑CSRF tokens, and double‑submit patterns | | Overloading JWT as a DB | Huge tokens, stale claims | Keep tokens small; fetch fresh claims/server checks when needed | | No token rotation/expiry | Long‑lived compromise | Short TTL access tokens + refresh rotation | | AuthZ only at gateway | Bypass via internal paths | Enforce in service too (defense in depth) |


Quick checklist

  • [ ] Pick OIDC for login; prefer Auth Code + PKCE.
  • [ ] For browsers: HTTP‑only, Secure cookies; add CSRF protection.
  • [ ] For APIs: check iss/aud/exp/signature and scopes.
  • [ ] Implement resource‑level authorization in services.
  • [ ] Model roles/scopes/permissions clearly; log deny decisions.
  • [ ] Protect tokens/secrets; rotate refresh tokens; monitor anomalies.

One‑minute adoption plan

  1. Write down your resources and actions (e.g., orders: read/write).
  2. Choose OIDC IdP; implement Auth Code + PKCE login.
  3. APIs: add JWT validation and scope checks at the edge; add resource checks inside handlers.
  4. Store sessions in HTTP‑only cookies (web) or use Bearer tokens (native/CLI).
  5. Add CSRF, CORS, and logging/metrics for allow/deny.
  6. Review token TTLs and introduce refresh rotation & revocation where needed.