← Computer Networks
Sessions, Auth & Browser Security

Authentication & Authorization (JWT, OAuth)

Watch: "OAuth 2 Explained In Simple Terms" — ByteByteGo

Authentication and authorization get conflated constantly, and JWTs and OAuth 2.0 get conflated even more — untangling both is one of the most reliable signals interviewers use to judge backend security fluency. AuthN answers "who are you?" — proving identity at login. AuthZ answers "what are you allowed to do?" — deciding whether that already-established identity can access a specific resource. Every real system separates them: you authenticate once, then every subsequent request is authorized independently against the resource it's touching.

What Is Authentication?

Authentication is proving who you are. Every time you type a password, tap a fingerprint sensor, or approve a login on your phone, you're authenticating — showing a system some proof that you're really the person you claim to be, once, at login.

What Is Authorization?

Authorization is deciding what someone who's already proven their identity is allowed to do. Being logged in doesn't mean you can see or change everything — a system checks, for each action separately, whether your identity has permission for that specific thing.

Where This Is Used

A Day-to-Day Example

Think of an office building. Showing your ID badge to the security guard at the front door is authentication — proving you're really an employee. Once inside, your badge only opens certain doors: the main office, sure, but not the server room or the executive floor unless you're specifically cleared for it. That door-by-door check is authorization — a separate decision made every time, even though you already proved who you are once at the entrance.

Deep Dive: JWT Structure, Decoded

The three parts of a JWTheader{"alg":"HS256","typ":"JWT"}payload (claims)sub, name, iat, exp, iss, audsignatureHMAC(header.payload)..header . payload . signature (each part base64url-encoded)anyone can decode header and payload: they are signed, not encryptedchanging any byte breaks the signature, so the server detects tampering
A JWT is three base64url strings joined by dots. The signature covers the first two parts, so the server can trust the claims without a database lookup. Never put secrets in the payload: it's readable by anyone holding the token. The full token and decoded JSON are below.
headerclaimssignature
header.payload.signature

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiJ1c3JfMTIzIiwibmFtZSI6IkFkYSIsImlhdCI6MTcwMDAwMDAwMCwiZXhwIjoxNzAwMDAzNjAwLCJpc3MiOiJhcGkuZXhhbXBsZS5jb20iLCJhdWQiOiJhcHAuZXhhbXBsZS5jb20ifQ
.
4f7c2b1a9e8d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b

// Header, base64url-decoded:
{"alg":"HS256","typ":"JWT"}

// Payload, base64url-decoded:
{"sub":"usr_123","name":"Ada","iat":1700000000,"exp":1700003600,"iss":"api.example.com","aud":"app.example.com"}

Anyone holding the token can decode the header and payload themselves — base64url is an encoding, not encryption. The critical claims a server must validate on every request: exp (has it expired), iss (issuer — was this actually issued by a service I trust), aud (audience — was this token meant for me specifically, or for some other service), and sub (subject — the identity the claims are about). The signature is what actually protects integrity: it's computed over the header and payload using either an HMAC shared secret (HS256) or an RSA/ECDSA private key (RS256/ES256), and verifying it proves the token wasn't tampered with since issuance — it does not mean the contents are secret. Never put a password, a raw credit card number, or anything sensitive directly in the payload of a signed-only JWT.

Deep Dive: The OAuth 2.0 Authorization Code Flow

OAuth 2.0 authorization code flowUserClient appAuth serverResource server1. click Login2. redirect to authorize3. login + consent screen4. approve5. redirect back with code6. code + client_secret7. access + refresh token8. GET /calendar, Bearer token9. 200 + data
The user types their password only on the authorization server, never in the app. The browser carries back a short-lived code (step 5), and the app swaps it for tokens in a direct server-to-server call using its secret (step 6, red), so tokens never pass through the browser's address bar.
user / app requestsresponsesback-channel with secret
Show text version
User          Client App              Authorization Server        Resource Server
 |--1. Click "Login"-->|                          |                        |
 |                      |--2. Redirect to AuthZ-->|                        |
 |<-----------------3. Login + consent screen-----|                        |
 |--------------------4. Approves----------------->|                        |
 |                      |<--5. Redirect w/ code---|                        |
 |                      |--6. code + client_secret->|  (server-to-server)   |
 |                      |<--7. access_token, refresh_token----------------|
 |                      |---------------8. GET /calendar, Bearer token---->|
 |                      |<--------------------------------9. 200 + data---|

The whole point of the extra "exchange a code for a token" round trip (steps 5–7) rather than handing the app a token directly is that the code is useless on its own — it's short-lived and single-use, and exchanging it for the actual access token requires the app's client_secret, which only lives server-side. This means the access token itself never transits the browser's URL bar or JavaScript context, unlike the older, now-deprecated implicit flow which put the token directly in a redirect fragment.

Deep Dive: OAuth vs OIDC — Where Identity Actually Comes From

OAuth 2.0 by itself issues an access token, which is a bearer credential for calling an API on the resource owner's behalf — it doesn't standardize any claim about who the user is. Treating "I got a valid access token back" as "I know who this user is" is a real, documented vulnerability class (an access token might not even be a JWT — plenty are opaque strings meaningless outside the issuing service). OpenID Connect (OIDC) is a thin identity layer built directly on top of OAuth 2.0: it adds a distinct ID token (always a JWT, with standardized claims like sub, email, name) specifically meant to answer "who is this," plus a /userinfo endpoint. The rule to internalize: use the OAuth access token to call APIs; use the OIDC ID token to authenticate the user in your app.

Key Pointers

How It Works

  1. The user starts login; the app redirects to the identity/authorization provider with a client ID, redirect URI, and requested scopes.
  2. The user authenticates with the provider and consents to the requested access.
  3. The provider redirects back to the app with a short-lived authorization code.
  4. The app's backend exchanges that code plus its client secret for an access token (and often a refresh token) via a direct server-to-server call — the token never transits the browser's URL bar.
  5. The app presents the access token on subsequent API calls, typically as a Bearer token in the Authorization header; the resource server verifies its signature and expiry to authorize the request.
  6. When the access token expires, the app uses the refresh token to obtain a new one without re-prompting the user.

Try It Yourself

# Decode any JWT's header and payload without a library
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | base64 -d
echo "eyJzdWIiOiJ1c3JfMTIzIn0" | base64 -d

# Or just paste a token at jwt.io — it decodes header/payload live in the
# browser and lets you verify the signature if you supply the secret/public key

# Send an authenticated request with a Bearer token
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  https://api.example.com/me

# Inspect what scopes an OAuth access token actually carries (provider-specific,
# example shown for a Google-style tokeninfo endpoint)
curl "https://oauth2.googleapis.com/tokeninfo?access_token=ya29.a0Af..."

Interview Q&A

Q: Is a JWT encrypted?

A: No, not by default. A standard JWT (JWS) is signed, which guarantees integrity — you can trust it wasn't tampered with — but the payload is just base64url-encoded, which anyone can reverse with no key at all. If you need the contents themselves to be unreadable, you need JWE (JSON Web Encryption), a related but distinct standard, and it's much less commonly used in practice than plain signed JWTs.

Q follow-up: Given that, what's actually unsafe to put in a JWT payload?

A: Anything you wouldn't want exposed to whoever holds the token — passwords obviously, but also things like internal database IDs you don't want enumerable, PII beyond what's needed, or granular permission details that reveal your system's internal structure. The signature protects against a forged or modified token; it does nothing to hide the content from the holder.

Q: How do you revoke a JWT before it expires?

A: Strictly, you can't make the token itself invalid early — verification is purely local/stateless. The practical mitigations: keep access token lifetimes short (minutes, not days) so exposure windows are small, pair them with a longer-lived refresh token that is checked against server-side state on each use (so refresh itself is revocable), or maintain a denylist of revoked token IDs (jti claim) that gets checked alongside signature verification — which reintroduces the state lookup you were trying to avoid, just for a smaller set of revoked tokens instead of every session.

Q: What's the actual difference between OAuth 2.0 and OpenID Connect?

A: OAuth 2.0 is an authorization framework — it's about a client getting delegated, scoped access to a resource on a user's behalf (e.g. "let this app read my calendar"), and its core output is an access token. It was never designed to answer "who is this user" in a standardized way. OpenID Connect adds an identity layer on top: it defines a separate ID token (always a JWT with standard identity claims) and a userinfo endpoint specifically for authentication. If you see "Sign in with Google," that's OIDC running on top of OAuth's plumbing, not bare OAuth.

Q follow-up: What goes wrong if a system uses a raw OAuth access token to authenticate a user instead of using OIDC properly?

A: The access token's format and contents are provider- and API-specific, not standardized for identity — it might be an opaque string with no user info in it at all, or its audience might be a completely different resource server, meaning it was never intended to prove identity to your app in the first place. Treating "I received some access token" as proof of identity is exactly the class of mistake OIDC exists to prevent; the correct pattern is to validate a properly-audienced ID token instead.

System Design Tie-in

This is close to guaranteed to come up in "design an authentication system" or "design a public API with third-party integrations" prompts. For first-party auth (your own app, your own users) a straightforward session-or-JWT scheme with short-lived access tokens plus refresh tokens is standard. The moment a third party needs delegated access to a user's data without ever seeing their password — "connect your Google Calendar," "let this app post to your Slack" — that's OAuth 2.0's actual use case, and volunteering the authorization-code flow (not implicit, which is deprecated) is the expected answer. For microservices, JWTs shine because any service can verify a token's signature locally without a network call back to an auth service — but be ready to discuss how you'd still revoke access quickly if a service or token were compromised (short expiries, a fast-path denylist, or rotating signing keys).

Trade-offs

Stateless JWTs remove the session-store lookup from every request but complicate revocation — a compromised token stays valid until it naturally expires unless you maintain a denylist, which reintroduces the state you were trying to avoid. Session tokens are trivially revocable but require a lookup against shared storage on every request. The interview gotcha worth memorizing: OAuth 2.0 alone is not an authentication protocol — it was designed for delegated authorization, and treating a raw access token as proof of identity (instead of using OpenID Connect's ID token) is the mistake experienced interviewers listen for.