All articles
Backend from First Principles

Authentication & Authorization: A Mental Model for Backend Engineers

Every backend system faces two questions: 'Who are you?' and 'What are you allowed to do?' Here's the mental model that makes sessions, JWTs, OAuth, and RBAC click.

2026-01-2012 min read

Every backend system eventually faces the same two questions: "Who are you?" and "What are you allowed to do?" Get these wrong, and nothing else you build matters.

This is the fourth fundamental I'm revisiting in my backend-from-first-principles series. Authentication and authorization are words every developer hears daily, but the line between them blurs more often than it should. Let's fix that with a mental model that makes the entire system click.


The Two Gates: A Mental Model

Imagine walking into a corporate office building.

Gate 1 — Authentication: The security desk at the entrance. You show your ID badge. The guard checks it and confirms: "Yes, you are who you claim to be." This is identity verification. It answers "Who are you?"

Gate 2 — Authorization: The elevator access system. Your badge says "Engineer — Floor 3." You can reach Floor 3, but not Floor 7 (Executive Suite) or the basement (Server Room). This is permission enforcement. It answers "What are you allowed to do?"

Critical ordering: authentication always comes first. You can't check someone's permissions until you know who they are. The building won't let you scan for floor access until the front desk has verified your identity.

In HTTP terms:

  • Authentication failure = 401 Unauthorized — "I don't know who you are."
  • Authorization failure = 403 Forbidden — "I know who you are, but you can't do this."

Every auth system you'll ever encounter is just these two gates with different mechanisms behind them.


The Fundamental Problem: HTTP Has Amnesia

Remember from the HTTP mental model: the server has perfect amnesia. Every request starts from scratch. The server doesn't remember that you logged in two seconds ago.

But every real application needs memory. Amazon needs to remember your cart. Gmail needs to know whose inbox to show. A banking app absolutely cannot forget who's making a transfer.

So the core engineering problem is: how do you give a stateless protocol a persistent identity?

The answer has evolved over decades, and understanding that evolution makes the modern tools intuitive.


The Building Blocks: Sessions, JWTs, and Cookies

Before diving into authentication types, you need to understand the three primitives. Think of these as the raw materials every auth system is built from.

Sessions — The Coat-Check Ticket

Mental model: you walk into a restaurant and hand your coat to the attendant. They give you a small numbered ticket. The ticket itself is meaningless — it's just a reference number. Your actual coat (your identity data) stays with the restaurant.

That's exactly how sessions work:

  1. User logs in with credentials
  2. Server validates, creates a session record containing the user's identity data
  3. Server stores this record in a fast data store (typically Redis — an in-memory store)
  4. Server sends back just the Session ID (the ticket number) to the client
  5. Client includes this Session ID in every future request
  6. Server looks up the Session ID in Redis, finds the identity, and proceeds

The power: the server has full control. Need to force-logout a user? Delete the session record. Need to see all active sessions? Query Redis. Need to ban someone immediately? Gone in one operation.

The cost: every single request requires a round-trip to the session store. Scale to millions of users and that store becomes critical infrastructure.

JWTs — The Signed Wristband

Mental model: instead of a coat-check ticket, imagine a festival wristband that has your name, your access level, and an expiry date printed directly on it — plus a holographic seal that only the festival organizers can produce.

Anyone at any gate can read the wristband and verify the holographic seal without calling headquarters. The information travels with you.

That's a JWT (JSON Web Token). It has three parts, all base64-encoded:

  1. Header — Metadata: which algorithm signed this token
  2. Payload — The actual claims: user ID (sub), role, issued time (iat), expiry (exp)
  3. Signature — A cryptographic seal created with a secret key only the server holds

Verification flow: the server takes the header + payload, re-signs them with its secret key, and checks if the resulting signature matches the one in the token. If it does — the token is authentic and untampered. No database lookup required.

The power: stateless verification. Any server in a cluster of 50 can verify the token independently. No shared session store, no synchronization, no single point of failure.

The cost: you can't un-ring the bell. Once a JWT is issued, it's valid until it expires. You can't revoke a single token without either maintaining a blacklist (defeating the stateless benefit) or rotating the secret key (logging out every user).

Cookies — The Automatic Delivery Envelope

Cookies aren't an authentication mechanism — they're a transport mechanism. Think of them as an envelope the browser automatically attaches to every request going to a specific domain.

The server says: "Here, store this Session ID (or JWT) in a cookie." The browser says: "Got it — I'll automatically include it in every future request to your domain."

Key security flags every backend engineer must set:

  • HttpOnly — JavaScript cannot read this cookie (prevents XSS theft)
  • Secure — Only sent over HTTPS
  • SameSite — Controls cross-site sending (prevents CSRF)

The Four Authentication Patterns

Every auth system in production is a combination of these four patterns. Each exists because it solves a specific problem the others can't.

1. Stateful Authentication (Sessions + Redis)

The mental model: A hotel front desk with a guest registry.

Login → Server creates session in Redis → Sends Session ID via HttpOnly cookie
                                                    ↓
Next request → Browser sends cookie → Server looks up Session ID in Redis → Identity found → Proceed

When to use: Traditional web applications where you need instant control — force logout, session monitoring, rate limiting per session.

The tradeoff: Control vs. scale. You get absolute power over every session, but you pay with infrastructure complexity.

2. Stateless Authentication (JWTs)

The mental model: A passport with a tamper-proof seal.

Login → Server signs JWT with secret key → Sends JWT to client
                                                    ↓
Next request → Client sends JWT in Authorization header → Server verifies signature → Claims trusted → Proceed

When to use: Microservices, mobile apps, and any distributed system where multiple servers need to verify identity independently.

The tradeoff: Scale vs. control. You get infinite scalability, but you lose the ability to instantly revoke access.

3. API Key Authentication

The mental model: A building access card for robots.

API keys aren't for humans — they're for machine-to-machine communication. When your backend calls OpenAI's API, or Stripe's payment endpoint, or a weather service — there's no human logging in. A pre-generated cryptographic string proves identity.

GET /api/completions
x-api-key: sk-abc123def456...

When to use: Server-to-server integrations, third-party API consumption, automated pipelines.

The tradeoff: Simplicity vs. granularity. Easy to implement, but API keys typically represent an application, not a specific user.

4. OAuth 2.0 & OpenID Connect

The mental model: A valet parking ticket. You give the valet your car keys, but only for the purpose of parking — not for opening the glove box, not for driving across the country. Limited, scoped delegation.

The problem OAuth solved: In the early web, if a travel app wanted to scan your Gmail contacts, you'd literally give it your Gmail password. The app had full access to your entire account. You couldn't revoke access without changing your password (which broke every other app too).

OAuth 2.0 solved this with delegated authorization. Instead of sharing passwords, the user authorizes a specific app to get a token with specific, limited permissions (scopes) from an Authorization Server.

But OAuth 2.0 only handles authorization — "this app can read your contacts." It doesn't tell the app who you are.

OpenID Connect (OIDC) was built on top to add authentication. It introduced the ID Token — a JWT containing verified user identity (name, email, profile picture). This is what powers every "Sign in with Google" button you've ever clicked.

The Authorization Code Flow (the most common and secure)

1. User clicks "Sign in with Google" on your app
2. Your app redirects to Google's auth server with: client_id, redirect_uri, scopes, state
3. User logs into Google and grants permission
4. Google redirects back to your app with an authorization code
5. Your backend exchanges that code (+ client_secret) for tokens
6. Google returns: access_token + refresh_token + id_token (if OIDC)
7. Your app uses the access_token to call Google's APIs on behalf of the user

Key security details:

  • The state parameter prevents CSRF attacks — your app generates a random string, sends it to Google, and verifies it comes back unchanged
  • The code → token exchange happens server-side, so the access token never touches the browser
  • PKCE (Proof Key for Code Exchange) adds an extra layer for public clients (mobile apps, SPAs) where a client_secret can't be safely stored

The Decision Map

ScenarioUse ThisWhy
Traditional web app, need instant logoutSessions (stateful)Full control, easy revocation
Microservices, distributed systemsJWT (stateless)No shared store, any server verifies
Server-to-server API callsAPI KeysSimple, no user context needed
Third-party login, delegated accessOAuth 2.0 + OIDCScoped permissions, no password sharing
Production realityHybridMost systems combine 2-3 of these

The production reality is that almost no system uses just one. A typical modern app might use OAuth/OIDC for social login, issue a JWT as the access token, store a session in Redis for revocation control, and deliver it all via an HttpOnly cookie. Four patterns working together.


The Token Lifecycle: Access + Refresh

One pattern that cuts across all token-based systems: the two-token strategy.

The mental model: Think of it like a day pass and an annual membership card.

  • Access token — Short-lived (minutes to hours). Used for every API call. If stolen, the damage window is small.
  • Refresh token — Long-lived (days to weeks). Stored securely. Used only to request a new access token when the current one expires.
Login → Server issues access token (15 min) + refresh token (7 days)
          ↓
API calls use access token → Works until expired
          ↓
Token expires → Client sends refresh token → Server issues new access token
          ↓
Refresh token expires → User must log in again

This gives you the best of both worlds: short-lived tokens limit breach damage, while refresh tokens prevent the user from logging in every 15 minutes.


Authorization: After Identity Comes Permission

Once you know who someone is, you need to decide what they can do. The most common model is Role-Based Access Control (RBAC).

The mental model: A hospital. Everyone in the building has been authenticated (they have a badge). But a nurse, a surgeon, and a janitor can access very different rooms.

User: { id: 123, role: "editor" }

Roles → Permissions:
  admin    → [read, write, delete, manage_users]
  editor   → [read, write]
  viewer   → [read]

During each request, the server extracts the role from the token (JWT payload or session data), checks it against the required permission for that endpoint, and either proceeds or returns 403 Forbidden.


Security: The Details That Save You

Authentication is one of the highest-value targets for attackers. Two specific practices separate secure systems from vulnerable ones.

1. Generic Error Messages

When a login fails, never tell the attacker what went wrong specifically.

  • Bad: "User not found" — Attacker now knows this email isn't registered. They try another.
  • Bad: "Incorrect password" — Attacker now knows the email IS registered. They focus on cracking that password.
  • Good: "Authentication failed" — Attacker learns nothing.

Specific error messages are information leaks that shrink the attacker's search space. A generic message gives them zero signal.

2. Timing Attacks — The Invisible Side Channel

This one is subtle and powerful. Here's how it works:

A standard string comparison (===) checks character by character and stops at the first mismatch. If the username doesn't exist, the server fails instantly. If the username exists but the password is wrong, the server takes longer because it runs the heavy hashing algorithm before failing.

An attacker measures these response times across thousands of requests. A consistently slower response means "the username exists." They've just enumerated your user database without ever seeing it.

For token/key comparisons, it's even worse. Comparing "secret123" vs "axxxxxxxx" fails faster (first character mismatch) than "secret123" vs "secret999" (fails at character 7). An attacker can deduce the correct token character by character — reducing a brute-force attack from billions of attempts to a few hundred.

The fix: Use constant-time comparison functions that take exactly the same time regardless of where the mismatch occurs:

Node.js  → crypto.timingSafeEqual()
Python   → hmac.compare_digest()
Go       → subtle.ConstantTimeCompare()
Rust     → subtle::ConstantTimeEq

Plus: add a consistent response delay (e.g., 200ms floor) so timing variations in business logic can't leak information either.


The Mental Model Summary

Carry this forward:

  • Authentication = Gate 1: "Who are you?" — Verify identity before anything else
  • Authorization = Gate 2: "What can you do?" — Check permissions after identity is established
  • Sessions = Coat-check ticket. Server holds the data. Full control, but needs infrastructure.
  • JWT = Signed wristband. Data travels with you. Infinitely scalable, but hard to revoke.
  • Cookies = The automatic envelope. A transport mechanism, not an auth mechanism.
  • API Keys = Robot access cards. Machine-to-machine, no human in the loop.
  • OAuth 2.0 = Valet parking ticket. Delegated, scoped authorization without sharing passwords.
  • OIDC = OAuth + identity. The "Sign in with Google" layer.
  • RBAC = Hospital badges. Same building, different room access.
  • Security = Never leak information. Generic errors. Constant-time comparisons. Short-lived tokens.

Authentication and authorization aren't features you bolt on at the end. They're architectural decisions that shape your entire system. The mental model of two sequential gates — first prove who you are, then check what you can do — is the foundation everything else builds on.


This is part 4 of my "Backend from First Principles" series. Everything in this article comes from what I learned watching the Backend from First Principles playlist by Sriniously — specifically the video "Authentication and Authorization for backend engineers." I'm not claiming original research. This is me taking what I studied, internalizing it, and presenting it as a mental model that made the concepts stick for me. If it helps you too, even better.

Playlist: https://youtube.com/playlist?list=PLui3EUkuMTPgZcV0QhQrOcwMPcBCcd_Q1

Follow along for the next one.

#BackendEngineering #Authentication #Authorization #OAuth #JWT #SoftwareEngineering #SystemDesign #LearningInPublic