← Back to all projects

LEARN AUTHENTICATION DEEP DIVE

Learn Authentication: From Zero to Security Expert

Goal: Deeply understand how authentication works—from password hashing to OAuth flows, from session cookies to JWTs, from single-factor to hardware tokens. Build systems that prove identity correctly and securely.


Why Authentication Matters

Every time you log in, every API call with a token, every “Remember Me” checkbox—authentication is happening. Yet most developers copy-paste authentication code without understanding it. When things break (or get hacked), they have no idea why.

After completing these projects, you will:

  • Understand why passwords are hashed (and why MD5 is broken)
  • Know how sessions work at the cookie and server level
  • Build JWT authentication from scratch (and know its weaknesses)
  • Implement OAuth 2.0 flows correctly
  • Understand multi-factor authentication deeply
  • Recognize and prevent common authentication attacks
  • Build secure authentication systems confidently

Core Concept Analysis

What is Authentication?

Authentication: "WHO are you?"  →  Proving identity
Authorization:  "WHAT can you do?"  →  Checking permissions

       ┌─────────────────────────────────────────────────────────┐
       │                    Authentication                        │
       │                                                          │
       │   Something you KNOW     (password, PIN)                 │
       │   Something you HAVE     (phone, hardware key)           │
       │   Something you ARE      (fingerprint, face)             │
       │                                                          │
       └─────────────────────────────────────────────────────────┘

The Authentication Flow (Simplified)

┌──────────┐         ┌──────────┐         ┌──────────┐
│  Client  │         │  Server  │         │ Database │
└────┬─────┘         └────┬─────┘         └────┬─────┘
     │                    │                    │
     │  1. Credentials    │                    │
     │ ──────────────────>│                    │
     │                    │  2. Lookup user    │
     │                    │ ──────────────────>│
     │                    │                    │
     │                    │  3. User record    │
     │                    │ <──────────────────│
     │                    │                    │
     │                    │  4. Verify password│
     │                    │    (hash compare)  │
     │                    │                    │
     │  5. Session/Token  │                    │
     │ <──────────────────│                    │
     │                    │                    │
     │  6. Subsequent     │                    │
     │     requests with  │                    │
     │     session/token  │                    │
     │ ──────────────────>│                    │

Fundamental Concepts

  1. Password Storage
    NEVER store plaintext passwords!
    
    BAD:  password = "secret123"  →  store "secret123"
    BAD:  password = "secret123"  →  store MD5("secret123")
    BAD:  password = "secret123"  →  store SHA256("secret123")
    
    GOOD: password = "secret123"
          salt = random_bytes(16)
          hash = bcrypt(password + salt, cost=12)
          store: hash (salt is embedded in bcrypt output)
    
  2. Why Hashing Isn’t Enough
    • Rainbow tables: Precomputed hash→password mappings
    • Solution: Salting (random data added before hashing)
    • Why SHA256 is bad: Too fast! Attackers can try billions/second
    • Solution: Slow hashes (bcrypt, Argon2, scrypt)
  3. Sessions vs Tokens | Aspect | Sessions | Tokens (JWT) | |——–|———-|————–| | Storage | Server-side | Client-side | | Scalability | Needs shared storage | Stateless | | Revocation | Easy (delete session) | Hard (need blocklist) | | Size | Small cookie | Larger payload | | Security | CSRF vulnerable | XSS vulnerable |

  4. OAuth 2.0 Flows
    Authorization Code Flow (most common):
    
    User → App: "Login with Google"
    App → Google: Redirect to authorization URL
    User → Google: Approves access
    Google → App: Authorization code
    App → Google: Exchange code for tokens (server-side)
    Google → App: Access token + refresh token
    App → Google API: Use access token
    
  5. Multi-Factor Authentication (MFA)
    • TOTP: Time-based One-Time Password (Google Authenticator)
    • HOTP: HMAC-based One-Time Password (counter-based)
    • WebAuthn/FIDO2: Hardware keys (YubiKey)
    • SMS/Email: Weaker but still adds security
  6. Common Attacks | Attack | Description | Prevention | |——–|————-|————| | Brute Force | Try all passwords | Rate limiting, lockouts | | Credential Stuffing | Use leaked passwords | Breach detection, MFA | | Session Hijacking | Steal session cookie | HTTPS, HttpOnly, Secure | | CSRF | Trick user’s browser | CSRF tokens, SameSite | | Timing Attack | Measure response time | Constant-time comparison |

Project List

Projects are ordered from fundamental cryptography to complete authentication systems.


Project 1: Password Hasher & Cracker

  • File: LEARN_AUTHENTICATION_DEEP_DIVE.md
  • Main Programming Language: Python
  • Alternative Programming Languages: Go, Rust, C
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 1: Beginner
  • Knowledge Area: Cryptography / Hashing
  • Software or Tool: Password Security Tool
  • Main Book: “Serious Cryptography” by Jean-Philippe Aumasson

What you’ll build: A tool that demonstrates password hashing with different algorithms (MD5, SHA256, bcrypt, Argon2) and then attempts to crack them—showing viscerally why some methods are secure and others aren’t.

Why it teaches authentication: Before you can build auth systems, you need to understand why passwords are stored as hashes and why the choice of hash function matters enormously. Seeing MD5 crack instantly while bcrypt takes forever is unforgettable.

Core challenges you’ll face:

  • Understanding hash functions → maps to one-way functions, collision resistance
  • Implementing salting → maps to rainbow table prevention
  • Comparing hash speeds → maps to why bcrypt uses “cost factor”
  • Building a cracker → maps to understanding attacker capabilities

Key Concepts:

  • Hash function properties: “Serious Cryptography” Chapter 6 - Aumasson
  • Password hashing: OWASP Password Storage Cheat Sheet
  • bcrypt internals: “Practical Cryptography” Chapter 8 - Ferguson & Schneier
  • Rainbow tables: “The Art of Intrusion” Chapter 6 - Mitnick

Difficulty: Beginner Time estimate: Weekend Prerequisites: Basic Python, understanding of what a hash is

Real world outcome:

$ python hasher.py --hash "password123"
Hashing 'password123' with different algorithms:

MD5:      482c811da5d5b4bc6d497ffa98491e38          (0.000001s)
SHA256:   ef92b778bafe771e89245b89ecbc08a44a4e166c... (0.000001s)
bcrypt:   $2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN... (0.234000s)
Argon2:   $argon2id$v=19$m=65536,t=3,p=4$...        (0.089000s)

$ python cracker.py --hash "482c811da5d5b4bc6d497ffa98491e38" --type md5
Attempting to crack MD5 hash...
Dictionary attack: password123  [CRACKED in 0.003s]

$ python cracker.py --hash "ef92b778bafe..." --type sha256
Attempting to crack SHA256 hash...
Dictionary attack: password123  [CRACKED in 0.005s]

$ python cracker.py --hash '$2b$12$LQv3c1yqBWVHxkd0LHAkCO...' --type bcrypt
Attempting to crack bcrypt hash...
Dictionary attack: Trying 'password'... (0.21s per attempt)
                   Trying '123456'...   (0.21s per attempt)
                   Trying 'password123'... [CRACKED in 47.3s]

Cracking speed comparison:
  MD5:    ~1,000,000,000 attempts/second (GPU)
  SHA256: ~500,000,000 attempts/second (GPU)
  bcrypt: ~15,000 attempts/second (cost=12)
  Argon2: ~1,000 attempts/second (with memory hardness)

Conclusion: A password crackable in 0.003s with MD5 would take
            ~2.7 hours with bcrypt (cost=12)

Implementation Hints:

Hash functions in Python:

import hashlib
import bcrypt
from argon2 import PasswordHasher

# MD5 (NEVER use for passwords)
md5_hash = hashlib.md5(password.encode()).hexdigest()

# SHA256 (Still bad for passwords - too fast)
sha256_hash = hashlib.sha256(password.encode()).hexdigest()

# bcrypt (Good - intentionally slow)
# The salt is automatically generated and embedded in the output
bcrypt_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))

# Argon2 (Best - memory-hard)
ph = PasswordHasher()
argon2_hash = ph.hash(password)

For the cracker, think about:

  • Dictionary attacks (common passwords list)
  • Brute force (try all combinations)
  • Rule-based attacks (password → p@ssw0rd)

Questions to answer:

  • Why does bcrypt embed the salt in its output?
  • What is a “cost factor” and how does it affect security?
  • Why is Argon2 “memory-hard” and why does that matter?
  • How long would it take to crack an 8-character random password with each method?

Learning milestones:

  1. Can hash passwords with multiple algorithms → You understand hash basics
  2. Can crack MD5/SHA256 quickly → You understand why fast hashes are bad
  3. bcrypt takes much longer → You understand work factors
  4. Understand the math → You can calculate crack times for password policies

Project 2: Session-Based Login System

  • File: LEARN_AUTHENTICATION_DEEP_DIVE.md
  • Main Programming Language: Python (Flask)
  • Alternative Programming Languages: Node.js, Go, Ruby
  • Coolness Level: Level 2: Practical but Forgettable
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Web Security / Sessions
  • Software or Tool: Session Auth System
  • Main Book: “The Web Application Hacker’s Handbook” by Stuttard & Pinto

What you’ll build: A complete session-based authentication system with login, logout, “remember me”, and session management—the traditional way web apps handle authentication.

Why it teaches authentication: Sessions are the foundation of web authentication. Understanding how cookies, session IDs, and server-side storage work together is essential before moving to tokens.

Core challenges you’ll face:

  • Generating secure session IDs → maps to randomness, entropy
  • Storing sessions server-side → maps to in-memory, Redis, database
  • Cookie security attributes → maps to HttpOnly, Secure, SameSite
  • Session lifecycle → maps to expiration, renewal, invalidation

Key Concepts:

  • Session management: “The Web Application Hacker’s Handbook” Chapter 7
  • Cookie security: OWASP Session Management Cheat Sheet
  • CSRF protection: “Web Security for Developers” Chapter 6 - Malcolm McDonald
  • Session fixation: OWASP Session Fixation documentation

Difficulty: Intermediate Time estimate: 1 week Prerequisites: Project 1 completed, basic web development, HTTP understanding

Real world outcome:

┌─────────────────────────────────────────────────────────────┐
│  Login System Demo                                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Email:    [john@example.com          ]                     │
│  Password: [••••••••••                ]                     │
│                                                             │
│  [✓] Remember me for 30 days                                │
│                                                             │
│  [ Login ]                                                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

# After login, inspect cookies:
Set-Cookie: session_id=a8f5e2d1c9b7...;
            HttpOnly;
            Secure;
            SameSite=Lax;
            Path=/;
            Max-Age=86400

# Server-side session store:
{
  "a8f5e2d1c9b7...": {
    "user_id": 42,
    "email": "john@example.com",
    "created_at": "2024-01-15T10:00:00Z",
    "last_activity": "2024-01-15T10:05:00Z",
    "ip_address": "192.168.1.1",
    "user_agent": "Mozilla/5.0..."
  }
}

# Admin panel showing active sessions:
┌────────────────────────────────────────────────────────────────────┐
│  Active Sessions for john@example.com                              │
├────────────────────────────────────────────────────────────────────┤
│  Session          Device              Location    Last Active      │
│  ──────────────────────────────────────────────────────────────────│
│  a8f5e2d1...      Chrome / Windows    New York    2 minutes ago    │
│  b7c4d3e2...      Safari / iPhone     New York    1 hour ago       │
│  c6b5a4f3...      Firefox / Linux     Boston      3 days ago       │
│                                                                    │
│  [Revoke] [Revoke] [Revoke]                                        │
│  [Revoke All Other Sessions]                                       │
└────────────────────────────────────────────────────────────────────┘

Implementation Hints:

Session ID generation (must be cryptographically random):

import secrets

# GOOD: Cryptographically secure
session_id = secrets.token_hex(32)  # 64 characters, 256 bits

# BAD: Predictable!
import random
session_id = str(random.randint(0, 1000000))  # DON'T DO THIS

Session storage options:

# Option 1: In-memory (loses sessions on restart)
sessions = {}

# Option 2: Redis (good for production)
import redis
r = redis.Redis()
r.setex(f"session:{session_id}", 86400, json.dumps(session_data))

# Option 3: Database (persistent but slower)
cursor.execute(
    "INSERT INTO sessions (id, user_id, data, expires_at) VALUES (?, ?, ?, ?)",
    (session_id, user_id, json.dumps(data), expires_at)
)

Cookie settings explained:

  • HttpOnly: JavaScript can’t access (prevents XSS stealing cookies)
  • Secure: Only sent over HTTPS
  • SameSite=Lax: Prevents CSRF for most cases
  • Path=/: Cookie sent for all paths
  • Max-Age/Expires: When cookie expires

Questions to think about:

  • What happens if an attacker gets a session ID?
  • How do you invalidate all sessions when a user changes their password?
  • What’s session fixation and how do you prevent it?
  • Should you regenerate session IDs on login? Why?

Learning milestones:

  1. Basic login/logout works → You understand session creation/destruction
  2. Cookies are secure → You understand cookie security attributes
  3. Remember me works → You understand session expiration
  4. Can revoke sessions → You understand session management

Project 3: JWT Authentication from Scratch

  • File: LEARN_AUTHENTICATION_DEEP_DIVE.md
  • Main Programming Language: Python
  • Alternative Programming Languages: Node.js, Go, Rust
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Tokens / Cryptographic Signatures
  • Software or Tool: JWT Implementation
  • Main Book: “Serious Cryptography” by Jean-Philippe Aumasson

What you’ll build: A JWT (JSON Web Token) implementation from scratch—no libraries—understanding exactly how tokens are created, signed, verified, and what can go wrong.

Why it teaches authentication: JWTs are everywhere in modern APIs. Building one from scratch reveals the cryptographic foundations (HMAC, RSA) and common vulnerabilities (algorithm confusion, secret weakness).

Core challenges you’ll face:

  • Understanding JWT structure → maps to header.payload.signature
  • Implementing HMAC signing → maps to symmetric cryptography
  • Implementing RSA signing → maps to asymmetric cryptography
  • Preventing algorithm confusion → maps to the “alg:none” attack

Key Concepts:

  • JWT structure: RFC 7519
  • HMAC: “Serious Cryptography” Chapter 7 - Aumasson
  • RSA signatures: “Serious Cryptography” Chapter 12 - Aumasson
  • JWT vulnerabilities: “Web Application Security” Chapter 8 - Andrew Hoffman

Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Project 2 completed, basic cryptography understanding

Real world outcome:

$ python jwt_tool.py create --payload '{"user_id": 42, "role": "admin"}' \
                            --secret "my-secret-key" \
                            --exp 3600

Created JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ1c2VyX2lkIjo0Miwicm9sZSI6ImFkbWluIiwiZXhwIjoxNzA1MzE1MjAwfQ.
K7PmhLqzN3nh4xQkX8D1yZ2mVwE9cR5aB6fG7hJ8iK0

Decoded:
  Header:  {"alg": "HS256", "typ": "JWT"}
  Payload: {"user_id": 42, "role": "admin", "exp": 1705315200}
  Signature: K7PmhLqzN3nh4xQkX8D1yZ2mVwE9cR5aB6fG7hJ8iK0 (valid)

$ python jwt_tool.py verify --token "eyJhbGc..." --secret "my-secret-key"

Token Verification:
  Signature: ✓ Valid
  Expiration: ✓ Not expired (expires in 58 minutes)
  Payload: {"user_id": 42, "role": "admin"}

$ python jwt_tool.py verify --token "eyJhbGc..." --secret "wrong-secret"

Token Verification:
  Signature: ✗ INVALID - Signature mismatch!
  WARNING: This token may have been tampered with

$ python jwt_tool.py attack --token "eyJhbGc..." --type alg-none
Attempting 'alg:none' attack...

Modified token (algorithm set to 'none'):
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.
eyJ1c2VyX2lkIjo0Miwicm9sZSI6ImFkbWluIn0.

If the server is vulnerable, this token will be accepted without signature!

Implementation Hints:

JWT structure:

┌─────────────────────────────────────────────────────────────┐
│                         JWT                                  │
├─────────────────┬─────────────────┬─────────────────────────┤
│     Header      │     Payload     │       Signature         │
│  (base64url)    │   (base64url)   │      (base64url)        │
├─────────────────┼─────────────────┼─────────────────────────┤
│ {"alg":"HS256"} │ {"user_id":42,  │ HMAC-SHA256(            │
│ {"typ":"JWT"}   │  "exp":1234...} │   base64(header) + "."  │
│                 │                 │   + base64(payload),    │
│                 │                 │   secret                │
│                 │                 │ )                       │
└─────────────────┴─────────────────┴─────────────────────────┘

Final token: base64(header).base64(payload).base64(signature)

Implementing HMAC-SHA256 signature:

import hmac
import hashlib
import base64
import json

def base64url_encode(data):
    if isinstance(data, str):
        data = data.encode('utf-8')
    return base64.urlsafe_b64encode(data).rstrip(b'=').decode('utf-8')

def create_jwt(payload, secret, algorithm='HS256'):
    header = {"alg": algorithm, "typ": "JWT"}

    # Encode header and payload
    header_b64 = base64url_encode(json.dumps(header))
    payload_b64 = base64url_encode(json.dumps(payload))

    # Create signature
    message = f"{header_b64}.{payload_b64}"

    if algorithm == 'HS256':
        signature = hmac.new(
            secret.encode('utf-8'),
            message.encode('utf-8'),
            hashlib.sha256
        ).digest()
    elif algorithm == 'RS256':
        # RSA signing (asymmetric)
        # ... different implementation

    signature_b64 = base64url_encode(signature)

    return f"{header_b64}.{payload_b64}.{signature_b64}"

Critical security questions:

  • What is the “alg: none” attack and why did it work on many servers?
  • Why should you NEVER trust the algorithm specified in the token header?
  • What’s the difference between HS256 (symmetric) and RS256 (asymmetric)?
  • When would you use RS256 vs HS256?
  • What’s a JWT refresh token pattern?

Learning milestones:

  1. Can create and verify JWTs → You understand the structure
  2. HMAC signing works → You understand symmetric signatures
  3. RSA signing works → You understand asymmetric signatures
  4. Understand attacks → You know how to build secure JWT systems

Project 4: OAuth 2.0 Client & Server

  • File: LEARN_AUTHENTICATION_DEEP_DIVE.md
  • Main Programming Language: Python (Flask)
  • Alternative Programming Languages: Node.js, Go, Ruby
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 2. The “Micro-SaaS / Pro Tool” (Solo-Preneur Potential)
  • Difficulty: Level 3: Advanced
  • Knowledge Area: OAuth / Federated Identity
  • Software or Tool: OAuth 2.0 Implementation
  • Main Book: “OAuth 2 in Action” by Justin Richer & Antonio Sanso

What you’ll build: Both an OAuth 2.0 authorization server (like Google’s login) and a client that uses it—implementing the Authorization Code flow with PKCE.

Why it teaches authentication: OAuth 2.0 is how “Login with Google/GitHub/Facebook” works. Building both sides reveals how tokens flow, why each step exists, and what attacks each step prevents.

Core challenges you’ll face:

  • Authorization Code flow → maps to redirect dance, code exchange
  • PKCE (Proof Key for Code Exchange) → maps to preventing authorization code interception
  • Token management → maps to access tokens, refresh tokens, scopes
  • Security considerations → maps to state parameter, redirect URI validation

Key Concepts:

  • OAuth 2.0 flows: “OAuth 2 in Action” Chapters 3-6 - Richer & Sanso
  • PKCE: RFC 7636
  • Security threats: “OAuth 2.0 Threat Model” (RFC 6819)
  • OpenID Connect: “OAuth 2 in Action” Chapter 13 - Richer & Sanso

Difficulty: Advanced Time estimate: 2-3 weeks Prerequisites: Projects 1-3 completed, HTTP understanding

Real world outcome:

┌─────────────────────────────────────────────────────────────────────┐
│                    Authorization Server (port 5000)                  │
└─────────────────────────────────────────────────────────────────────┘

Step 1: Client registration
$ curl -X POST http://localhost:5000/oauth/register \
       -d '{"name": "My App", "redirect_uri": "http://localhost:3000/callback"}'

{
  "client_id": "abc123",
  "client_secret": "secret456",
  "redirect_uris": ["http://localhost:3000/callback"]
}

┌─────────────────────────────────────────────────────────────────────┐
│                    Client Application (port 3000)                    │
└─────────────────────────────────────────────────────────────────────┘

[Login with OAuth Server]  ← User clicks this

Step 2: Redirect to authorization server
GET http://localhost:5000/oauth/authorize?
    response_type=code&
    client_id=abc123&
    redirect_uri=http://localhost:3000/callback&
    scope=profile+email&
    state=xyz789&
    code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&
    code_challenge_method=S256

┌─────────────────────────────────────────────────────────────────────┐
│              Authorization Server - Consent Screen                   │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  "My App" wants to access your account                               │
│                                                                      │
│  This will allow the application to:                                 │
│    ✓ View your profile information                                   │
│    ✓ View your email address                                         │
│                                                                      │
│  [Deny]                                      [Allow]                 │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

Step 3: Redirect back with code
HTTP/1.1 302 Found
Location: http://localhost:3000/callback?
          code=AUTH_CODE_HERE&
          state=xyz789

Step 4: Exchange code for tokens (server-side)
POST http://localhost:5000/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=AUTH_CODE_HERE&
redirect_uri=http://localhost:3000/callback&
client_id=abc123&
client_secret=secret456&
code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

Response:
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "xRxGGEpVawiUak6He367W3oeOfh...",
  "scope": "profile email"
}

Step 5: Use access token
GET http://localhost:5000/api/userinfo
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

{
  "sub": "user123",
  "name": "John Doe",
  "email": "john@example.com"
}

Implementation Hints:

The Authorization Code flow with PKCE:

CLIENT                           AUTHORIZATION SERVER

1. Generate code_verifier (random string)
   code_challenge = BASE64URL(SHA256(code_verifier))

2. Redirect user to /authorize with code_challenge
   ─────────────────────────────────────────────────>

                                 3. User logs in, consents

                                 4. Store code_challenge with auth code

5. Receive auth code via redirect
   <─────────────────────────────────────────────────

6. POST to /token with code + code_verifier
   ─────────────────────────────────────────────────>

                                 7. Verify: SHA256(code_verifier) == stored challenge

8. Receive access_token + refresh_token
   <─────────────────────────────────────────────────

PKCE implementation:

import secrets
import hashlib
import base64

# Client generates these
code_verifier = secrets.token_urlsafe(32)  # Random 43-128 chars
code_challenge = base64.urlsafe_b64encode(
    hashlib.sha256(code_verifier.encode()).digest()
).rstrip(b'=').decode()

# Server verifies by recomputing
def verify_pkce(code_verifier, stored_challenge):
    computed = base64.urlsafe_b64encode(
        hashlib.sha256(code_verifier.encode()).digest()
    ).rstrip(b'=').decode()
    return secrets.compare_digest(computed, stored_challenge)

Security questions:

  • Why do we need the state parameter? (CSRF prevention)
  • Why is PKCE necessary even with client_secret? (native apps can’t keep secrets)
  • What’s the difference between access tokens and refresh tokens?
  • What happens if you skip redirect_uri validation?

Learning milestones:

  1. Authorization code flow works → You understand the redirect dance
  2. PKCE implemented → You understand code interception prevention
  3. Refresh tokens work → You understand token lifecycle
  4. Scopes enforced → You understand OAuth authorization

Project 5: TOTP Authenticator App

  • File: LEARN_AUTHENTICATION_DEEP_DIVE.md
  • Main Programming Language: Python
  • Alternative Programming Languages: Go, Rust, JavaScript
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: MFA / TOTP / Cryptography
  • Software or Tool: Authenticator App
  • Main Book: “Serious Cryptography” by Jean-Philippe Aumasson

What you’ll build: A TOTP (Time-based One-Time Password) authenticator like Google Authenticator—implementing RFC 6238 from scratch to understand how those 6-digit codes work.

Why it teaches authentication: TOTP is the most common form of 2FA. Building it reveals how a shared secret and time create synchronized codes without network communication—pure cryptographic magic.

Core challenges you’ll face:

  • Implementing HMAC-SHA1 → maps to message authentication codes
  • Time-based code generation → maps to time steps, synchronization
  • QR code parsing → maps to otpauth:// URI scheme
  • Handling time drift → maps to accepting codes from adjacent windows

Key Concepts:

  • TOTP algorithm: RFC 6238
  • HOTP (underlying algorithm): RFC 4226
  • HMAC: “Serious Cryptography” Chapter 7 - Aumasson
  • Key provisioning: Google Authenticator Key URI Format

Difficulty: Intermediate Time estimate: 1 week Prerequisites: Project 1 completed, understanding of HMAC

Real world outcome:

$ python totp_auth.py add --name "GitHub" \
    --secret "JBSWY3DPEHPK3PXP" \
    --issuer "GitHub"

Added account: GitHub (GitHub)
Current code: 123456 (expires in 23s)

$ python totp_auth.py add --qr github_qr.png

Scanned QR code:
  otpauth://totp/GitHub:john@example.com?
    secret=JBSWY3DPEHPK3PXP&
    issuer=GitHub&
    algorithm=SHA1&
    digits=6&
    period=30

Added account: GitHub:john@example.com
Current code: 123456

$ python totp_auth.py list

┌────────────────────────────────────────────────────────────┐
│  Your Accounts                                              │
├────────────────────────────────────────────────────────────┤
│                                                             │
│  GitHub                     847 293    ████████░░░░ (18s)   │
│  john@example.com                                           │
│                                                             │
│  AWS                        156 832    ██████░░░░░░ (12s)   │
│  admin@company.com                                          │
│                                                             │
│  Google                     492 187    ████░░░░░░░░ (8s)    │
│  john.doe@gmail.com                                         │
│                                                             │
└────────────────────────────────────────────────────────────┘

$ python totp_auth.py verify --name "GitHub" --code 847293

✓ Code is valid!

$ python totp_auth.py explain --secret "JBSWY3DPEHPK3PXP"

TOTP Calculation Breakdown:
  1. Current Unix time: 1705315200
  2. Time step (T): floor(1705315200 / 30) = 56843840
  3. T as 8-byte big-endian: 0x0000000003642280
  4. HMAC-SHA1(secret, T): a4f2c8e1b5d7f3a9c2e4...
  5. Offset (last nibble & 0x0F): 12
  6. Extract 4 bytes at offset 12: 0x4a3b2c1d
  7. Mask MSB: 0x4a3b2c1d & 0x7FFFFFFF = 1244978205
  8. Modulo 10^6: 978205
  9. Pad to 6 digits: 978205

  Current code: 978205 (but we're mid-calculation, actual is 847293)

Implementation Hints:

TOTP algorithm (RFC 6238):

import hmac
import hashlib
import struct
import time
import base64

def generate_totp(secret_base32, time_step=30, digits=6):
    # 1. Decode the base32 secret
    secret = base64.b32decode(secret_base32.upper())

    # 2. Calculate time counter
    counter = int(time.time()) // time_step

    # 3. Convert counter to 8-byte big-endian
    counter_bytes = struct.pack('>Q', counter)

    # 4. Calculate HMAC-SHA1
    hmac_result = hmac.new(secret, counter_bytes, hashlib.sha1).digest()

    # 5. Dynamic truncation
    offset = hmac_result[-1] & 0x0F
    code_int = struct.unpack('>I', hmac_result[offset:offset+4])[0]
    code_int &= 0x7FFFFFFF  # Mask MSB

    # 6. Get the final code
    code = code_int % (10 ** digits)

    return str(code).zfill(digits)

# Example
secret = "JBSWY3DPEHPK3PXP"  # Base32 encoded
code = generate_totp(secret)
print(f"Current code: {code}")

QR code URI format:

otpauth://totp/LABEL?secret=SECRET&issuer=ISSUER&algorithm=SHA1&digits=6&period=30

Questions to understand:

  • Why does TOTP use a 30-second window?
  • How do you handle clock drift between server and authenticator?
  • What’s the difference between TOTP and HOTP?
  • Why is the shared secret sent during setup but never again?

Learning milestones:

  1. Generate codes that match Google Authenticator → You understand the algorithm
  2. Parse QR codes → You understand the otpauth:// format
  3. Handle time windows → You understand synchronization
  4. Build a CLI or GUI app → You have a working authenticator

Project 6: WebAuthn/Passkey Implementation

  • File: LEARN_AUTHENTICATION_DEEP_DIVE.md
  • Main Programming Language: Python (Flask) + JavaScript
  • Alternative Programming Languages: Node.js, Go
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 3. The “Service & Support” Model (B2B Utility)
  • Difficulty: Level 3: Advanced
  • Knowledge Area: WebAuthn / FIDO2 / Public Key Cryptography
  • Software or Tool: Passkey Authentication System
  • Main Book: “Serious Cryptography” by Jean-Philippe Aumasson

What you’ll build: A passwordless authentication system using WebAuthn (the standard behind Passkeys)—where users authenticate with their fingerprint, face, or security key instead of passwords.

Why it teaches authentication: WebAuthn is the future of authentication. It uses public-key cryptography instead of shared secrets, making it phishing-resistant. Building it reveals how challenge-response authentication works at the cryptographic level.

Core challenges you’ll face:

  • Understanding attestation → maps to proving the authenticator is genuine
  • Challenge-response flow → maps to preventing replay attacks
  • Public key registration → maps to storing credentials server-side
  • Assertion verification → maps to signature verification

Key Concepts:

  • WebAuthn specification: W3C WebAuthn Recommendation
  • FIDO2/CTAP: FIDO Alliance specifications
  • Public key cryptography: “Serious Cryptography” Chapter 11 - Aumasson
  • CBOR encoding: RFC 8949

Difficulty: Advanced Time estimate: 2-3 weeks Prerequisites: Projects 1-3 completed, public-key cryptography basics

Real world outcome:

┌─────────────────────────────────────────────────────────────────┐
│                    Passkey Demo                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Welcome! Choose how to sign in:                                 │
│                                                                  │
│  [🔐 Sign in with Passkey]                                       │
│                                                                  │
│  ───────────────────────────────────────────────────────────── │
│                                                                  │
│  [Register new Passkey]                                          │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

# Registration flow:
1. Server generates challenge: "a3f8c2e1b5d7..."

2. Browser prompts for biometric/security key:
   ┌─────────────────────────────────────┐
   │  🔐 Create a passkey               │
   │                                     │
   │  Use Touch ID to create a passkey  │
   │  for example.com                   │
   │                                     │
   │  [Cancel]              [Continue]  │
   └─────────────────────────────────────┘

3. Authenticator creates key pair, signs challenge

4. Server receives and verifies:
   {
     "id": "base64-credential-id",
     "rawId": "base64-raw-id",
     "type": "public-key",
     "response": {
       "clientDataJSON": "...",
       "attestationObject": "..."  // Contains public key
     }
   }

5. Server stores public key, credential ID, user association

# Login flow:
1. Server sends challenge with allowed credential IDs

2. Browser prompts:
   ┌─────────────────────────────────────┐
   │  🔐 Sign in with passkey           │
   │                                     │
   │  Use Touch ID to sign in to        │
   │  example.com                       │
   │                                     │
   │        👆 Touch ID                 │
   └─────────────────────────────────────┘

3. Authenticator signs challenge with private key

4. Server verifies signature using stored public key

5. ✓ Authenticated! No password needed, phishing-resistant

Implementation Hints:

Registration (simplified):

# Server-side: Generate challenge
challenge = secrets.token_bytes(32)

# Client-side: Call WebAuthn API
const credential = await navigator.credentials.create({
    publicKey: {
        challenge: challenge,
        rp: { name: "My App", id: "example.com" },
        user: {
            id: userId,
            name: "john@example.com",
            displayName: "John Doe"
        },
        pubKeyCredParams: [
            { alg: -7, type: "public-key" },   // ES256
            { alg: -257, type: "public-key" }  // RS256
        ],
        authenticatorSelection: {
            authenticatorAttachment: "platform",  // Built-in (Touch ID, Windows Hello)
            userVerification: "required"
        }
    }
});

# Server-side: Verify and store
# 1. Decode attestationObject (CBOR)
# 2. Verify challenge matches
# 3. Extract public key from authData
# 4. Verify attestation (optional for passkeys)
# 5. Store credential_id + public_key + user_id

Authentication (simplified):

# Server-side: Generate challenge, specify allowed credentials
challenge = secrets.token_bytes(32)
allowed_credentials = get_user_credentials(user_id)

# Client-side: Call WebAuthn API
const assertion = await navigator.credentials.get({
    publicKey: {
        challenge: challenge,
        rpId: "example.com",
        allowCredentials: allowedCredentials.map(c => ({
            id: c.id,
            type: "public-key"
        })),
        userVerification: "required"
    }
});

# Server-side: Verify signature
# 1. Get stored public key for credential_id
# 2. Verify clientDataJSON contains correct challenge
# 3. Verify signature over authenticatorData + clientDataHash
# 4. Check signature counter (replay protection)
# 5. User is authenticated!

Key concepts to understand:

  • Why is WebAuthn phishing-resistant? (Origin binding)
  • What’s the difference between “platform” and “cross-platform” authenticators?
  • What is attestation and when do you need it?
  • How does the signature counter prevent cloned authenticators?

Learning milestones:

  1. Registration flow works → You understand credential creation
  2. Authentication flow works → You understand challenge-response
  3. Signature verification works → You understand the cryptography
  4. Multiple authenticators work → You understand credential management

Project 7: Secure Password Reset System

  • File: LEARN_AUTHENTICATION_DEEP_DIVE.md
  • Main Programming Language: Python (Flask)
  • Alternative Programming Languages: Node.js, Go, Ruby
  • Coolness Level: Level 2: Practical but Forgettable
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Token Security / Email Verification
  • Software or Tool: Password Reset Flow
  • Main Book: “The Web Application Hacker’s Handbook” by Stuttard & Pinto

What you’ll build: A complete password reset system with secure token generation, rate limiting, and proper expiration—covering one of the most attacked features in web applications.

Why it teaches authentication: Password reset is where many authentication systems fail. Understanding how to securely generate tokens, handle timing attacks, and prevent account enumeration is essential for building secure systems.

Core challenges you’ll face:

  • Secure token generation → maps to cryptographic randomness, token length
  • Token storage → maps to hashing tokens, expiration
  • Rate limiting → maps to preventing brute force, account enumeration
  • Constant-time comparison → maps to timing attacks

Key Concepts:

  • Password reset vulnerabilities: “The Web Application Hacker’s Handbook” Chapter 7
  • Timing attacks: “Serious Cryptography” Chapter 4 - Aumasson
  • Rate limiting: OWASP Testing Guide
  • Account enumeration: OWASP Authentication Cheat Sheet

Difficulty: Intermediate Time estimate: 1 week Prerequisites: Project 2 completed, email sending basics

Real world outcome:

Password Reset Flow:

1. User requests reset:
   POST /forgot-password
   {"email": "john@example.com"}

   Response (ALWAYS same, prevents enumeration):
   {"message": "If an account exists, a reset link has been sent."}

2. Server generates token:
   - token = secrets.token_urlsafe(32)  # 256 bits
   - token_hash = SHA256(token)         # Store hash, not token
   - expiry = now + 1 hour
   - Store: (user_id, token_hash, expiry, used=false)

3. Email sent:
   Subject: Password Reset Request

   Click to reset your password:
   https://example.com/reset-password?token=Ux7Kz...

   This link expires in 1 hour.
   If you didn't request this, ignore this email.

4. User clicks link:
   GET /reset-password?token=Ux7Kz...

   Server:
   - Hash the token: SHA256(received_token)
   - Look up by hash (NOT by raw token!)
   - Verify not expired
   - Verify not already used
   - Show password reset form

5. User submits new password:
   POST /reset-password
   {"token": "Ux7Kz...", "password": "newSecurePassword123!"}

   Server:
   - Verify token again (same checks)
   - Update password (hash with bcrypt)
   - Mark token as used
   - Invalidate all sessions for user
   - Send confirmation email

Security log:
[2024-01-15 10:23:45] Password reset requested for john@example.com from 1.2.3.4
[2024-01-15 10:24:12] Password reset token used for user_id=42 from 1.2.3.4
[2024-01-15 10:24:12] All sessions invalidated for user_id=42
[2024-01-15 10:24:13] Password changed confirmation sent to john@example.com

Implementation Hints:

Token generation and storage:

import secrets
import hashlib
from datetime import datetime, timedelta

def create_reset_token(user_id):
    # Generate cryptographically secure token
    token = secrets.token_urlsafe(32)

    # Store hash, not the token itself!
    # (if database is compromised, attacker can't use tokens)
    token_hash = hashlib.sha256(token.encode()).hexdigest()

    expiry = datetime.utcnow() + timedelta(hours=1)

    db.execute("""
        INSERT INTO password_resets (user_id, token_hash, expires_at, used)
        VALUES (?, ?, ?, false)
    """, (user_id, token_hash, expiry))

    return token  # Send this in email

def verify_reset_token(token):
    token_hash = hashlib.sha256(token.encode()).hexdigest()

    reset = db.execute("""
        SELECT * FROM password_resets
        WHERE token_hash = ? AND expires_at > ? AND used = false
    """, (token_hash, datetime.utcnow())).fetchone()

    return reset

Preventing timing attacks:

import secrets

# BAD: Early return reveals whether email exists
if not user_exists(email):
    return {"message": "No such user"}  # Attacker knows!

# GOOD: Same response and timing regardless
def request_reset(email):
    user = get_user(email)

    if user:
        token = create_reset_token(user.id)
        send_reset_email(email, token)

    # Always return same response
    # Add artificial delay if needed for timing consistency
    return {"message": "If an account exists, a reset link has been sent."}

Rate limiting:

from functools import wraps
import time

rate_limits = {}

def rate_limit(max_requests=5, window_seconds=300):
    def decorator(f):
        @wraps(f)
        def wrapped(*args, **kwargs):
            key = f"{request.remote_addr}:{request.endpoint}"
            now = time.time()

            if key in rate_limits:
                requests, window_start = rate_limits[key]
                if now - window_start > window_seconds:
                    rate_limits[key] = (1, now)
                elif requests >= max_requests:
                    return {"error": "Too many requests"}, 429
                else:
                    rate_limits[key] = (requests + 1, window_start)
            else:
                rate_limits[key] = (1, now)

            return f(*args, **kwargs)
        return wrapped
    return decorator

@rate_limit(max_requests=3, window_seconds=300)
def forgot_password():
    ...

Learning milestones:

  1. Token generation secure → You understand cryptographic randomness
  2. Token storage secure → You understand why to hash tokens
  3. Same response for all cases → You understand account enumeration
  4. Rate limiting works → You understand brute force prevention

Project 8: Single Sign-On (SSO) with SAML

  • File: LEARN_AUTHENTICATION_DEEP_DIVE.md
  • Main Programming Language: Python
  • Alternative Programming Languages: Java, Node.js, C#
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 3. The “Service & Support” Model (B2B Utility)
  • Difficulty: Level 4: Expert
  • Knowledge Area: SAML / Enterprise SSO / XML Security
  • Software or Tool: SAML Identity Provider
  • Main Book: “OAuth 2 in Action” by Justin Richer & Antonio Sanso

What you’ll build: A SAML 2.0 Identity Provider (IdP) and Service Provider (SP) implementation—the enterprise standard for single sign-on used by Okta, Azure AD, etc.

Why it teaches authentication: SAML is complex but ubiquitous in enterprise environments. Understanding it reveals XML signature verification, assertion handling, and federated identity concepts that OAuth builds upon.

Core challenges you’ll face:

  • XML signature creation/verification → maps to xmldsig, canonicalization
  • SAML assertion structure → maps to claims, conditions, audience
  • Binding types → maps to POST, Redirect, Artifact
  • Metadata exchange → maps to entity descriptors, certificates

Key Concepts:

  • SAML 2.0 specification: OASIS SAML Technical Overview
  • XML Signature: W3C XML Signature Syntax
  • SAML bindings: OASIS SAML Bindings spec
  • SAML security: “Federated Identity Primer” - Shibboleth docs

Difficulty: Expert Time estimate: 3-4 weeks Prerequisites: OAuth project completed, XML basics, public-key cryptography

Real world outcome:

SAML SSO Flow:

1. User accesses Service Provider:
   GET https://app.example.com/dashboard

2. SP redirects to IdP with AuthnRequest:
   GET https://idp.company.com/sso?
       SAMLRequest=base64(deflate(
         <samlp:AuthnRequest
           ID="_abc123"
           Version="2.0"
           IssueInstant="2024-01-15T10:00:00Z"
           Destination="https://idp.company.com/sso"
           AssertionConsumerServiceURL="https://app.example.com/acs">
           <saml:Issuer>https://app.example.com</saml:Issuer>
         </samlp:AuthnRequest>
       ))&
       RelayState=/dashboard

3. IdP authenticates user (login page)

4. IdP redirects back with signed Response:
   POST https://app.example.com/acs

   SAMLResponse=base64(
     <samlp:Response ID="_xyz789" InResponseTo="_abc123">
       <saml:Issuer>https://idp.company.com</saml:Issuer>
       <ds:Signature>...</ds:Signature>
       <saml:Assertion>
         <saml:Issuer>https://idp.company.com</saml:Issuer>
         <saml:Subject>
           <saml:NameID>john@company.com</saml:NameID>
         </saml:Subject>
         <saml:Conditions NotBefore="..." NotOnOrAfter="...">
           <saml:AudienceRestriction>
             <saml:Audience>https://app.example.com</saml:Audience>
           </saml:AudienceRestriction>
         </saml:Conditions>
         <saml:AttributeStatement>
           <saml:Attribute Name="firstName">
             <saml:AttributeValue>John</saml:AttributeValue>
           </saml:Attribute>
           <saml:Attribute Name="groups">
             <saml:AttributeValue>Engineering</saml:AttributeValue>
             <saml:AttributeValue>Admins</saml:AttributeValue>
           </saml:Attribute>
         </saml:AttributeStatement>
       </saml:Assertion>
     </samlp:Response>
   )

5. SP validates response:
   ✓ Signature valid (using IdP's public key from metadata)
   ✓ InResponseTo matches our request
   ✓ NotBefore <= now <= NotOnOrAfter
   ✓ Audience matches our entity ID
   ✓ Issuer is trusted IdP

6. SP creates session, redirects to RelayState
   → User is logged in!

Implementation Hints:

XML Signature verification (the hard part):

from lxml import etree
from signxml import XMLVerifier

def verify_saml_response(saml_response_b64, idp_certificate):
    # Decode
    saml_xml = base64.b64decode(saml_response_b64)
    doc = etree.fromstring(saml_xml)

    # Find signature
    signature = doc.find('.//{http://www.w3.org/2000/09/xmldsig#}Signature')

    # Verify
    verifier = XMLVerifier()
    verified_data = verifier.verify(
        doc,
        x509_cert=idp_certificate
    )

    # IMPORTANT: Use verified_data, not the original doc!
    # (prevents signature wrapping attacks)
    return verified_data

SAML assertion validation checklist:

def validate_assertion(assertion, expected_audience, request_id):
    # 1. Check timing conditions
    conditions = assertion.find('saml:Conditions')
    not_before = parse_datetime(conditions.get('NotBefore'))
    not_on_or_after = parse_datetime(conditions.get('NotOnOrAfter'))

    now = datetime.utcnow()
    if not (not_before <= now <= not_on_or_after):
        raise ValueError("Assertion timing invalid")

    # 2. Check audience
    audience = assertion.find('.//saml:Audience').text
    if audience != expected_audience:
        raise ValueError("Wrong audience")

    # 3. Check InResponseTo (prevents replay)
    response = assertion.getparent()
    if response.get('InResponseTo') != request_id:
        raise ValueError("Response doesn't match request")

    # 4. Check issuer
    issuer = assertion.find('saml:Issuer').text
    if issuer not in trusted_idps:
        raise ValueError("Unknown IdP")

    return True

Key security considerations:

  • Signature wrapping attacks: Always use the verified signed data
  • XML injection: Use a safe XML parser
  • Replay attacks: Check InResponseTo and track used assertion IDs
  • Time synchronization: Allow small clock skew

Learning milestones:

  1. Can generate AuthnRequest → You understand SAML request format
  2. Can verify signatures → You understand XML signatures
  3. Can parse assertions → You understand SAML response structure
  4. Full SSO flow works → You understand federated identity

Project 9: API Key Authentication System

  • File: LEARN_AUTHENTICATION_DEEP_DIVE.md
  • Main Programming Language: Python (FastAPI)
  • Alternative Programming Languages: Go, Node.js, Rust
  • Coolness Level: Level 2: Practical but Forgettable
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: API Security / Key Management
  • Software or Tool: API Key Management System
  • Main Book: “API Security in Action” by Neil Madden

What you’ll build: A complete API key management system with key generation, rotation, rate limiting per key, and scope-based permissions—like Stripe or GitHub’s API key systems.

Why it teaches authentication: API keys are different from user passwords—they’re long-lived, need rotation, and require different security considerations. Understanding API authentication is essential for building modern services.

Core challenges you’ll face:

  • Key generation → maps to prefix design, entropy, storage
  • Key rotation → maps to overlapping validity, deprecation
  • Rate limiting per key → maps to tiered access, abuse prevention
  • Scopes and permissions → maps to least privilege, granular access

Key Concepts:

  • API key design: “API Security in Action” Chapter 5 - Neil Madden
  • Rate limiting algorithms: Token bucket, sliding window
  • Key prefixes: Stripe’s approach (sk_live_, sk_test_)
  • Key storage: Hashing API keys like passwords

Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: JWT project completed, REST API basics

Real world outcome:

# Create API key
$ curl -X POST https://api.example.com/v1/api-keys \
       -H "Authorization: Bearer user-jwt-token" \
       -d '{"name": "Production Key", "scopes": ["read:users", "write:orders"]}'

{
  "id": "key_abc123",
  "name": "Production Key",
  "key": "sk_live_7Kx9Pz2mQ4wR8nL5tY3vB6",  # Only shown ONCE!
  "prefix": "sk_live_7Kx9",
  "scopes": ["read:users", "write:orders"],
  "created_at": "2024-01-15T10:00:00Z",
  "last_used_at": null,
  "rate_limit": {
    "requests_per_minute": 100,
    "requests_per_day": 10000
  }
}

WARNING: Save this key now. You won't be able to see it again!

# Use API key
$ curl https://api.example.com/v1/users \
       -H "Authorization: Bearer sk_live_7Kx9Pz2mQ4wR8nL5tY3vB6"

{
  "users": [...]
}

# Rate limit headers in response
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99
X-RateLimit-Reset: 1705315260

# List keys (key values hidden)
$ curl https://api.example.com/v1/api-keys \
       -H "Authorization: Bearer user-jwt-token"

{
  "keys": [
    {
      "id": "key_abc123",
      "name": "Production Key",
      "prefix": "sk_live_7Kx9",  # Only prefix shown
      "scopes": ["read:users", "write:orders"],
      "created_at": "2024-01-15T10:00:00Z",
      "last_used_at": "2024-01-15T10:05:00Z",
      "request_count_30d": 15234
    }
  ]
}

# Rotate key (creates new, deprecates old)
$ curl -X POST https://api.example.com/v1/api-keys/key_abc123/rotate

{
  "new_key": {
    "id": "key_xyz789",
    "key": "sk_live_3Mn4Op5qR6sT7uV8wX9yZ0",
    "prefix": "sk_live_3Mn4"
  },
  "old_key": {
    "id": "key_abc123",
    "status": "deprecated",
    "expires_at": "2024-01-22T10:00:00Z"  # 7 days grace period
  }
}

Implementation Hints:

API key generation (with identifiable prefix):

import secrets
import hashlib

def generate_api_key(environment='live'):
    # Format: sk_{environment}_{random}
    # Example: sk_live_7Kx9Pz2mQ4wR8nL5tY3vB6

    prefix = f"sk_{environment}_"
    random_part = secrets.token_urlsafe(18)  # ~24 chars

    full_key = prefix + random_part
    return full_key

def store_api_key(user_id, key, name, scopes):
    # Store HASH of key, not the key itself
    # (same principle as passwords)
    key_hash = hashlib.sha256(key.encode()).hexdigest()

    # Store prefix for identification
    prefix = key[:12]

    db.execute("""
        INSERT INTO api_keys (user_id, key_hash, prefix, name, scopes)
        VALUES (?, ?, ?, ?, ?)
    """, (user_id, key_hash, prefix, name, json.dumps(scopes)))

def verify_api_key(key):
    key_hash = hashlib.sha256(key.encode()).hexdigest()
    return db.execute(
        "SELECT * FROM api_keys WHERE key_hash = ? AND revoked = false",
        (key_hash,)
    ).fetchone()

Rate limiting per key (token bucket):

import time
import redis

r = redis.Redis()

def check_rate_limit(key_id, limit_per_minute):
    now = time.time()
    window_start = now - 60

    pipe = r.pipeline()
    key = f"ratelimit:{key_id}"

    # Remove old requests
    pipe.zremrangebyscore(key, 0, window_start)
    # Count requests in window
    pipe.zcard(key)
    # Add current request
    pipe.zadd(key, {str(now): now})
    # Set expiry
    pipe.expire(key, 60)

    results = pipe.execute()
    request_count = results[1]

    if request_count >= limit_per_minute:
        return False, limit_per_minute - request_count

    return True, limit_per_minute - request_count - 1

Scope checking:

def require_scopes(*required_scopes):
    def decorator(f):
        @wraps(f)
        def wrapped(*args, **kwargs):
            api_key = get_api_key_from_request()
            key_scopes = set(api_key.scopes)

            for scope in required_scopes:
                if scope not in key_scopes:
                    return {"error": f"Missing scope: {scope}"}, 403

            return f(*args, **kwargs)
        return wrapped
    return decorator

@require_scopes("read:users")
def list_users():
    ...

Learning milestones:

  1. Key generation with prefix works → You understand key design
  2. Keys are hashed in storage → You understand key security
  3. Rate limiting per key works → You understand abuse prevention
  4. Scope checking works → You understand authorization

Project 10: Auth Audit Logger & Anomaly Detector

  • File: LEARN_AUTHENTICATION_DEEP_DIVE.md
  • Main Programming Language: Python
  • Alternative Programming Languages: Go, Rust, Elixir
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 3. The “Service & Support” Model (B2B Utility)
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Security Monitoring / Anomaly Detection
  • Software or Tool: Auth Security Monitor
  • Main Book: “The Practice of Network Security Monitoring” by Richard Bejtlich

What you’ll build: An authentication audit logging system that detects suspicious patterns—impossible travel, credential stuffing, brute force, account takeover attempts—and alerts in real-time.

Why it teaches authentication: Authentication isn’t just about letting people in—it’s about detecting when something is wrong. Understanding attack patterns helps you build systems that can defend themselves.

Core challenges you’ll face:

  • Comprehensive logging → maps to what to log, privacy considerations
  • Pattern detection → maps to velocity checks, geographic analysis
  • Real-time alerting → maps to stream processing, thresholds
  • False positive reduction → maps to baselining, context

Key Concepts:

  • Auth event logging: “Security Monitoring” Chapter 5 - Chris Fry
  • Anomaly detection: “Practical Machine Learning” Chapter 10
  • Threat indicators: MITRE ATT&CK (Credential Access)
  • SIEM integration: “Security Information and Event Management” - David Miller

Difficulty: Advanced Time estimate: 2-3 weeks Prerequisites: Session and API key projects completed

Real world outcome:

┌────────────────────────────────────────────────────────────────────────┐
│  Authentication Security Dashboard                                      │
├────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  Last 24 Hours                                                          │
│  ─────────────────────────────────────────────────────────────────────│
│  Total auth events:     45,678                                          │
│  Successful logins:     44,123 (96.6%)                                  │
│  Failed logins:         1,555 (3.4%)                                    │
│  Blocked attempts:      234                                             │
│                                                                         │
│  ⚠️  ACTIVE ALERTS                                                      │
│  ─────────────────────────────────────────────────────────────────────│
│                                                                         │
│  🔴 HIGH: Credential Stuffing Detected                                  │
│     Pattern: 1,234 failed logins from 156 IPs in 10 minutes             │
│     Targeting: Sequential email addresses (user1@, user2@, user3@...)   │
│     Action: Rate limiting enabled, IPs blocked                          │
│     Time: 2 minutes ago                                                 │
│                                                                         │
│  🟠 MEDIUM: Impossible Travel for user john@example.com                 │
│     Login from New York at 10:00 AM                                     │
│     Login from Tokyo at 10:15 AM (8,500 miles in 15 minutes)            │
│     Action: Session flagged, step-up auth required                      │
│     Time: 15 minutes ago                                                │
│                                                                         │
│  🟡 LOW: Unusual Login Pattern for sarah@example.com                    │
│     First login from Linux device (usually Windows)                     │
│     Different browser fingerprint                                       │
│     Action: Logged for review                                           │
│     Time: 1 hour ago                                                    │
│                                                                         │
│  RECENT EVENTS                                                          │
│  ─────────────────────────────────────────────────────────────────────│
│  Time          User                Event        Location      Risk     │
│  10:23:45      john@...            login_success  New York     ✓       │
│  10:23:44      attacker@...        login_failed   Unknown      ⚠️      │
│  10:23:43      sarah@...           mfa_success    London       ✓       │
│  10:23:42      admin@...           password_reset San Francisco ✓       │
│                                                                         │
└────────────────────────────────────────────────────────────────────────┘

$ auth-audit query --user john@example.com --days 30

Authentication history for john@example.com:
  Total events: 234
  Successful logins: 198
  Failed logins: 12
  Password resets: 1
  MFA enrollments: 1

  Devices used: 3
    - Chrome/Windows (primary, 156 logins)
    - Safari/iPhone (45 logins)
    - Firefox/Linux (first seen 2 days ago, 3 logins) ⚠️

  Locations: 2
    - New York, USA (home, 220 events)
    - Boston, USA (12 events)

  Risk score: 23/100 (LOW)
  Anomalies detected: 2
    - New device (Firefox/Linux) - 2 days ago
    - Login at unusual time (3:00 AM) - 5 days ago

Implementation Hints:

Event logging structure:

from dataclasses import dataclass
from datetime import datetime
from typing import Optional

@dataclass
class AuthEvent:
    timestamp: datetime
    event_type: str  # login_success, login_failed, logout, password_reset, mfa_*
    user_id: Optional[str]
    email: str
    ip_address: str
    user_agent: str
    device_fingerprint: str
    geo_location: dict  # {country, city, lat, lon}
    success: bool
    failure_reason: Optional[str]
    mfa_method: Optional[str]
    session_id: Optional[str]
    risk_score: int
    metadata: dict

def log_auth_event(event: AuthEvent):
    # Store in time-series database (InfluxDB, TimescaleDB)
    # or append-only log (Kafka, Elasticsearch)
    pass

Anomaly detection rules:

def detect_impossible_travel(user_id, new_location, new_timestamp):
    """Detect if user logged in from two far locations too quickly."""
    last_login = get_last_login(user_id)
    if not last_login:
        return None

    distance_km = haversine_distance(
        last_login.location, new_location
    )
    time_diff_hours = (new_timestamp - last_login.timestamp).total_seconds() / 3600

    # Max reasonable travel speed (commercial flight ~900 km/h)
    max_travel_km = time_diff_hours * 1000

    if distance_km > max_travel_km:
        return Alert(
            type="impossible_travel",
            severity="medium",
            message=f"Login from {new_location.city} after {last_login.location.city} "
                    f"({distance_km:.0f}km in {time_diff_hours:.1f}h)",
            user_id=user_id
        )
    return None

def detect_credential_stuffing(time_window_minutes=10):
    """Detect mass login attempts with different credentials."""
    recent_failures = get_recent_failures(minutes=time_window_minutes)

    # Group by IP
    by_ip = group_by(recent_failures, 'ip_address')

    for ip, attempts in by_ip.items():
        unique_usernames = len(set(a.email for a in attempts))

        # Many different usernames from same IP = credential stuffing
        if unique_usernames > 10:
            return Alert(
                type="credential_stuffing",
                severity="high",
                message=f"IP {ip} tried {unique_usernames} different accounts",
                ip_address=ip
            )
    return None

def detect_brute_force(user_id, time_window_minutes=15):
    """Detect repeated failed logins to same account."""
    recent_failures = get_failures_for_user(user_id, minutes=time_window_minutes)

    if len(recent_failures) >= 5:
        unique_ips = len(set(f.ip_address for f in recent_failures))

        return Alert(
            type="brute_force",
            severity="high" if unique_ips == 1 else "medium",
            message=f"{len(recent_failures)} failed attempts from {unique_ips} IPs",
            user_id=user_id
        )
    return None

Risk scoring:

def calculate_risk_score(event: AuthEvent, user_profile: UserProfile):
    score = 0

    # New device
    if event.device_fingerprint not in user_profile.known_devices:
        score += 20

    # New location
    if not is_near_known_locations(event.geo_location, user_profile.locations):
        score += 25

    # Unusual time
    usual_hours = user_profile.typical_login_hours
    if event.timestamp.hour not in usual_hours:
        score += 10

    # VPN/Proxy detected
    if is_vpn_or_proxy(event.ip_address):
        score += 15

    # TOR exit node
    if is_tor_exit_node(event.ip_address):
        score += 40

    # Recent password reset
    if had_recent_password_reset(event.user_id, hours=24):
        score += 15

    return min(score, 100)

Learning milestones:

  1. Comprehensive logging works → You understand what to capture
  2. Impossible travel detection works → You understand geographic analysis
  3. Credential stuffing detection works → You understand attack patterns
  4. Risk scoring works → You understand context-based security

Final Capstone Project: Complete Identity Platform

  • File: LEARN_AUTHENTICATION_DEEP_DIVE.md
  • Main Programming Language: Python (FastAPI) + TypeScript (React)
  • Alternative Programming Languages: Go, Rust, Node.js
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 4. The “Open Core” Infrastructure (Enterprise Scale)
  • Difficulty: Level 5: Master
  • Knowledge Area: Full Identity Stack / Enterprise Auth
  • Software or Tool: Identity Platform (like Auth0/Okta)
  • Main Book: “Identity and Access Management” by Ertem Osmanoglu

What you’ll build: A complete identity platform combining all previous projects—multi-tenant, supporting passwords, social login, MFA, SSO, API keys, and comprehensive audit logging. Essentially, a self-hosted Auth0.

Why it teaches authentication: This is the culmination of everything. Building a real identity platform forces you to think about all the edge cases, security considerations, and user experience challenges that come with authentication at scale.

Core challenges you’ll face:

  • Multi-tenancy → maps to organization isolation, data separation
  • Multiple auth methods → maps to unified identity, linking accounts
  • Universal login → maps to embedded vs redirect, customization
  • Admin management → maps to user lifecycle, delegated admin

Key Concepts:

  • Identity platform architecture: “Identity and Access Management” - Osmanoglu
  • Multi-tenancy patterns: “Designing Data-Intensive Applications” Chapter 4 - Kleppmann
  • Account linking: Auth0 documentation on identity linking
  • Compliance: SOC 2, GDPR, HIPAA auth requirements

Difficulty: Master Time estimate: 2-3 months Prerequisites: All previous projects completed

Real world outcome:

┌────────────────────────────────────────────────────────────────────────┐
│                    🔐 MyAuth - Identity Platform                       │
├────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  Dashboard                                                              │
│  ─────────────────────────────────────────────────────────────────────│
│                                                                         │
│  Applications: 12       Users: 45,678      MFA Adoption: 78%            │
│  Daily Logins: 12,456   API Calls: 1.2M    Avg Response: 45ms           │
│                                                                         │
│  APPLICATIONS                                                           │
│  ─────────────────────────────────────────────────────────────────────│
│  │ Name              │ Type      │ Users  │ Auth Methods              │ │
│  ├───────────────────┼───────────┼────────┼───────────────────────────┤ │
│  │ Customer Portal   │ SPA       │ 34,567 │ Password, Google, MFA     │ │
│  │ Admin Dashboard   │ Web App   │ 234    │ Password, MFA (required)  │ │
│  │ Mobile App        │ Native    │ 23,456 │ Password, Biometric       │ │
│  │ Partner API       │ M2M       │ 45     │ API Keys, OAuth           │ │
│  │ Internal Tools    │ SAML SP   │ 567    │ SSO (Okta)                │ │
│                                                                         │
│  AUTHENTICATION METHODS                                                 │
│  ─────────────────────────────────────────────────────────────────────│
│  ✓ Email + Password    ✓ Google     ✓ GitHub     ✓ Microsoft            │
│  ✓ Magic Link          ✓ TOTP       ✓ WebAuthn   ✓ SMS                  │
│  ✓ SAML SSO            ✓ API Keys   ✓ OAuth 2.0  ✓ OIDC                 │
│                                                                         │
│  SECURITY                                                               │
│  ─────────────────────────────────────────────────────────────────────│
│  Brute Force Protection:    ✓ Enabled (5 attempts / 15 min)             │
│  Impossible Travel:         ✓ Enabled (step-up auth)                    │
│  Breached Password Check:   ✓ Enabled (HaveIBeenPwned API)              │
│  Suspicious IP Blocking:    ✓ Enabled (auto-block after 10 failures)    │
│                                                                         │
└────────────────────────────────────────────────────────────────────────┘

User Login Experience:
┌─────────────────────────────────────────────────┐
│            Welcome to Customer Portal            │
│                                                  │
│  [📧 Continue with Email]                        │
│                                                  │
│  ─────────────── or ───────────────              │
│                                                  │
│  [G Continue with Google]                        │
│  [🐙 Continue with GitHub]                       │
│  [🔐 Continue with Passkey]                      │
│                                                  │
│  ─────────────────────────────────────────────  │
│  [🏢 Sign in with SSO]                           │
│                                                  │
└─────────────────────────────────────────────────┘

After MFA enrollment prompt:
┌─────────────────────────────────────────────────┐
│        Secure your account with MFA              │
│                                                  │
│  Choose a second factor:                         │
│                                                  │
│  [📱 Authenticator App]  Recommended             │
│  [🔐 Security Key]       Most secure             │
│  [📱 SMS Code]           Less secure             │
│                                                  │
│  [Skip for now]                                  │
└─────────────────────────────────────────────────┘

API response:
POST /oauth/token
{
  "access_token": "eyJhbG...",
  "id_token": "eyJhbG...",
  "refresh_token": "v1.abc...",
  "token_type": "Bearer",
  "expires_in": 86400,
  "scope": "openid profile email"
}

Decoded ID token:
{
  "sub": "user_abc123",
  "email": "john@example.com",
  "email_verified": true,
  "name": "John Doe",
  "picture": "https://...",
  "auth_methods": ["password", "mfa:totp"],
  "mfa_verified": true,
  "org_id": "org_xyz789",
  "roles": ["admin", "developer"],
  "iss": "https://auth.myapp.com",
  "aud": "customer-portal",
  "iat": 1705315200,
  "exp": 1705401600
}

Implementation Hints:

Multi-tenant architecture:

# Organization/tenant model
class Organization:
    id: str
    name: str
    slug: str  # mycompany.myauth.com
    settings: OrgSettings
    branding: BrandingConfig
    connections: list[AuthConnection]  # Enabled auth methods
    mfa_policy: MFAPolicy
    session_policy: SessionPolicy

class User:
    id: str
    org_id: str  # Tenant isolation
    email: str
    identities: list[Identity]  # Multiple linked identities

class Identity:
    provider: str  # "password", "google", "saml:okta"
    provider_user_id: str
    profile_data: dict
    linked_at: datetime

Universal login architecture:

# Customizable login page per tenant
class LoginConfiguration:
    org_id: str
    logo_url: str
    primary_color: str
    enabled_connections: list[str]  # ["password", "google", "saml"]
    mfa_required: bool
    custom_css: str
    custom_html: str

# Render login page
def render_login(org_slug, auth_request):
    org = get_org_by_slug(org_slug)
    config = get_login_config(org.id)
    connections = get_enabled_connections(org.id)

    return render_template("universal_login.html",
        config=config,
        connections=connections,
        auth_request=auth_request
    )

Account linking:

def link_identity(user_id, new_identity):
    """Link a new identity (e.g., Google) to existing user."""
    user = get_user(user_id)

    # Check if identity already linked to another user
    existing = find_user_by_identity(
        new_identity.provider,
        new_identity.provider_user_id
    )
    if existing and existing.id != user_id:
        raise ConflictError("Identity already linked to another account")

    # Add identity
    user.identities.append(new_identity)
    save_user(user)

    # Merge profile data if needed
    if not user.profile_picture and new_identity.profile_data.get('picture'):
        user.profile_picture = new_identity.profile_data['picture']

Components to build:

  1. Core Auth Service: User management, password auth, sessions
  2. OAuth/OIDC Server: Authorization code flow, token management
  3. Social Connections: Google, GitHub, Microsoft integration
  4. MFA Service: TOTP, WebAuthn, SMS
  5. SAML IdP/SP: Enterprise SSO
  6. API Key Service: Machine-to-machine auth
  7. Audit Service: Logging, anomaly detection
  8. Admin API: User/org management
  9. Universal Login UI: Customizable login pages
  10. Management Dashboard: Admin interface

Learning milestones:

  1. Single-tenant auth works → Foundation complete
  2. Multi-tenant isolation works → Organizational boundaries
  3. Multiple auth methods work → Unified identity
  4. Account linking works → Identity federation
  5. Full admin interface works → Production-ready platform

Project Comparison Table

Project Difficulty Time Depth of Understanding Fun Factor
Password Hasher & Cracker Beginner Weekend ⭐⭐⭐ ⭐⭐⭐⭐
Session-Based Login Intermediate 1 week ⭐⭐⭐ ⭐⭐
JWT from Scratch Intermediate 1-2 weeks ⭐⭐⭐⭐ ⭐⭐⭐
OAuth 2.0 Client & Server Advanced 2-3 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐
TOTP Authenticator Intermediate 1 week ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
WebAuthn/Passkey Advanced 2-3 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
Password Reset System Intermediate 1 week ⭐⭐⭐ ⭐⭐
SAML SSO Expert 3-4 weeks ⭐⭐⭐⭐ ⭐⭐
API Key System Intermediate 1-2 weeks ⭐⭐⭐ ⭐⭐⭐
Auth Audit & Anomaly Advanced 2-3 weeks ⭐⭐⭐⭐ ⭐⭐⭐⭐
Complete Identity Platform Master 2-3 months ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐

If you’re new to security:

  1. Password Hasher & Cracker — See why security matters
  2. Session-Based Login — Understand traditional auth
  3. JWT from Scratch — Understand modern tokens
  4. TOTP Authenticator — See crypto magic in action

If you’re building a web app:

  1. Session-Based Login → Traditional approach
  2. JWT from Scratch → API authentication
  3. OAuth 2.0 → Social login
  4. Password Reset → Critical security flow
  5. API Key System → For your API

If you’re targeting enterprise:

  1. OAuth 2.0 → Foundation
  2. SAML SSO → Enterprise requirement
  3. WebAuthn → Modern passwordless
  4. Auth Audit → Compliance requirements

The complete path (3-6 months):

1-2-3-4-5-6-7-8-9-10 → Capstone


Essential Resources

Primary Books

  1. “Serious Cryptography” by Jean-Philippe Aumasson — Understand the crypto foundations
  2. “OAuth 2 in Action” by Justin Richer & Antonio Sanso — The OAuth bible
  3. “The Web Application Hacker’s Handbook” by Stuttard & Pinto — Attack perspective
  4. “API Security in Action” by Neil Madden — Modern API auth patterns

Standards & Specifications

  • RFC 6238 — TOTP
  • RFC 7519 — JWT
  • RFC 6749 — OAuth 2.0
  • W3C WebAuthn — Passkeys
  • OASIS SAML 2.0 — Enterprise SSO

Online Resources

  • OWASP Cheat Sheets — Security best practices
  • Auth0 Docs — Excellent explanations
  • jwt.io — JWT debugger
  • webauthn.io — WebAuthn testing

Tools

  • Burp Suite — Test your auth implementations
  • jwt.io — Decode and debug JWTs
  • SAML Tracer — Browser extension for SAML debugging
  • webauthn.io — Test WebAuthn implementations

Summary

Project Main Language
Password Hasher & Cracker Python
Session-Based Login System Python (Flask)
JWT Authentication from Scratch Python
OAuth 2.0 Client & Server Python (Flask)
TOTP Authenticator App Python
WebAuthn/Passkey Implementation Python + JavaScript
Secure Password Reset System Python (Flask)
Single Sign-On (SSO) with SAML Python
API Key Authentication System Python (FastAPI)
Auth Audit Logger & Anomaly Detector Python
Complete Identity Platform (Capstone) Python (FastAPI) + TypeScript

After completing these projects, you’ll understand authentication at a level that very few developers achieve. You won’t just implement auth—you’ll understand why each piece exists, what attacks it prevents, and how to build systems that are secure by design.