Skip to content

Authentication

TL;DR

Authentication answers "who are you?" and authorization answers "what can you do?" The three main auth mechanisms are session-based auth (server stores state), API keys (long random strings for server-to-server use), and JWTs (stateless tokens encoding user info). OAuth 2.0 combines API keys and JWTs into the industry standard for developer access. Default to mentioning "JWT-based auth" in interviews and move on unless asked to go deeper.

Authentication vs. Authorization — They're Not the Same Thing

Think of going through airport security.

Your passport is authentication. It proves who you are. The security officer checks the photo, verifies the document is real, and confirms your identity.

Your boarding pass is authorization. It proves what you're allowed to do. You might be authenticated as John Smith, but that doesn't mean you can board any flight. Your boarding pass says you can board Flight 742 to Tokyo, in seat 14C, boarding group 3.

In API terms:

Concept Question Example
Authentication (AuthN) Who are you? "I am user 42, alice@example.com"
Authorization (AuthZ) What can you do? "User 42 can read events but cannot delete them"

Every API request goes through both checks, in that order. First, authenticate the request — figure out who's calling. Then, authorize it — decide if that user is allowed to perform this action.

Why does this matter for interviews? When you design an API, interviewers expect you to mention both. Saying "this endpoint requires authentication" is a start. Saying "this endpoint requires authentication AND only users with the admin role can access it" shows you understand the full picture.

Authentication methods comparison


Mechanism 1: Session-Based Authentication

This is the oldest and most straightforward approach. It works like a coat check at a restaurant.

How It Works

  1. Client sends credentials: POST /login with email and password.
  2. Server verifies: Checks credentials against the database.
  3. Server creates a session: Stores a session object (user ID, creation time, expiration) in memory or a session store (like Redis).
  4. Server sets a cookie: Returns a Set-Cookie header with a session ID — a random string like sess_abc123xyz.
  5. Client sends cookie on every request: The browser automatically includes it in every subsequent request.
  6. Server looks up the session: On each request, the server takes the session ID from the cookie, looks it up in the session store, and retrieves the user's identity.
Client                          Server                    Session Store
  |                               |                           |
  |-- POST /login (email, pass) ->|                           |
  |                               |-- verify credentials ---->|
  |                               |<-- user found ------------|
  |                               |-- store session ---------->|
  |<-- Set-Cookie: sess_abc123 ---|                           |
  |                               |                           |
  |-- GET /events                 |                           |
  |   Cookie: sess_abc123 ------->|                           |
  |                               |-- lookup sess_abc123 ---->|
  |                               |<-- user_id: 42 ----------|
  |<-- 200 OK (events data) ------|                           |

Pros and Cons

Pros Cons
Simple to implement Server must store state (session store)
Server has full control — revoke a session instantly by deleting it Doesn't scale horizontally without sticky sessions or a shared session store (Redis)
Cookie handling is built into browsers — no client-side code needed Cookies don't work well for mobile apps or third-party API access
Battle-tested and well-understood Cross-origin requests (CORS) with cookies get tricky

When to Use Sessions

Session-based auth is a solid choice for traditional web applications where the client is a browser, the backend is a monolith, and you need the ability to revoke sessions instantly (think "log out of all devices"). Rails, Django, and Express all have excellent session middleware.

But for APIs consumed by mobile apps, third-party developers, or distributed microservices, sessions start to creak. That's where tokens come in.


Mechanism 2: API Keys

An API key is a long, randomly generated string that identifies a client application. Think of it like a library card — it identifies who is making the request, but it doesn't carry any information about what they're allowed to do.

What API Keys Look Like

sk_live_4eC39HqLyjWDarjtT1zdp7dc
pk_test_TYooMQauvdEDq54NiTphI7jx

The sk_ and pk_ prefixes are a convention popularized by Stripe: sk = secret key (keep on your server), pk = publishable key (safe for the frontend).

How They Work

  1. Developer signs up for your API and generates a key.
  2. Key is stored in the database, associated with the developer's account.
  3. Client sends the key in the Authorization header (or sometimes as a query parameter, though that's less secure).
  4. Server looks up the key in the database and identifies the caller.
GET /api/events HTTP/1.1
Host: api.ticketmaster.com
Authorization: Bearer sk_live_4eC39HqLyjWDarjtT1zdp7dc

Pros and Cons

Pros Cons
Dead simple to implement No user context — the key identifies an application, not a user
Easy to revoke — just delete the key from the database No built-in expiration — keys live forever unless you add rotation logic
Great for server-to-server communication If leaked, attacker has full access until you manually revoke it
Easy to rate-limit per key Users shouldn't manage cryptographic strings — bad UX

When to Use API Keys

API keys are good for server-to-server communication and third-party developer access where the key stays on a backend server. Stripe, Twilio, and SendGrid all use API keys because their customers are developers writing server-side code.

API keys are bad for user-facing apps. You don't want your mobile app users copy-pasting 40-character strings. You don't want those keys sitting in a JavaScript bundle where anyone can view-source them.

Interview Tip

If an interviewer asks how third-party developers access your API, don't just say "API keys." Say "OAuth 2.0" — which combines API keys and JWTs. We'll get to that shortly.


Mechanism 3: JWT (JSON Web Tokens)

If session-based auth is a coat check (server stores your coat and gives you a ticket), a JWT is a wristband. Everything the bouncer needs to know is printed right on the wristband itself — your name, your access level, when it expires. No need to call the coat check.

What a JWT Looks Like

A JWT is three Base64-encoded strings separated by dots:

eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyX2lkIjo0MiwiZW1haWwiOiJhbGljZUBleGFtcGxlLmNvbSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcxNzAyNDAwMH0.signature_here

That looks like gibberish, but it decodes to three parts:

Header — metadata about the token:

{
  "alg": "RS256",
  "typ": "JWT"
}

Payload — the actual user data (called "claims"):

{
  "user_id": 42,
  "email": "alice@example.com",
  "role": "admin",
  "exp": 1717024000
}

Signature — cryptographic proof that the token hasn't been tampered with.

How JWT Authentication Works

  1. Client sends credentials: POST /login with email and password.
  2. Server verifies: Checks credentials against the database.
  3. Server creates a JWT: Encodes the user's info into the payload, signs it with a secret key, and returns the token.
  4. Client stores the token: Typically in memory or localStorage.
  5. Client sends the token on every request: In the Authorization: Bearer <token> header.
  6. Server verifies the signature: No database lookup needed. If the signature is valid, the payload is trustworthy.
Client                          Server
  |                               |
  |-- POST /login (email, pass) ->|
  |                               |-- verify credentials against DB
  |                               |-- create JWT (sign with secret key)
  |<-- { "token": "eyJhbG..." } --|
  |                               |
  |-- GET /events                 |
  |   Authorization: Bearer eyJ.. |
  |                               |-- verify JWT signature (NO DB lookup)
  |                               |-- extract user_id: 42, role: admin
  |<-- 200 OK (events data) ------|

The Killer Feature: Stateless Verification

With sessions, the server must look up every session in a database or Redis store on every request. That's a bottleneck.

With JWTs, the server just verifies the signature. It's a CPU operation, not a database operation. This is a massive advantage in distributed systems — any service with the verification key can independently validate the token without calling a central auth service.

JWT Signing: Symmetric vs. Asymmetric

This is a detail that comes up in senior-level interviews and is worth understanding.

Symmetric signing (HS256): One shared secret key is used to both sign and verify the token. Like a shared password — anyone who can verify the token can also create fake tokens.

Auth Service: sign(payload, "my-secret-key") → token
API Service:  verify(token, "my-secret-key") → payload

Problem: Every service that needs to verify tokens must have the secret key. If any one of them is compromised, the attacker can forge tokens.

Asymmetric signing (RS256): A private key signs the token, and a public key verifies it. The public key cannot be used to create tokens.

Auth Service: sign(payload, private_key) → token
API Service:  verify(token, public_key)  → payload

Advantage: Only the auth service has the private key. Every other service gets the public key, which is safe to distribute widely. Even if an API service is compromised, the attacker can't forge tokens.

HS256 (Symmetric) RS256 (Asymmetric)
Keys One shared secret Private key (signer) + public key (verifier)
Who can create tokens? Anyone with the secret Only the auth service with the private key
Who can verify tokens? Anyone with the secret Anyone with the public key
Key distribution Must keep secret everywhere Public key is safe to distribute
Best for Single-service apps, simple setups Distributed systems, microservices
Risk if compromised Attacker can forge tokens Attacker can only read tokens, not forge them

Interview Tip

For most interview scenarios, just say "JWT signed with RS256." If asked why, explain: "Asymmetric signing means only the auth service can create tokens, but any service can verify them. It's more secure in a distributed system."

JWT Gotchas

  • JWTs can't be revoked easily. Unlike sessions, you can't just delete a JWT. It's valid until it expires. Workarounds include short expiration times (15-60 minutes) plus refresh tokens, or maintaining a token blacklist (which partially defeats the "stateless" benefit).
  • Payload is NOT encrypted. Anyone can decode a JWT and read the payload. Never put sensitive data (passwords, credit card numbers) in a JWT. The signature prevents tampering, not reading.
  • Token size. JWTs are larger than session IDs because they carry data. A session cookie might be 32 bytes; a JWT might be 800+ bytes. This adds up when sent with every request.

OAuth 2.0 — The Industry Standard

Here's where a lot of people get confused. OAuth 2.0 is not a replacement for API keys or JWTs. It's a framework that combines them.

The Common Misconception

Many developers think the choice is:

  • Option A: Use API keys
  • Option B: Use JWTs
  • Option C: Use OAuth 2.0

But that's wrong. OAuth 2.0 is Option A + Option B working together with a defined flow.

How OAuth 2.0 Works (Client Credentials Flow)

This is the flow used for server-to-server and developer API access — the most relevant flow for system design interviews.

  1. Developer registers an application and receives a Client ID (like a username) and a Client Secret (like a password). These are essentially API keys.
  2. Application sends credentials to the token endpoint: POST /oauth/token with the Client ID and Client Secret.
  3. Auth server verifies the credentials and returns a Bearer Token — which is typically a JWT.
  4. Application uses the Bearer Token for all API calls: Authorization: Bearer <jwt>.
  5. Token expires (typically 1 hour). Application requests a new token with its credentials.
Developer App                    Auth Server                 API Server
  |                                 |                           |
  |-- POST /oauth/token             |                           |
  |   client_id: abc123             |                           |
  |   client_secret: xyz789         |                           |
  |   grant_type: client_credentials|                           |
  |                                 |                           |
  |<-- { "access_token": "eyJ..",   |                           |
  |      "token_type": "Bearer",    |                           |
  |      "expires_in": 3600 }       |                           |
  |                                 |                           |
  |-- GET /api/events ----------------------------------------->|
  |   Authorization: Bearer eyJ..   |                           |
  |                                 |                   verify JWT
  |<-- 200 OK (events data) ----------------------------------------|

Why OAuth 2.0 Exists

  • API keys alone have no expiration. If leaked, they're valid forever.
  • OAuth tokens expire. Even if intercepted, they're only valid for a short time (usually 1 hour).
  • OAuth provides scoped authorization. A token can grant read:events but not write:events. API keys are typically all-or-nothing.
  • OAuth separates authentication from API access. The auth server handles credentials; the API server just verifies tokens.
  • It's the industry standard. Google, GitHub, Stripe, AWS, Slack — every major platform uses OAuth 2.0 for developer access.

Other OAuth 2.0 Flows (For Completeness)

Flow Use Case How It Works
Client Credentials Server-to-server, developer APIs Client ID + Secret → token (no user involved)
Authorization Code User-facing apps ("Login with Google") Redirect to auth provider → user consents → auth code → exchange for token
Authorization Code + PKCE Mobile/SPA apps Same as above, but with a code challenge to prevent interception
Implicit (deprecated) Was for SPAs Returned token directly in URL — insecure, replaced by PKCE

For system design interviews, Client Credentials is the most relevant. If asked about user authentication ("Login with Google"), mention the Authorization Code flow.


The Big Comparison Table

Sessions API Keys JWT OAuth 2.0
State Server stores session Server stores key Stateless (token carries data) Stateless (token is a JWT)
Scalability Needs shared session store Scales well Scales perfectly — no DB lookups Scales perfectly
Revocation Instant — delete session Instant — delete key Hard — must wait for expiry or use blacklist Token expires naturally
User context Yes (session contains user info) No (identifies app, not user) Yes (payload carries user info) Yes (JWT payload)
Expiration Configurable None by default Built-in (exp claim) Built-in (short-lived tokens)
Best for Traditional web apps (browser + monolith) Server-to-server, developer APIs Distributed systems, microservices Developer API access (the standard)
Complexity Low Low Medium Medium-High

When to Use What in Interviews

Here's the practical guide:

  • Designing a web app with a single backend? Session-based auth is fine. Mention it and move on.
  • Designing a system with multiple microservices? JWTs. "The auth service issues JWTs, and any downstream service can verify them independently using the public key."
  • Designing a public API for developers? OAuth 2.0. "Developers register for Client ID and Secret, exchange them for a short-lived Bearer Token, and use that token for API calls."
  • The interviewer didn't specifically ask about auth? Say "I'll use JWT-based authentication" and move on. Most interviewers don't deep-dive here — they want to see that you've thought about it.

Interview Tip

Auth is almost never the focus of a system design interview. Mention it early, pick a mechanism, and move on. If the interviewer wants to dig deeper, they'll ask. Spending 5 minutes unprompted on JWT vs sessions is a red flag that you're not prioritizing the right things.


Quick Recap

  • Authentication = who are you? Authorization = what can you do? They're separate concerns.
  • Sessions are simple but require server state. Good for monoliths, bad for distributed systems.
  • API keys are long random strings for server-to-server use. No user context, no expiration by default.
  • JWTs encode user info in a signed token. Stateless, scalable, and perfect for distributed systems. Use RS256 (asymmetric) for microservices.
  • OAuth 2.0 is the industry standard — it combines API keys (Client ID + Secret) with JWTs (Bearer Token). Don't just say "API keys for developers" — say "OAuth 2.0."
  • In interviews, say "JWT-based auth" and move on unless the interviewer pushes further.

Interview Expectations: Junior vs. Senior

  • Junior/Mid-level: Knows the difference between session cookies and JWTs. Expected to mention HTTPS/TLS for encrypting credentials in transit.
  • Senior/Staff: Understands the profound trade-offs of stateless JWTs (hard to revoke instantly without a blacklist) vs stateful sessions (requires database lookup, reducing horizontal scalability). Discusses token lifespans (short expiration + refresh tokens) as a security best practice.