Skip to content

Authentication and Authorization

Authentication and Authorization

Authentication (AuthN) answers: "Who are you?"
Authorization (AuthZ) answers: "What are you allowed to do?"
Identity mistakes cause serious security AND usability issues, so adopt simple, well-understood patterns over homegrown crypto.


1. Core Terms

Term Meaning
Identity Provider (IdP) System that proves identity (Google, Auth0, Okta)
Session Server-side record binding a user to state (expires, revocable)
Token Signed blob the server trusts (JWT) or opaque ID referencing state
Claims Attributes about the user (email, roles, exp) inside a token
RBAC Role-Based Access Control (roles → permissions)
ABAC Attribute-Based (conditions on user/resource)
MFA Multi-Factor Auth (password + OTP, etc.)

2. Choosing an Approach

Scenario Recommended Pattern Why
Classic web app (same domain) Server session + secure httpOnly cookie Simple, mitigates XSS token theft
SPA + API (same top-level domain) httpOnly cookie (SameSite=Lax/Strict) Avoid localStorage token exposure
Mobile app / public API Short-lived JWT + refresh token rotation Scales across native clients
Third-party integrations OAuth2 (authorization code + PKCE) Delegated permission & consent

Avoid rolling custom password hashing; use battle-tested libs (bcrypt/argon2) and a framework or managed IdP when possible.


3. Session vs JWT (Trade-offs)

Aspect Server Session Stateless JWT
Revocation Easy (delete server record) Hard (need blacklist or short TTL)
Storage Server memory/DB Client stores token (header/cookie)
Size Tiny cookie ID JWT can grow (claims)
Rotation Not required Refresh flow required
Simplicity Higher (framework support) More moving parts

Rule of thumb: Start with server sessions unless you truly require stateless scale across many services without shared session storage.


Attribute Purpose
httpOnly JS cannot read (mitigate XSS theft)
Secure Only sent over HTTPS
SameSite=Lax/Strict CSRF mitigation
Path=/api Scope cookie to API route if desired
Short TTL + rotation Limits window of stolen cookie usefulness

Still add CSRF protection (synchronizer token or double-submit cookie) if performing state-changing requests with automatic cookies.


5. OAuth2 + OIDC Simplified

OpenID Connect = OAuth2 + identity layer (ID token with user claims).

sequenceDiagram
    participant U as User Browser
    participant SPA as Frontend (SPA)
    participant IDP as Identity Provider
    participant API as Backend API

    U->>SPA: Navigate /login
    SPA->>IDP: Authorization Code + PKCE (redirect)
    IDP-->>U: Login form
    U->>IDP: Credentials
    IDP-->>SPA: Redirect with code
    SPA->>IDP: Exchange code + verifier
    IDP-->>SPA: ID Token + Access Token (JSON)
    SPA->>API: Request with Access Token (Bearer)
    API-->>SPA: Data (after token validation)

Token Types

Token Purpose Audience
ID Token (JWT) Authenticates user to client Client (not API)
Access Token Authorization for APIs API (resource server)
Refresh Token Obtain new access token Authorization server

6. Authorization Models

  1. RBAC: Users have roles (admin, editor); roles map to permissions. Simple matrix.
  2. ABAC: Policies evaluate attributes (user.department == resource.department).
  3. ReBAC: Relationship-based (e.g., user is MEMBER of project). Systems like Google Zanzibar model this.

Start with RBAC (YAML / table mapping). Introduce attribute/policy checks only when necessary.

Example (pseudo policy):

roles:
    admin:
        - user.read
        - user.write
        - project.delete
    member:
        - project.read
        - project.update_own


  1. User submits credentials to /login.
  2. Server validates and creates session row {session_id, user_id, expires_at}.
  3. Server sets cookie: Set-Cookie: sid=<opaque>; HttpOnly; Secure; SameSite=Lax; Path=/.
  4. Browser automatically sends cookie on subsequent API calls.
  5. Middleware loads session → attaches req.user.
  6. Authorization layer checks role/permissions before handler.
  7. Logout: server deletes session row + set cookie expired.

8. JWT Validation Checklist

Step Description
Verify signature Use correct algorithm (e.g., RS256) & public key
Check exp & nbf Reject expired / not-yet-valid tokens
Enforce audience (aud) Ensure token meant for this API
Enforce issuer (iss) Matches your IdP domain
Minimal claims Include only required data (avoid PII)

Never trust client-provided roles without verifying signature.


9. Refresh Token Rotation (JWT)

Store refresh tokens securely (httpOnly cookie or secure storage on mobile). On usage: 1. Validate existing refresh token.
2. Issue new access + refresh tokens.
3. Invalidate old refresh token (prevent replay).
4. Detect reuse: if a previously invalidated refresh token appears, revoke entire session.


10. MFA Quick Add

  1. User enrolls: generate TOTP secret (QR code).
  2. Store hashed secret server-side.
  3. On login success (password valid) require 6-digit TOTP.
  4. Lock after N failures; backup recovery codes for loss scenario.

11. Common Pitfalls & Fixes

Pitfall Risk Fix
Storing JWT in localStorage XSS theft Use httpOnly cookie + CSRF defenses
Long-lived access tokens Hard revocation Use short TTL + refresh rotation
Leaking detailed error messages Account enumeration Generic auth error msg
Password hashing with SHA256 Easy cracking Use bcrypt/argon2 with salt
Orphaned sessions Privilege persistence Expire & cleanup cron

12. Example Express Middleware (Conceptual)

// Pseudo-code only
function authSession(req, res, next) {
    const sid = req.cookies.sid;
    if (!sid) return res.status(401).json({ error: 'UNAUTHENTICATED' });
    const session = db.sessions.find(sid);
    if (!session || session.expires_at < Date.now()) {
        return res.status(401).json({ error: 'SESSION_EXPIRED' });
    }
    req.user = db.users.find(session.user_id);
    next();
}

function requireRole(role) {
    return (req, res, next) => {
        if (!req.user?.roles.includes(role)) {
            return res.status(403).json({ error: 'FORBIDDEN' });
        }
        next();
    }
}

13. Performance & Caching Considerations

  • Avoid storing excessive claims in JWT (size impacts header bloat).
  • Use a short in-memory cache for user roles/permissions if DB lookups are frequent.
  • Invalidate cache on role changes to prevent stale rights.

14. Checklist

  • Auth method selected (session vs JWT) & documented
  • Secure cookie attributes set (if cookies used)
  • Password hashing library (bcrypt/argon2) configured
  • CSRF protection for state-changing routes
  • Role/permission matrix documented in repo
  • Token/Session expiration + rotation policy
  • MFA (optional) plan or deferred rationale documented
  • Central logout / revocation path works
  • Minimal personally identifiable info in tokens
  • Logging of auth events (login success/fail, role changes)

15. Further Resources