📚 Module: Unit 7 • Security & Identity

Authentication, Authorization & Identity Systems

A first-principles engineering textbook on securing modern distributed web architectures. Master cryptographic key derivation functions (Argon2id and Bcrypt), stateful session stores vs stateless JSON Web Tokens (JWT), dual-token refresh rotation with replay detection, OAuth 2.0 PKCE authorization code flows, Role-Based Access Control (RBAC), secure credential recovery, and Time-based One-Time Password (TOTP) multi-factor authentication.

🔐 Hashing: Argon2id & Bcrypt Adaptive Cost
🎟️ Tokens: JWT Access/Refresh Rotation
🌐 Federation: OAuth 2.0 & OIDC (PKCE)
🎯 Formula: 60% Theory • 20% Code • 20% Architecture
← Unit 06: REST API Development Backend & Cloud Track • Unit 07 of 12 Track Index Syllabus →

Authentication vs Authorization & Memory-Hard Password Hashing

Security in backend engineering begins by separating two distinct identity concepts:

The Physics of Password Hashing: Why General Hashes Fail

General-purpose cryptographic hash functions (such as MD5, SHA-1, SHA-256) were engineered for file integrity verification and digital signatures. They are intentionally designed to be ultra-fast. A modern high-end GPU cluster can compute over 100,000,000,000 SHA-256 hashes per second. If an attacker dumps a database containing SHA-256 hashed passwords, every standard human password can be cracked via pre-computed Rainbow Tables and brute-force dictionaries in hours.

Key Derivation Functions: Salts & Adaptive Cost

Password hashing algorithms must be computationally expensive, deliberately slow, and unique per record:

Figure 7.1 — Rainbow Table Attack Vector vs Salted Argon2id Memory-Hard Pipeline
[UNSALTED FAST HASH: VULNERABLE TO RAINBOW TABLES] Password: "Password123!" ===> SHA-256 ===> 5e884898da28... <=== Match found in pre-computed table! (Cracked in 0.000001 seconds) [SALTED ARGON2ID PIPELINE: ASIC & GPU IMMUNE] User Password: "Password123!" | v +-------------------------+ | CSPRNG Salt Generator | ===> Generates 16 Bytes Unique Salt: 0xa94b81c2... +-------------------------+ | v +---------------------------------------------------------------------------------------+ | ARGON2ID KEY DERIVATION ENGINE | | - Time Cost: 3 Iterations | | - Memory Cost: 64 MB RAM per hash (Defeats GPU Parallel Core Architecture) | | - Parallelism: 4 Threads | +---------------------------------------------------------------------------------------+ | v Final Formatted Hash String Stored in Database: $argon2id$v=19$m=65536,t=3,p=4$qUKBw...$8fK19Z... (Takes ~250ms to verify on server)
TypeScript src/infrastructure/crypto/password-hasher.ts
import * as argon2 from 'argon2';

export class PasswordService {
  // Argon2id Configuration conforming to OWASP specifications
  private static readonly ARGON_CONFIG: argon2.Options = {
    type: argon2.argon2id,
    memoryCost: 65536, // 64 MB of RAM
    timeCost: 3,       // 3 iterations
    parallelism: 4,    // 4 concurrent threads
    hashLength: 32
  };

  public static async hashPassword(plainPassword: string): Promise<string> {
    return await argon2.hash(plainPassword, this.ARGON_CONFIG);
  }

  public static async verifyPassword(storedHash: string, plainAttempt: string): Promise<boolean> {
    try {
      return await argon2.verify(storedHash, plainAttempt);
    } catch {
      return false;
    }
  }
}

Stateful Sessions vs Stateless Tokens & Cookie Security

Once credentials are authenticated, the server must issue a proof-of-identity token for subsequent HTTP requests:

1. Stateful Session-Based Authentication

The server generates a cryptographically random Session ID string, persists session metadata inside a centralized in-memory store (Redis), and issues the Session ID to the client via an HTTP cookie.

2. Stateless Token-Based Authentication (JWT)

The server cryptographically signs a payload containing the user's identity and claims. Any microservice with the public/shared key can verify the token in-memory using pure mathematics with zero database queries.

Hardening HTTP Cookies: Defeating XSS & CSRF

Storing authentication tokens in browser localStorage is a severe security vulnerability: any third-party script, analytics tag, or Cross-Site Scripting (XSS) vulnerability can execute localStorage.getItem('token') and exfiltrate credentials. Tokens must be stored in hardened HTTP cookies:

JSON Web Tokens (JWT) & Dual-Token Rotation Architecture

A JSON Web Token (RFC 7519) consists of three Base64URL-encoded strings separated by dots:
Header.Payload.Signature

The Dual-Token Pattern & Refresh Token Rotation

Stateless JWTs cannot be revoked without a database query. If an access token with a 30-day expiration is stolen, the attacker has 30 days of uninhibited access.
The Solution: The Dual-Token Architecture with Rotation & Replay Detection:

  1. Access Token: Short-lived (15 minutes). Held in client memory. Used in Authorization: Bearer <token> to access APIs.
  2. Refresh Token: Long-lived (7 days). Stored in an HttpOnly cookie.
  3. Rotation: When the access token expires, the client calls /auth/refresh. The server issues a new Access Token and a new Refresh Token, invalidating the old refresh token.
  4. Automatic Replay Detection: If an attacker steals a refresh token and uses it after the legitimate user has already refreshed, the server detects that an invalidated token was submitted! The server flags this as an active breach and instantly revokes the entire token family, forcing all parties to re-authenticate.
Figure 7.2 — Dual-Token Rotation with Automatic Replay Detection
CLIENT APP AUTH SERVER & REDIS | | | 1. POST /auth/refresh (Sends RefreshToken #1) | | ======================================================================> | | | Check: Is RT #1 valid? | | Yes -> Invalidate RT #1 | 2. Issues AccessToken #2 + RefreshToken #2 | Store RT #2 in Family | <====================================================================== | | | | | [ATTACKER EXPLOITS STOLEN REFRESH TOKEN #1] | | | | 3. Malicious POST /auth/refresh (Submits Already-Used RT #1) | | ======================================================================> | | | Detects REPLAY ATTACK! | | Token #1 was already used! | | ACTION: WIPE TOKEN FAMILY! | 4. Returns 401 Unauthorized (Entire Session Revoked) | Invalidate RT #2 in Redis | <====================================================================== |
TypeScript src/interfaces/http/middleware/auth-guard.ts
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';

export interface AuthenticatedUser {
  userId: string;
  roles: string[];
}

export function requireAuthentication(req: any, res: Response, next: NextFunction) {
  const authHeader = req.headers.authorization;

  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({
      type: 'https://api.devascentacademy.me/errors/unauthorized',
      title: 'Unauthorized',
      status: 401,
      detail: 'Missing or malformed Bearer authorization token.'
    });
  }

  const token = authHeader.split(' ')[1];

  try {
    // Verify digital signature and token expiration in-memory (Zero DB overhead)
    const payload = jwt.verify(token, process.env.JWT_ACCESS_SECRET!) as AuthenticatedUser;
    req.user = payload;
    return next();
  } catch (err) {
    return res.status(401).json({
      type: 'https://api.devascentacademy.me/errors/token-expired',
      title: 'Token Expired or Invalid',
      status: 401,
      detail: 'The provided access token is invalid or expired. Refresh credentials.'
    });
  }
}

OAuth 2.0 & Social Login (Authorization Code with PKCE)

The OAuth 2.0 authorization framework (RFC 6749) enables third-party applications to obtain limited access to a HTTP service on behalf of a resource owner without sharing the user's password. OpenID Connect (OIDC) sits directly on top of OAuth 2.0 to provide an identity authentication layer (issuing a signed id_token).

Why PKCE (Proof Key for Code Exchange) is Mandatory

In public clients (Single Page Applications like React, mobile apps), client secrets cannot be securely stored in client code. An attacker could intercept the incoming authorization code via operating system URI scheme hijacking.
PKCE (RFC 7636) eliminates this attack vector:

  1. Client generates a high-entropy random cryptographic string: code_verifier.
  2. Client hashes it via SHA-256 to create the code_challenge.
  3. Client initiates the OAuth redirect to the provider, passing the code_challenge.
  4. After the user logs in (e.g., via Google), Google redirects back with an authorization code.
  5. Client exchanges the authorization code for tokens, transmitting the original unhashed code_verifier.
  6. The Authorization Server verifies: SHA256(code_verifier) === code_challenge. An attacker who intercepted the authorization code cannot exchange it because they do not have the raw verifier!
Figure 7.3 — OAuth 2.0 Authorization Code Flow with PKCE
USER BROWSER / CLIENT AUTHORIZATION SERVER (Google/GitHub) | | | 1. Generate code_verifier & SHA256 code_challenge | | 2. GET /oauth/authorize?challenge=ABC&method=S256 | | =================================================> | | | User Authenticates | 3. Redirect back with: ?code=AUTH_CODE_99 | & Grants Consent | <================================================= | | | | 4. POST /oauth/token | | code=AUTH_CODE_99 & code_verifier=RAW_SECRET | | =================================================> | Verify: | | SHA256(verifier) == ABC | 5. Return: id_token (OIDC) + access_token | | <================================================= | [TOKENS ISSUED]

Role-Based Access Control (RBAC) & Permission Guards

In an enterprise backend, access control is governed by Role-Based Access Control (RBAC). Permissions are mapped to abstract operational roles, and users are assigned one or more roles.

TypeScript src/interfaces/http/middleware/rbac-guard.ts
import { Response, NextFunction } from 'express';

// Declarative RBAC Authorization Middleware Factory
export const requireRoles = (...allowedRoles: string[]) => {
  return (req: any, res: Response, next: NextFunction) => {
    if (!req.user || !req.user.roles) {
      return res.status(403).json({
        type: 'https://api.devascentacademy.me/errors/forbidden',
        title: 'Access Forbidden',
        status: 403,
        detail: 'Principal does not possess required security roles.'
      });
    }

    const hasPermission = req.user.roles.some((role: string) => 
      allowedRoles.includes(role)
    );

    if (!hasPermission) {
      return res.status(403).json({
        type: 'https://api.devascentacademy.me/errors/insufficient-privileges',
        title: 'Insufficient Privileges',
        status: 403,
        detail: `Requires one of the following roles: [${allowedRoles.join(', ')}]`
      });
    }

    return next();
  };
};

Password Reset Lifecycles & Multi-Factor Authentication (TOTP)

Securing the edge extends beyond login to credential recovery and multi-factor authentication (MFA).

Secure Password Reset Architecture

Time-Based One-Time Password (TOTP — RFC 6238)

TOTP provides a secondary possession factor without SMS carrier vulnerabilities (SIM swapping). It uses a shared cryptographic base32 secret seed key. Both the server and the authenticator app (Google Authenticator, 1Password) divide the current Unix epoch time into 30-second windows ($T_0 = 30$):

Figure 7.4 — TOTP Mathematical Token Generation Pipeline (RFC 6238)
CURRENT UNIX EPOCH TIME (e.g., 1718900015 seconds) | v [DIVIDE BY 30-SECOND WINDOW]: Counter T = floor(1718900015 / 30) = 57296667 | v +-------------------------------------------------------------+ | SHARED SECRET SEED KEY (Base32) | | (Scanned via QR Code during registration: "JBSWY3DPEHPK3PXP")| +-------------------------------------------------------------+ | v [HMAC-SHA1 ALGORITHM]: Hash = HMAC-SHA1(Secret, Counter T) | v [DYNAMIC TRUNCATION]: Extract 4-byte dynamic binary code | v [MODULO 1,000,000]: Generates exactly 6 decimal digits: ===> "491823"

University & Industrial Examination Review

1. Why is Argon2id mathematically superior to SHA-256 and Bcrypt for password storage in high-security applications?
Answer: SHA-256 is an ultra-fast general hash with zero memory overhead, allowing GPUs to test billions of combinations per second using pre-computed Rainbow Tables. Bcrypt introduces adaptive work factor iterations, but remains CPU-bound. Argon2id is a memory-hard key derivation function that mandates allocating tens of megabytes of physical RAM during hash computation. Because specialized ASIC and GPU crackers possess limited memory per execution thread, Argon2id renders massive parallel hardware brute-forcing computationally and economically impossible.
2. Explain how storing access tokens in `HttpOnly` and `SameSite=Strict` cookies defeats both Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF).
Answer: The HttpOnly flag forbids the browser's JavaScript execution engine from accessing the cookie via document.cookie. If an attacker injects malicious JavaScript via an XSS flaw, the script cannot read or exfiltrate the token. The SameSite=Strict flag instructs the browser to omit the cookie on cross-site requests (requests initiated from third-party websites). If a victim visits an attacker's malicious webpage that attempts a forged POST request to the bank API, the browser refuses to attach the authentication cookie, neutralizing CSRF.
3. Detail the mechanics of Refresh Token Rotation with Automatic Replay Detection. What action must the auth server take upon detecting an invalid replayed token?
Answer: Under Refresh Token Rotation, every time an access token is refreshed, the server invalidates the submitted refresh token and issues a brand-new refresh token paired with the new access token. If an attacker intercepts a refresh token and attempts to submit it after the legitimate user has already refreshed, the server detects that an already-invalidated token was presented (a Replay Attack). Because this signals a credential breach, the server immediately revokes the entire refresh token family in Redis/DB, immediately terminating all active sessions for that user across all devices.
4. Why is PKCE (Proof Key for Code Exchange) mandatory for public clients executing the OAuth 2.0 Authorization Code flow?
Answer: Public clients (Single Page Apps and mobile native apps) cannot securely store a static client_secret. An attacker could intercept the incoming authorization code via custom URI scheme sniffing. Under PKCE, the client generates a unique cryptographic code_verifier and sends its hash (code_challenge) in the authorization request. When exchanging the authorization code, the client supplies the unhashed verifier. The server verifies SHA256(verifier) === challenge. An intercepting attacker possessing only the authorization code cannot redeem tokens because they lack the raw verifier generated inside the legitimate client's memory.
5. Deconstruct the Time-Based One-Time Password (TOTP) algorithm defined in RFC 6238. Why does TOTP remain synchronized without network communication?
Answer: TOTP calculates tokens by evaluating HMAC-SHA1(Secret, Counter T), where T = floor(UnixTime / 30). Because both the server and the client authenticator app share the identical cryptographic base32 secret seed (exchanged during initial QR code registration) and agree on the standardized 30-second time step, their computed counter T values match identically based purely on the synchronized global Unix clock. No cellular data or internet network connection is required on the user's authenticator device.