>_
EngineeringNotes
← Back to All Backend Concepts
Concept 05

Authentication & Authorization

Understanding the mechanisms to verify user identity and manage access controls in modern backend systems.

01

AuthN vs. AuthZ

🛂

Authentication (AuthN)

The process of verifying who you are in a given context (like a platform or OS).

"Are you really John Doe?" → Validates Credentials
🗝️

Authorization (AuthZ)

The process of determining what you can do or what specifically you have permission to access.

"Is John allowed to delete this file?" → Validates Roles
02

The Historical Evolution

Authentication hasn't always been about database tables and passwords. The psychological principle of trust has heavily evolved alongside technology.

🤝 Pre-industrial

Authentication was entirely implied based on visceral human trust and facial recognition (e.g., village elders physically vouching for a stranger).

📜 Medieval Period

Explicit authentication grew strictly out of global trade needs. To prevent massive forgery, people relied on physical objects: wax seals, specialized watermarks, and encrypted numeric codes.

🚂 Industrial Revolution

The telegram forced a major architectural shift. Senders couldn't verify physical goods over a wire, transitioning trust to the principle of "something you know" (shared secrets or static pass phrases).

💻 Computational Era (1961)

Early time-sharing mainframes at MIT required multi-user accounts, birthing the digital password. Initially, passwords were just printed in massive, publicly readable plain-text files.

⚠️ A famous system breach forced the computer science industry to develop secure storage architecture.
🔐 Cryptographic Advancements

The creation of hashing mathematics allowed storing irreversible, fixed-length representations of passwords instead of raw text. The 1970s introduced highly complex asymmetric cryptography (Diffie-Hellman) and ticket-based systems like Kerberos, the direct precursors to the token systems we use today.

03

Core Components of Modern Auth

The Core Problem

🌐 HTTP is Inherently Stateless

By fundamental architecture design, HTTP treats every single request as completely isolated. The server has absolutely zero memory of past exchanges. Once a user logs in, their next click is seen as a brand-new stranger unless we forcefully attach state to the interaction.

STATEFUL

Sessions

To create dynamic interactions (like a shopping cart), servers create a "Session". A unique randomized Session ID is generated upon successful login and sent to the client.

1. Client sends ID:sid=xyz123
2. Server scans DB (Redis):{ id: 42 }

Severe Constaint: The server must look up every single request in the database, massively impacting highly-scaled systems.

STATELESS

JSON Web Tokens (JWT)

A breakthrough mechanism for transferring claims securely. JWTs are self-contained, meaning they hold all crucial user data (IDs, roles) and a cryptographic signature directly inside the token string itself.

JWT Structure (Base64)
HeaderAlgorithm
PayloadUser Data
SignatureValidation

Massive Scale: Eliminates the need for server-side session storage or repetitive database lookups entirely.

🍪

The Delivery Vehicle: Cookies

A Cookie is the native browser mechanism allowing the backend server to automatically store small pieces of data (like a Session ID or a JWT) directly inside the user's browser. Once set via HTTP headers, the browser automatically and securely attaches that cookie back to the server with every subsequent request, creating the illusion of a continuous, logged-in state without manual frontend intervention.

04

JWT Limitations & Hybrid Approach

STATELESS DRAWBACK

⚠️ Instant Revocation Wait-Time

Because JWTs are entirely self-contained, the server doesn't natively track them in the database. This creates a severe security challenge: if a token is stolen or a user is explicitly banned, you cannot instantly revoke that exact token across distributed systems.

The token remains 100% mathematically valid until its built-in expiration timestamp is naturally reached.
BEST PRACTICE

🔄 The Hybrid Architecture

To solve the invalidation problem without sacrificing scalability, modern applications use the Hybrid Approach:

  • Access Token (DB-Free): A highly short-lived JWT (e.g., 15 minutes) used for instant, stateless authorization continuously.
  • Refresh Token (Stateful): A long-lived token stored safely in an encrypted HTTP-only cookie and securely tracked in the database. Used uniquely to hit an endpoint generating fresh Access Tokens.
05

Types of Authentication

🗄️
Session-Based
Stateful Memory

The earliest traditional method. Server stores all user data. Offers instant, flawless revocation (just forcibly drop the row from Redis) but suffers brutally from high intra-server latency and massive scaling bottlenecks.

🎟️
Token-Based
Stateless Algorithms

Utilizes cryptographic standard JWTs. The client exclusively holds the data. Essential for modern, highly-distributed microservice architectures because it completely offloads database lookups to ultra-fast mathematical signature validation.

🔑
API Key
Machine-to-Machine

A long-lived, massive entropy static string uniquely identifying a client application. Used primarily when your backend talks precisely to another backend (e.g., calling OpenAI or Stripe servers without a human user interface).

06

OAuth 2.0 & OpenID Connect

Before OAuth, the internet suffered from the Password Sharing Anti-Pattern. If Yelp wanted to scan your Google Contacts to find friends, Yelp literally asked you to type your raw Google password directly into Yelp's website. They would then script a robot to log into Google as you.

The Catastrophic Flaw:

Yelp now had permanent, god-level access to your entire Google account. If Yelp got hacked, your Google account was compromised. You couldn't revoke just Yelp's access without resetting your global Google password.

OAuth 1.0 (2007)

Created to solve the password sharing problem. Instead of passwords, it issued tokens.

The Issue: Required highly complex cryptographic signatures on every single HTTP request. It was notoriously difficult for developers to implement and completely ignored the upcoming mobile app revolution.
OAuth 2.0 (2012)

A total rewrite. Ditched heavy cryptography for simple Bearer tokens over HTTPS. Dominates the industry today for Delegated Authorization.

The Issue: It provides Authorization (Permissions), NOT Authentication (Identity). Developers maliciously misused it for logins, creating massive security holes because OAuth inherently doesn't care who the user is, only what they can access.

⚙️ The Core Mechanism (How OAuth 2.0 works)

Let's explicitly trace how Spotify mathematically allows you to "Import contacts from Google".

1
The Request

User clicks "Import from Google" on Spotify. Spotify redirects the User's browser to the Google Login Page asking for specific "Scopes" (e.g., read:contacts).

2
Consent & Authentication

The User explicitly logs into Google using their actual credentials. Google then prompts the User on-screen: "Spotify wants to view your contacts. Allow?"

3
The Handshake (Auth Code)

If accepted, Google aggressively redirects the browser back to Spotify with a temporary, universally useless Authorization Code strictly appended to the URL.

4
The Secure Token Exchange (Backend)

Spotify's Backend script uses that temporary Auth Code, plus its own highly secret Client ID/Secret, and talks directly to Google's backend (server-to-server) to securely exchange them for the final mathematical Access Token.

The 4 Players
  • 👤 Resource Owner You (The User)
  • 📱 Client Spotify (App)
  • 🛂 Auth Server Google Login Page
  • 🗄️ Resource Server Google Contacts API
🪪

OpenID Connect (OIDC)

Federated identity (AuthN)

Because OAuth 2.0 physically didn't handle identity, the industry created OIDC (2014). It is strictly an identity layer bolted perfectly on top of the native OAuth flow.

During step 4 of the flow above, OIDC mathematically ensures the Auth server returns an Access Token along side a brand new ID Token.

The ID Token is literally just a standard JWT containing the user's explicit profile information (name, email, avatar). It definitively proves exactly who the user is. This is what securely powers "Sign in with Google or Apple" buttons across the internet.

The Industry Standard Summary

🛡️
OAuth 2.0

Delegated Authorization. Grants keys to access specific data endpoints on your behalf.

🪪
OpenID Connect (OIDC)

Federated Authentication. Mathematically verifies human identity via JWT ID Tokens.

07

Authorization Strategies (RBAC)

middleware/requireRole.js
const requireRole = (role) => { return (req, res, next) => { if (req.user.role !== role) { return res.status(403).json({ error: "Forbidden: Insufficient permissions" }); } next(); }; }; // Usage explicitly protecting a route: app.delete("/api/users/:id", requireRole("admin"), deleteUser);

Once a backend confirms who a user is natively (Authentication), it must inherently determine exactly what they are mathematically allowed to do (Authorization).

🛡️ Role-Based Access Control (RBAC)

The industry standard architectural pattern for managing scaled permissions. Instead of assigning thousands of specific endpoint permissions individually to one user, you definitively assign system Roles (e.g., admin, moderator, user).

The backend middleware then simply intercepts incoming HTTP requests and ensures the JWT Payload contains a role with sufficient privileges entirely before allowing execution to hit the primary controller logic.

08

Security Best Practices

Backend engineers are the absolute last line of defense against catastrophic data breaches. Mastering defensive foundational architectural patterns is mandatory.

🚨 Generic Error Messages

Never provide hyper-specific UI feedback like "User not found" vs "Incorrect password". This directly facilitates Username Enumeration, explicitly allowing hackers to brute-force a definitive list of valid emails on your server.

❌ Bad:Email does not exist.
✅ Good:Invalid credentials.

⏱️ Mitigating Timing Attacks

In immensely subtle cyberattacks, hackers can measure exactly how many milliseconds your server takes to respond to deduce if a username exists (because hashing a valid password takes ~100ms longer than instantly rejecting an invalid string).

Defense Architecture:

Backend engineers must explicitly employ constant-time comparison algorithms (like Node's native crypto.timingSafeEqual()), or deliberately inject simulated jitter delays into the response cycle so incredibly fast failures still computationally simulate a natural hashing lag.