Skip to main content
  1. Posts/

Who Goes There

·909 words·5 mins
Photograph By Mediamodifier
Blog Software Engineering Security
Table of Contents

Two Different Questions
#

Authentication and authorization get used interchangeably, but they’re different questions:

  • Authentication (AuthN): “Who are you?” — proving your identity
  • Authorization (AuthZ): “What can you do?” — checking your permissions

You can be authenticated (I know you’re Aaron) but not authorized (Aaron doesn’t have admin access). Getting these confused leads to security bugs — the kind where a logged-in user can access another user’s data because the system checked “is this person logged in?” but not “is this person allowed to see this?”

There are four main technologies for handling this. They overlap, they’re often used together, and they confuse almost everyone the first time around.

Sessions: The Original
#

The most straightforward approach. You log in, the server creates a session, stores it somewhere (memory, database, Redis ), and gives you a cookie with the session ID.

Login → Server creates session → Sends cookie (session_id=abc123)
Every request → Browser sends cookie → Server looks up abc123 → Finds your data
Logout → Server deletes session → Cookie is useless

Simple. Stateful. The server knows about every active session. Revoking access is instant — delete the session from the server, and the user is logged out.

The downside: the server has to store and look up every session. With one server, that’s fine. With ten servers behind a load balancer , they all need access to the same session store (usually Redis). It’s solvable, but it’s extra infrastructure.

JWTs: The Stateless Token
#

A JWT (JSON Web Token) flips the model. Instead of the server storing your session, all your data is packed into a signed token that the server gives you. The server doesn’t store anything — it just verifies the signature.

Login → Server creates JWT (contains: userId, role, expiry) → Signs it → Sends to client
Every request → Client sends JWT in header → Server verifies signature → Trusts the claims

The token looks like this (base64-encoded):

eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiIxMjMiLCJyb2xlIjoiYWRtaW4ifQ.signature

The middle section decodes to {"userId":"123","role":"admin"}. The server never stores this — it just checks that the signature is valid (proving the token wasn’t tampered with).

The tradeoff: you can’t revoke a JWT until it expires. With sessions, you delete the session. With JWTs, the token is valid until its expiry time, even if the user should be logged out. The workaround is short expiry (15 minutes) combined with refresh tokens (a separate long-lived token used to get new access tokens).

SessionsJWTs
Server stores stateYesNo
RevocationInstantWait for expiry
Scales across serversNeeds shared storeAny server can verify
Best forWeb apps with server renderingAPIs, microservices, mobile

OAuth 2.0: Letting Someone Else Handle It
#

OAuth isn’t authentication — it’s authorization delegation. “Let this app access my data without giving it my password.”

When you click “Sign in with Google,” you’re not logging into the app with Google’s credentials. You’re telling Google: “This app can access my profile information.” Google handles the login and gives the app a token.

1. App redirects to Google
2. You log in with Google (the app never sees your password)
3. Google asks: "Allow this app to access your profile?"
4. You approve → Google gives the app an access token
5. App uses the token to call Google's API for your profile

The most common flow (Authorization Code + PKCE) is designed for web and mobile apps. The app gets an authorization code, exchanges it for tokens server-side, and never exposes the tokens to the browser.

OAuth + JWT together: Often, the access token issued by the OAuth flow IS a JWT. The app uses OAuth to get the token, then uses the JWT for subsequent API calls. They’re not competing technologies — they’re layers.

SSO: One Login, Many Doors
#

Single Sign-On is what I set up with Authentik in my homelab. One login gets you into everything — Portainer, Traefik dashboard, Grafana. You authenticate once with the identity provider, and every connected service trusts that authentication.

Visit Grafana → Not logged in → Redirect to Authentik
Log in to Authentik (with 2FA) → Redirect back to Grafana → You're in
Visit Portainer → Redirect to Authentik → Already logged in → Token issued → You're in
  (no second login)

The practical benefit: one set of credentials to manage, one place to enforce 2FA, one place to revoke access. Disable a user in Authentik, and they’re locked out of everything.

The implementation is usually OpenID Connect (OIDC) — which is OAuth 2.0 with an identity layer on top. OAuth answers “what can this app access?” OIDC adds “who is this person?”

When to Use What
#

ScenarioUse
Traditional web app, server-renderedSessions
REST API consumed by mobile/SPAJWT
“Login with Google/GitHub”OAuth 2.0
Multiple internal services, one loginSSO (OpenID Connect)
API-to-API (no human)OAuth 2.0 Client Credentials

Most real systems combine them. You might use OAuth for initial login, receive a JWT as the access token, and use SSO to make that login work across multiple services. They’re not mutually exclusive — they’re building blocks.

The 2FA Decision
#

When I set up Authentik, enabling 2FA was a deliberate choice. Every service behind it requires a second factor. Is it annoying? Slightly. Is it worth it? After seeing credential-stuffing attempts in my CrowdSec logs, absolutely. One compromised password without 2FA means access to everything behind the SSO. With 2FA, a stolen password is useless on its own.

Aaron Yong
Author
Aaron Yong
Building things for the web. Writing about development, Linux, cloud, and everything in between.

Related

The One-Line Fix
·1004 words·5 mins
Photograph By Maksym Kaharlytskyi
Blog Software Engineering PostgreSQL
Database indexing, EXPLAIN ANALYZE, and why one CREATE INDEX beat five new pods
The Contract
·785 words·4 mins
Photograph By Romain Dancre
Blog Software Engineering Web Development
REST API design patterns that save your future self from debugging nightmares
Traffic Cops
·682 words·4 mins
Photograph By Adil Edin
Blog Software Engineering System Design
Load balancing algorithms, L4 vs L7, and why your requests end up where they do