The Complete Guide to JSON Web Tokens (JWT)

    A comprehensive guide to understanding and implementing JWT authentication and authorization using FastAPI and JavaScript.

    📚 Table of Contents

    Navigation Guide:
    🟢 Beginner | 🟡 Intermediate | 🟠 Advanced | 🔴 Expert

    Part I: Fundamentals 🟢

    Master the core concepts of JWT authentication

    1. Introduction to JWT – What is JWT and why use it?
    2. JWT Structure and Anatomy – Understanding the three parts
    3. Claims and Payload – Working with JWT data
    4. Cryptographic Algorithms – Security foundations
    5. Setting Up Development Environment – Get started

    Part II: Basic Implementation 🟡

    Build your first JWT authentication system

    1. Basic JWT Authentication with FastAPI – Backend implementation
    2. Frontend Integration with JavaScript – Client-side auth
    3. Token Storage and Management – Secure token handling
    4. Authorization and Role-Based Access Control – Permissions
    5. Error Handling and Validation – Robust error management

    Part III: Advanced Security 🟠

    Implement enterprise-grade security features

    1. JWT Encryption (JWE) – Encrypt sensitive data
    2. OAuth 2.0 Integration – Third-party authentication
    3. Key Management and Rotation – Advanced key strategies
    4. Token Revocation and Blacklisting – Invalidate tokens
    5. Security Best Practices – Comprehensive security guide

    Part IV: Production Systems 🔴

    Deploy production-ready applications

    1. Complete Full-Stack Application – End-to-end example
    2. Troubleshooting and FAQ – Common issues and solutions
    3. Production Deployment and Monitoring – Go live
    4. Testing and Quality Assurance – Comprehensive testing

    Part V: Enterprise and Scale 🔴

    Master microservices, performance, and future trends

    1. JWT in Microservices Architecture – Distributed systems
    2. Performance Optimization – Scale efficiently
    3. Migration Strategies – Legacy system transitions
    4. Future Standards and Trends – WebAuthn, DPoP, SD-JWT
    5. System Design and Architectural Patterns – Enterprise architecture
    6. Summary and Best Practices – Key takeaways

    1. Introduction to JWT

    What is JWT?

    JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed.

    Why Use JWT?

    JWTs are particularly useful for:

    • Stateless Authentication: No need to store session data on the server
    • Scalability: Perfect for distributed systems and microservices
    • Cross-Domain Authentication: Works across different domains and services
    • Mobile Applications: Lightweight and efficient for mobile apps
    • Single Sign-On (SSO): Enables seamless authentication across multiple applications

    JWT vs Traditional Sessions

    graph LR
        subgraph "Traditional Session-Based Auth"
            A[Client] -->|Login Request| B[Server]
            B -->|Session ID| A
            A -->|Session ID Cookie| C[Subsequent Requests]
            C -->|Lookup Session| D[Session Store]
            D -->|Session Data| B
        end
    
        subgraph "JWT-Based Auth"
            E[Client] -->|Login Request| F[Server]
            F -->|JWT Token| E
            E -->|JWT in Header| G[Subsequent Requests]
            G -->|Verify Signature| F
        end

    When to Use JWT

    Use JWT when:

    • Building stateless APIs
    • Implementing microservices architecture
    • Need cross-domain authentication
    • Building single-page applications (SPAs)
    • Implementing mobile app authentication

    Consider alternatives when:

    • Building simple server-side rendered applications
    • Need immediate token revocation
    • Working with highly sensitive data requiring frequent validation

    2. JWT Structure and Anatomy

    JWT Structure Overview

    A JWT consists of three parts separated by dots (.):

    header.payload.signature
    JSON
    graph TB
        A[JWT Token] --> B[Header]
        A --> C[Payload]
        A --> D[Signature]
    
        B --> E["Algorithm (alg)Token Type (typ)"]
        C --> F["ClaimsUser InfoExpiration"]
        D --> G["SignatureVerification"]

    The header typically consists of two parts:

    • alg: The signing algorithm being used (e.g., HS256, RS256)
    • typ: The type of token (JWT)

    Example Header:

    {
      "alg": "HS256",
      "typ": "JWT"
    }
    JSON

    Base64URL Encoded:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
    ANSI

    Payload

    The payload contains the claims – statements about an entity (typically, the user) and additional data.

    Standard Claims:

    • iss (issuer): Who issued the token
    • sub (subject): Who the token is about
    • aud (audience): Who the token is intended for
    • exp (expiration time): When the token expires
    • nbf (not before): When the token becomes valid
    • iat (issued at): When the token was issued
    • jti (JWT ID): Unique identifier for the token

    Example Payload:

    {
      "sub": "user123",
      "name": "John Doe",
      "role": "admin",
      "iat": 1697644800,
      "exp": 1697648400
    }
    JSON

    Base64URL Encoded:

    eyJzdWIiOiJ1c2VyMTIzIiwibmFtZSI6IkpvaG4gRG9lIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNjk3NjQ0ODAwLCJleHAiOjE2OTc2NDg0MDB9
    ANSI

    Signature

    The signature is created by taking the encoded header, encoded payload, a secret, and signing it using the algorithm specified in the header.

    For HMAC SHA256:

    HMACSHA256(
      base64UrlEncode(header) + "." +
      base64UrlEncode(payload),
      secret
    )
    ANSI

    Complete JWT Example

    Raw JWT:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIiwibmFtZSI6IkpvaG4gRG9lIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNjk3NjQ0ODAwLCJleHAiOjE2OTc2NDg0MDB9.5mUL-WOmOy-P5n6K8T0H3pPz4yM9QJF2k8N7V6x2A1M
    ANSI

    JWT Validation Flow

    sequenceDiagram
        participant C as Client
        participant S as Server
        participant V as JWT Validator
    
        C->>S: Send JWT Token
        S->>V: Extract Header & Payload
        V->>V: Decode Base64URL
        V->>V: Verify Signature
        V->>V: Check Expiration
        V->>V: Validate Claims
        V->>S: Validation Result
        S->>C: Access Granted/Denied

    3. Claims and Payload

    Understanding Claims

    Claims are statements about an entity (typically, the user) and additional data. There are three types of claims:

    1. Registered Claims: Predefined claims that are not mandatory but recommended
    2. Public Claims: Defined by those using JWTs (should be registered to avoid collisions)
    3. Private Claims: Custom claims created to share information between parties

    Registered Claims in Detail

    Essential Time-Based Claims

    {
      "iat": 1697644800,  // Issued At: October 18, 2023 10:00:00 UTC
      "exp": 1697648400,  // Expires: October 18, 2023 11:00:00 UTC
      "nbf": 1697644800   // Not Before: October 18, 2023 10:00:00 UTC
    }
    JSON

    Identity and Audience Claims

    {
      "iss": "https://auth.example.com",  // Issuer
      "sub": "user_123456",               // Subject (User ID)
      "aud": ["api.example.com", "mobile-app"],  // Audience
      "jti": "abc123-def456-ghi789"       // JWT ID (Unique identifier)
    }
    JSON

    Custom Claims Best Practices

    User Information Claims

    {
      "user_id": "123456",
      "username": "johndoe",
      "email": "john@example.com",
      "first_name": "John",
      "last_name": "Doe",
      "avatar_url": "https://example.com/avatars/john.jpg"
    }
    JSON

    Authorization Claims

    {
      "role": "admin",
      "permissions": ["read", "write", "delete"],
      "scopes": ["user:read", "user:write", "admin:all"],
      "groups": ["administrators", "developers"],
      "tenant_id": "company_abc"
    }
    JSON

    Application-Specific Claims

    {
      "subscription_type": "premium",
      "last_login": 1697644800,
      "login_count": 45,
      "preferences": {
        "theme": "dark",
        "language": "en-US",
        "timezone": "America/New_York"
      }
    }
    JSON

    Claim Validation Strategies

    graph TD
        A[Incoming JWT] --> B[Extract Claims]
        B --> C{Check exp}
        C -->|Expired| D[Reject Token]
        C -->|Valid| E{Check nbf}
        E -->|Too Early| D
        E -->|Valid| F{Check aud}
        F -->|Wrong Audience| D
        F -->|Valid| G{Check iss}
        G -->|Wrong Issuer| D
        G -->|Valid| H[Token Valid]

    Payload Size Considerations

    Keep payload minimal:

    // Good: Essential information only
    {
      "sub": "user123",
      "role": "admin",
      "exp": 1697648400
    }
    JSON
    // Avoid: Large payloads
    {
      "sub": "user123",
      "role": "admin",
      "exp": 1697648400,
      "profile": {
        "bio": "Very long biography text...",
        "preferences": { /* Large object */ },
        "history": [ /* Array of many items */ ]
      }
    }
    JSON

    4. Cryptographic Algorithms

    Symmetric vs Asymmetric Algorithms

    graph LR
        subgraph "Symmetric (HMAC)"
            A[Secret Key] --> B[Sign Token]
            A --> C[Verify Token]
            B --> D[JWT]
            D --> C
        end
    
        subgraph "Asymmetric (RSA/ECDSA)"
            E[Private Key] --> F[Sign Token]
            G[Public Key] --> H[Verify Token]
            F --> I[JWT]
            I --> H
        end

    HMAC Algorithms (Symmetric)

    HS256 (HMAC using SHA-256)

    Characteristics:

    • Single secret key for signing and verification
    • Fastest algorithm
    • Best for single-service applications
    • Secret must be kept secure on all services

    Use Case Example:

    # FastAPI with HS256
    SECRET_KEY = "your-secret-key-here"
    ALGORITHM = "HS256"
    
    def create_access_token(data: dict):
        to_encode = data.copy()
        expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        to_encode.update({"exp": expire})
        encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
        return encoded_jwt
    Python

    HS384 and HS512

    HS384: Uses SHA-384 hash function HS512: Uses SHA-512 hash function

    # Different HMAC algorithms
    algorithms_comparison = {
        "HS256": {"hash_size": 256, "speed": "fastest", "security": "good"},
        "HS384": {"hash_size": 384, "speed": "medium", "security": "better"},
        "HS512": {"hash_size": 512, "speed": "slower", "security": "best"}
    }
    Python

    RSA Algorithms (Asymmetric)

    RS256 (RSA using SHA-256)

    Characteristics:

    • Uses RSA key pairs (private/public)
    • Private key for signing, public key for verification
    • Perfect for microservices and distributed systems
    • Public key can be shared safely

    Key Generation:

    # Generate RSA private key
    openssl genrsa -out private_key.pem 2048
    
    # Extract public key
    openssl rsa -in private_key.pem -pubout -out public_key.pem
    Bash

    Implementation:

    # FastAPI with RS256
    from cryptography.hazmat.primitives import serialization
    
    def load_private_key():
        with open("private_key.pem", "rb") as key_file:
            private_key = serialization.load_pem_private_key(
                key_file.read(),
                password=None,
            )
        return private_key
    
    def load_public_key():
        with open("public_key.pem", "rb") as key_file:
            public_key = serialization.load_pem_public_key(key_file.read())
        return public_key
    
    def create_access_token(data: dict):
        to_encode = data.copy()
        expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        to_encode.update({"exp": expire})
    
        private_key = load_private_key()
        encoded_jwt = jwt.encode(to_encode, private_key, algorithm="RS256")
        return encoded_jwt
    Python

    ECDSA Algorithms (Asymmetric)

    ES256 (ECDSA using P-256 and SHA-256)

    Characteristics:

    • Elliptic Curve Digital Signature Algorithm
    • Smaller key sizes than RSA
    • Better performance than RSA
    • Same security level with smaller keys

    Key Generation:

    # Generate ECDSA private key
    openssl ecparam -genkey -name prime256v1 -noout -out ec_private_key.pem
    
    # Extract public key
    openssl ec -in ec_private_key.pem -pubout -out ec_public_key.pem
    Bash

    Algorithm Security Comparison

    AlgorithmTypeKey SizeSecurity LevelPerformanceUse Case
    HS256HMAC256-bitGoodFastestSingle service
    HS384HMAC384-bitBetterFastSingle service
    HS512HMAC512-bitBestFastSingle service
    RS256RSA2048-bitGoodMediumMicroservices
    RS384RSA2048-bitBetterMediumMicroservices
    RS512RSA2048-bitBestSlowerHigh security
    ES256ECDSA256-bitGoodFastModern apps
    ES384ECDSA384-bitBetterFastModern apps
    ES512ECDSA521-bitBestFastHigh security

    Algorithm Selection Guide

    flowchart TD
        A[Choose Algorithm] --> B{Single Service?}
        B -->|Yes| C[Use HMAC]
        B -->|No| D[Use Asymmetric]
    
        C --> E{Security Level?}
        E -->|Standard| F[HS256]
        E -->|High| G[HS512]
    
        D --> H{Key Size Preference?}
        H -->|Smaller Keys| I[ECDSA]
        H -->|Standard| J[RSA]
    
        I --> K{Security Level?}
        K -->|Standard| L[ES256]
        K -->|High| M[ES512]
    
        J --> N{Security Level?}
        N -->|Standard| O[RS256]
        N -->|High| P[RS512]

    Dangerous Algorithms to Avoid

    The “none” Algorithm

    Never use the “none” algorithm:

    {
      "alg": "none",
      "typ": "JWT"
    }
    JSON

    This bypasses signature verification entirely and should never be accepted in production.

    Weak Algorithms

    Avoid deprecated or weak algorithms:

    • MD5-based algorithms
    • SHA-1-based algorithms
    • Weak RSA key sizes (<2048 bits)

    5. Setting Up Development Environment

    Prerequisites

    Before we start building JWT applications, let’s set up our development environment with all necessary tools and dependencies.

    System Requirements

    • Python 3.8+ for FastAPI backend
    • Node.js 16+ for frontend tooling (optional)
    • Git for version control
    • VS Code or your preferred IDE
    • Postman or similar API testing tool

    Project Structure

    Let’s create a well-organized project structure:

    jwt-guide-project/
    ├── backend/
       ├── app/
          ├── __init__.py
          ├── main.py
          ├── auth/
             ├── __init__.py
             ├── jwt_handler.py
             └── models.py
          ├── routers/
             ├── __init__.py
             ├── auth.py
             └── users.py
          └── database/
              ├── __init__.py
              └── database.py
       ├── requirements.txt
       └── .env
    ├── frontend/
       ├── index.html
       ├── css/
          └── styles.css
       ├── js/
          ├── auth.js
          ├── api.js
          └── main.js
       └── assets/
    └── README.md
    Bash

    Backend Setup (FastAPI)

    Installing Dependencies

    Create backend/requirements.txt:

    fastapi==0.104.1
    uvicorn[standard]==0.24.0
    python-jose[cryptography]==3.3.0
    passlib[bcrypt]==1.7.4
    python-multipart==0.0.6
    python-decouple==3.8
    sqlalchemy==2.0.23
    databases[sqlite]==0.8.0
    aiosqlite==0.19.0
    TOML

    Environment Configuration

    Create backend/.env:

    # JWT Configuration
    SECRET_KEY=your-super-secret-key-here-change-in-production
    ALGORITHM=HS256
    ACCESS_TOKEN_EXPIRE_MINUTES=30
    REFRESH_TOKEN_EXPIRE_DAYS=7
    
    # Database Configuration
    DATABASE_URL=sqlite:///./app.db
    
    # CORS Configuration
    ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8080,http://127.0.0.1:5500
    TOML

    Basic FastAPI Application

    Create backend/app/main.py:

    from fastapi import FastAPI, Depends, HTTPException, status
    from fastapi.middleware.cors import CORSMiddleware
    from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
    from decouple import config
    import uvicorn
    
    # Import our modules (we'll create these)
    from app.auth.jwt_handler import JWTHandler
    from app.routers import auth, users
    
    # Initialize FastAPI app
    app = FastAPI(
        title="JWT Authentication API",
        description="A comprehensive JWT authentication system",
        version="1.0.0"
    )
    
    # CORS Configuration
    origins = config("ALLOWED_ORIGINS", default="http://localhost:3000").split(",")
    
    app.add_middleware(
        CORSMiddleware,
        allow_origins=origins,
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )
    
    # Security scheme
    security = HTTPBearer()
    
    # JWT Handler instance
    jwt_handler = JWTHandler()
    
    # Include routers
    app.include_router(auth.router, prefix="/auth", tags=["Authentication"])
    app.include_router(users.router, prefix="/users", tags=["Users"])
    
    @app.get("/")
    async def root():
        return {"message": "JWT Authentication API is running!"}
    
    @app.get("/protected")
    async def protected_route(credentials: HTTPAuthorizationCredentials = Depends(security)):
        """Example protected route"""
        token = credentials.credentials
        payload = jwt_handler.verify_token(token)
        if not payload:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid token"
            )
        return {"message": f"Hello {payload.get('sub')}!", "data": payload}
    
    if __name__ == "__main__":
        uvicorn.run(app, host="0.0.0.0", port=8000)
    Python

    Frontend Setup

    Basic HTML Structure

    Create frontend/index.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>JWT Authentication Demo</title>
        <link rel="stylesheet" href="css/styles.css">
    </head>
    <body>
        <div class="container">
            <header>
                <h1>JWT Authentication Demo</h1>
                <nav id="navigation">
                    <!-- Navigation will be populated by JavaScript -->
                </nav>
            </header>
    
            <main id="main-content">
                <!-- Login Form -->
                <div id="login-section" class="auth-section">
                    <h2>Login</h2>
                    <form id="login-form">
                        <div class="form-group">
                            <label for="login-email">Email:</label>
                            <input type="email" id="login-email" required>
                        </div>
                        <div class="form-group">
                            <label for="login-password">Password:</label>
                            <input type="password" id="login-password" required>
                        </div>
                        <button type="submit">Login</button>
                    </form>
                </div>
    
                <!-- Registration Form -->
                <div id="register-section" class="auth-section" style="display: none;">
                    <h2>Register</h2>
                    <form id="register-form">
                        <div class="form-group">
                            <label for="register-name">Full Name:</label>
                            <input type="text" id="register-name" required>
                        </div>
                        <div class="form-group">
                            <label for="register-email">Email:</label>
                            <input type="email" id="register-email" required>
                        </div>
                        <div class="form-group">
                            <label for="register-password">Password:</label>
                            <input type="password" id="register-password" required>
                        </div>
                        <button type="submit">Register</button>
                    </form>
                </div>
    
                <!-- Dashboard -->
                <div id="dashboard-section" class="dashboard" style="display: none;">
                    <h2>Dashboard</h2>
                    <div id="user-info"></div>
                    <div id="protected-content"></div>
                </div>
            </main>
    
            <footer>
                <div id="status"></div>
            </footer>
        </div>
    
        <script src="js/api.js"></script>
        <script src="js/auth.js"></script>
        <script src="js/main.js"></script>
    </body>
    </html>
    HTML

    CSS Styling

    Create frontend/css/styles.css:

    /* Global Styles */
    * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }
    
    body {
        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        line-height: 1.6;
        color: #333;
        background-color: #f4f4f4;
    }
    
    .container {
        max-width: 1200px;
        margin: 0 auto;
        padding: 20px;
    }
    
    /* Header Styles */
    header {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        color: white;
        padding: 2rem 0;
        margin-bottom: 2rem;
        border-radius: 10px;
        box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    }
    
    header h1 {
        text-align: center;
        margin-bottom: 1rem;
        font-size: 2.5rem;
    }
    
    nav {
        text-align: center;
    }
    
    nav button {
        background: rgba(255, 255, 255, 0.2);
        color: white;
        border: none;
        padding: 10px 20px;
        margin: 0 10px;
        border-radius: 5px;
        cursor: pointer;
        transition: background-color 0.3s;
    }
    
    nav button:hover {
        background: rgba(255, 255, 255, 0.3);
    }
    
    nav button.active {
        background: rgba(255, 255, 255, 0.4);
    }
    
    /* Form Styles */
    .auth-section {
        background: white;
        padding: 2rem;
        border-radius: 10px;
        box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        max-width: 500px;
        margin: 0 auto;
    }
    
    .auth-section h2 {
        text-align: center;
        margin-bottom: 1.5rem;
        color: #667eea;
    }
    
    .form-group {
        margin-bottom: 1rem;
    }
    
    .form-group label {
        display: block;
        margin-bottom: 0.5rem;
        font-weight: bold;
        color: #555;
    }
    
    .form-group input {
        width: 100%;
        padding: 0.75rem;
        border: 1px solid #ddd;
        border-radius: 5px;
        font-size: 1rem;
        transition: border-color 0.3s;
    }
    
    .form-group input:focus {
        outline: none;
        border-color: #667eea;
        box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
    }
    
    button {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        color: white;
        border: none;
        padding: 0.75rem 2rem;
        border-radius: 5px;
        cursor: pointer;
        font-size: 1rem;
        transition: transform 0.2s, box-shadow 0.2s;
        width: 100%;
    }
    
    button:hover {
        transform: translateY(-2px);
        box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
    }
    
    button:active {
        transform: translateY(0);
    }
    
    /* Dashboard Styles */
    .dashboard {
        background: white;
        padding: 2rem;
        border-radius: 10px;
        box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    }
    
    .dashboard h2 {
        color: #667eea;
        margin-bottom: 1.5rem;
        text-align: center;
    }
    
    #user-info {
        background: #f8f9fa;
        padding: 1rem;
        border-radius: 5px;
        margin-bottom: 1rem;
    }
    
    #protected-content {
        background: #e7f3ff;
        padding: 1rem;
        border-radius: 5px;
        border-left: 4px solid #667eea;
    }
    
    /* Status Messages */
    #status {
        margin-top: 1rem;
        padding: 1rem;
        border-radius: 5px;
        text-align: center;
        font-weight: bold;
    }
    
    .status-success {
        background: #d4edda;
        color: #155724;
        border: 1px solid #c3e6cb;
    }
    
    .status-error {
        background: #f8d7da;
        color: #721c24;
        border: 1px solid #f5c6cb;
    }
    
    .status-info {
        background: #d1ecf1;
        color: #0c5460;
        border: 1px solid #bee5eb;
    }
    
    /* Responsive Design */
    @media (max-width: 768px) {
        .container {
            padding: 10px;
        }
    
        header h1 {
            font-size: 2rem;
        }
    
        nav button {
            display: block;
            width: 100%;
            margin: 5px 0;
        }
    
        .auth-section {
            padding: 1rem;
        }
    }
    
    /* Loading Animation */
    .loading {
        display: inline-block;
        width: 20px;
        height: 20px;
        border: 3px solid #f3f3f3;
        border-top: 3px solid #667eea;
        border-radius: 50%;
        animation: spin 1s linear infinite;
    }
    
    @keyframes spin {
        0% { transform: rotate(0deg); }
        100% { transform: rotate(360deg); }
    }
    
    /* Token Display */
    .token-display {
        background: #f8f9fa;
        border: 1px solid #dee2e6;
        border-radius: 5px;
        padding: 1rem;
        margin: 1rem 0;
        word-wrap: break-word;
        font-family: 'Courier New', monospace;
        font-size: 0.9rem;
    }
    
    .token-header {
        color: #28a745;
        font-weight: bold;
    }
    
    .token-payload {
        color: #007bff;
        font-weight: bold;
    }
    
    .token-signature {
        color: #dc3545;
        font-weight: bold;
    }
    CSS

    Development Tools Setup

    VS Code Extensions

    Recommended extensions for JWT development:

    1. Python – Python language support
    2. Pylance – Python language server
    3. REST Client – API testing within VS Code
    4. Thunder Client – Another excellent API testing tool
    5. Live Server – Live reload for frontend development
    6. JWT Debugger – JWT token inspection

    Postman Collection

    Create a Postman collection for testing our API endpoints:

    {
        "info": {
            "name": "JWT Authentication API",
            "description": "Collection for testing JWT authentication endpoints",
            "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
        },
        "item": [
            {
                "name": "Authentication",
                "item": [
                    {
                        "name": "Register User",
                        "request": {
                            "method": "POST",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/json"
                                }
                            ],
                            "body": {
                                "mode": "raw",
                                "raw": "{\n    \"email\": \"user@example.com\",\n    \"password\": \"password123\",\n    \"full_name\": \"John Doe\"\n}"
                            },
                            "url": {
                                "raw": "{{base_url}}/auth/register",
                                "host": ["{{base_url}}"],
                                "path": ["auth", "register"]
                            }
                        }
                    },
                    {
                        "name": "Login User",
                        "request": {
                            "method": "POST",
                            "header": [
                                {
                                    "key": "Content-Type",
                                    "value": "application/x-www-form-urlencoded"
                                }
                            ],
                            "body": {
                                "mode": "urlencoded",
                                "urlencoded": [
                                    {
                                        "key": "username",
                                        "value": "user@example.com"
                                    },
                                    {
                                        "key": "password",
                                        "value": "password123"
                                    }
                                ]
                            },
                            "url": {
                                "raw": "{{base_url}}/auth/login",
                                "host": ["{{base_url}}"],
                                "path": ["auth", "login"]
                            }
                        }
                    }
                ]
            }
        ],
        "variable": [
            {
                "key": "base_url",
                "value": "http://localhost:8000"
            },
            {
                "key": "access_token",
                "value": ""
            }
        ]
    }
    JSON

    Testing Environment

    Unit Testing Setup

    Create backend/tests/test_jwt.py:

    import pytest
    from fastapi.testclient import TestClient
    from app.main import app
    from app.auth.jwt_handler import JWTHandler
    
    client = TestClient(app)
    jwt_handler = JWTHandler()
    
    def test_create_and_verify_token():
        """Test JWT token creation and verification"""
        payload = {"sub": "test_user", "role": "user"}
        token = jwt_handler.create_access_token(payload)
    
        assert token is not None
        assert isinstance(token, str)
    
        decoded_payload = jwt_handler.verify_token(token)
        assert decoded_payload is not None
        assert decoded_payload["sub"] == "test_user"
        assert decoded_payload["role"] == "user"
    
    def test_expired_token():
        """Test expired token handling"""
        from datetime import datetime, timedelta
        import jwt
        from decouple import config
    
        # Create an expired token
        payload = {
            "sub": "test_user",
            "exp": datetime.utcnow() - timedelta(minutes=1)
        }
    
        expired_token = jwt.encode(
            payload, 
            config("SECRET_KEY"), 
            algorithm=config("ALGORITHM")
        )
    
        decoded_payload = jwt_handler.verify_token(expired_token)
        assert decoded_payload is None
    
    def test_invalid_token():
        """Test invalid token handling"""
        invalid_token = "invalid.token.here"
        decoded_payload = jwt_handler.verify_token(invalid_token)
        assert decoded_payload is None
    Python

    Run tests with:

    pytest backend/tests/ -v
    Bash

    6. Basic JWT Authentication with FastAPI

    Now let’s implement a complete JWT authentication system using FastAPI. This chapter covers user registration, login, token creation, and basic protection mechanisms.

    JWT Handler Implementation

    Create backend/app/auth/jwt_handler.py:

    from datetime import datetime, timedelta
    from typing import Optional, Dict, Any
    import jwt
    from decouple import config
    from passlib.context import CryptContext
    
    class JWTHandler:
        def __init__(self):
            self.secret_key = config("SECRET_KEY")
            self.algorithm = config("ALGORITHM", default="HS256")
            self.access_token_expire_minutes = int(config("ACCESS_TOKEN_EXPIRE_MINUTES", default=30))
            self.refresh_token_expire_days = int(config("REFRESH_TOKEN_EXPIRE_DAYS", default=7))
    
            # Password hashing
            self.pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
    
        def create_access_token(self, data: Dict[str, Any]) -> str:
            """Create a new access token"""
            to_encode = data.copy()
            expire = datetime.utcnow() + timedelta(minutes=self.access_token_expire_minutes)
    
            to_encode.update({
                "exp": expire,
                "iat": datetime.utcnow(),
                "type": "access"
            })
    
            encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
            return encoded_jwt
    
        def create_refresh_token(self, data: Dict[str, Any]) -> str:
            """Create a new refresh token"""
            to_encode = data.copy()
            expire = datetime.utcnow() + timedelta(days=self.refresh_token_expire_days)
    
            to_encode.update({
                "exp": expire,
                "iat": datetime.utcnow(),
                "type": "refresh"
            })
    
            encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
            return encoded_jwt
    
        def verify_token(self, token: str) -> Optional[Dict[str, Any]]:
            """Verify and decode a JWT token"""
            try:
                payload = jwt.decode(
                    token, 
                    self.secret_key, 
                    algorithms=[self.algorithm]
                )
                return payload
            except jwt.ExpiredSignatureError:
                return None
            except jwt.JWTError:
                return None
    
        def hash_password(self, password: str) -> str:
            """Hash a password"""
            return self.pwd_context.hash(password)
    
        def verify_password(self, plain_password: str, hashed_password: str) -> bool:
            """Verify a password against its hash"""
            return self.pwd_context.verify(plain_password, hashed_password)
    
        def refresh_access_token(self, refresh_token: str) -> Optional[str]:
            """Create a new access token from a refresh token"""
            payload = self.verify_token(refresh_token)
    
            if not payload or payload.get("type") != "refresh":
                return None
    
            # Create new access token with same user data
            new_token_data = {
                "sub": payload.get("sub"),
                "email": payload.get("email"),
                "role": payload.get("role")
            }
    
            return self.create_access_token(new_token_data)
    Python

    User Models

    Create backend/app/auth/models.py:

    from pydantic import BaseModel, EmailStr
    from typing import Optional
    from datetime import datetime
    
    class UserCreate(BaseModel):
        email: EmailStr
        password: str
        full_name: str
    
    class UserResponse(BaseModel):
        id: int
        email: str
        full_name: str
        is_active: bool
        created_at: datetime
    
        class Config:
            from_attributes = True
    
    class UserLogin(BaseModel):
        username: str  # We'll use email as username
        password: str
    
    class Token(BaseModel):
        access_token: str
        refresh_token: str
        token_type: str = "bearer"
    
    class TokenPayload(BaseModel):
        sub: Optional[str] = None
        exp: Optional[int] = None
        iat: Optional[int] = None
        type: Optional[str] = None
    
    class RefreshTokenRequest(BaseModel):
        refresh_token: str
    Python

    Database Setup

    Create backend/app/database/database.py:

    from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, Boolean, DateTime
    from sqlalchemy.sql import func
    from databases import Database
    from decouple import config
    
    DATABASE_URL = config("DATABASE_URL", default="sqlite:///./app.db")
    
    # Database instance
    database = Database(DATABASE_URL)
    
    # SQLAlchemy metadata
    metadata = MetaData()
    
    # Users table
    users_table = Table(
        "users",
        metadata,
        Column("id", Integer, primary_key=True, index=True),
        Column("email", String, unique=True, index=True, nullable=False),
        Column("full_name", String, nullable=False),
        Column("hashed_password", String, nullable=False),
        Column("is_active", Boolean, default=True),
        Column("created_at", DateTime(timezone=True), server_default=func.now()),
        Column("updated_at", DateTime(timezone=True), onupdate=func.now()),
    )
    
    # Create engine
    engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
    
    # Create tables
    metadata.create_all(bind=engine)
    
    async def connect_db():
        """Connect to database"""
        await database.connect()
    
    async def disconnect_db():
        """Disconnect from database"""
        await database.disconnect()
    Python

    Authentication Router

    Create backend/app/routers/auth.py:

    from fastapi import APIRouter, Depends, HTTPException, status
    from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
    from sqlalchemy import select
    from typing import Dict, Any
    
    from app.auth.jwt_handler import JWTHandler
    from app.auth.models import UserCreate, UserResponse, Token, RefreshTokenRequest
    from app.database.database import database, users_table
    
    router = APIRouter()
    oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/login")
    jwt_handler = JWTHandler()
    
    @router.post("/register", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
    async def register_user(user: UserCreate):
        """Register a new user"""
    
        # Check if user already exists
        query = select(users_table).where(users_table.c.email == user.email)
        existing_user = await database.fetch_one(query)
    
        if existing_user:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Email already registered"
            )
    
        # Hash password
        hashed_password = jwt_handler.hash_password(user.password)
    
        # Insert new user
        query = users_table.insert().values(
            email=user.email,
            full_name=user.full_name,
            hashed_password=hashed_password
        )
    
        user_id = await database.execute(query)
    
        # Fetch and return the created user
        query = select(users_table).where(users_table.c.id == user_id)
        created_user = await database.fetch_one(query)
    
        return UserResponse(**created_user)
    
    @router.post("/login", response_model=Token)
    async def login_user(form_data: OAuth2PasswordRequestForm = Depends()):
        """Authenticate user and return tokens"""
    
        # Find user by email
        query = select(users_table).where(users_table.c.email == form_data.username)
        user = await database.fetch_one(query)
    
        if not user:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid email or password",
                headers={"WWW-Authenticate": "Bearer"},
            )
    
        # Verify password
        if not jwt_handler.verify_password(form_data.password, user.hashed_password):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid email or password",
                headers={"WWW-Authenticate": "Bearer"},
            )
    
        # Check if user is active
        if not user.is_active:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Account is disabled"
            )
    
        # Create tokens
        token_data = {
            "sub": str(user.id),
            "email": user.email,
            "full_name": user.full_name,
            "role": "user"  # Default role
        }
    
        access_token = jwt_handler.create_access_token(token_data)
        refresh_token = jwt_handler.create_refresh_token({"sub": str(user.id)})
    
        return Token(
            access_token=access_token,
            refresh_token=refresh_token
        )
    
    @router.post("/refresh", response_model=Dict[str, str])
    async def refresh_token(refresh_request: RefreshTokenRequest):
        """Refresh access token using refresh token"""
    
        new_access_token = jwt_handler.refresh_access_token(refresh_request.refresh_token)
    
        if not new_access_token:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid refresh token"
            )
    
        return {"access_token": new_access_token, "token_type": "bearer"}
    
    @router.get("/me", response_model=UserResponse)
    async def get_current_user(token: str = Depends(oauth2_scheme)):
        """Get current user information"""
    
        payload = jwt_handler.verify_token(token)
    
        if not payload:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid token",
                headers={"WWW-Authenticate": "Bearer"},
            )
    
        user_id = payload.get("sub")
        if not user_id:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid token payload"
            )
    
        # Fetch user from database
        query = select(users_table).where(users_table.c.id == int(user_id))
        user = await database.fetch_one(query)
    
        if not user:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="User not found"
            )
    
        return UserResponse(**user)
    
    @router.post("/logout")
    async def logout_user(token: str = Depends(oauth2_scheme)):
        """Logout user (in a real application, you might want to blacklist the token)"""
    
        payload = jwt_handler.verify_token(token)
    
        if not payload:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid token"
            )
    
        # In a production application, you would add the token to a blacklist
        # For now, we'll just return a success message
        return {"message": "Successfully logged out"}
    Python

    Protected Routes

    Create backend/app/routers/users.py:

    from fastapi import APIRouter, Depends, HTTPException, status
    from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
    from sqlalchemy import select
    from typing import List
    
    from app.auth.jwt_handler import JWTHandler
    from app.auth.models import UserResponse
    from app.database.database import database, users_table
    
    router = APIRouter()
    security = HTTPBearer()
    jwt_handler = JWTHandler()
    
    async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
        """Dependency to get current user from JWT token"""
        token = credentials.credentials
        payload = jwt_handler.verify_token(token)
    
        if not payload:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid token"
            )
    
        user_id = payload.get("sub")
        if not user_id:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid token payload"
            )
    
        # Fetch user from database
        query = select(users_table).where(users_table.c.id == int(user_id))
        user = await database.fetch_one(query)
    
        if not user or not user.is_active:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="User not found or inactive"
            )
    
        return user
    
    async def require_admin(current_user = Depends(get_current_user)):
        """Dependency to require admin role"""
        # In a real application, you would check the user's role
        # For this example, we'll assume all users can access admin endpoints
        # You would typically have a role field in your user model
        return current_user
    
    @router.get("/profile", response_model=UserResponse)
    async def get_user_profile(current_user = Depends(get_current_user)):
        """Get current user's profile"""
        return UserResponse(**current_user)
    
    @router.get("/all", response_model=List[UserResponse])
    async def get_all_users(current_user = Depends(require_admin)):
        """Get all users (admin only)"""
        query = select(users_table)
        users = await database.fetch_all(query)
        return [UserResponse(**user) for user in users]
    
    @router.get("/protected-data")
    async def get_protected_data(current_user = Depends(get_current_user)):
        """Example endpoint that requires authentication"""
        return {
            "message": f"Hello {current_user.full_name}!",
            "data": {
                "user_id": current_user.id,
                "email": current_user.email,
                "timestamp": "2023-10-18T10:00:00Z",
                "secret_data": "This data is only available to authenticated users"
            }
        }
    Python

    Updated Main Application

    Update backend/app/main.py to include database connection:

    from fastapi import FastAPI, Depends, HTTPException, status
    from fastapi.middleware.cors import CORSMiddleware
    from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
    from decouple import config
    import uvicorn
    
    # Import our modules
    from app.auth.jwt_handler import JWTHandler
    from app.routers import auth, users
    from app.database.database import connect_db, disconnect_db
    
    # Initialize FastAPI app
    app = FastAPI(
        title="JWT Authentication API",
        description="A comprehensive JWT authentication system",
        version="1.0.0"
    )
    
    # CORS Configuration
    origins = config("ALLOWED_ORIGINS", default="http://localhost:3000").split(",")
    
    app.add_middleware(
        CORSMiddleware,
        allow_origins=origins,
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )
    
    # Security scheme
    security = HTTPBearer()
    
    # JWT Handler instance
    jwt_handler = JWTHandler()
    
    # Database event handlers
    @app.on_event("startup")
    async def startup():
        await connect_db()
        print("Database connected")
    
    @app.on_event("shutdown")
    async def shutdown():
        await disconnect_db()
        print("Database disconnected")
    
    # Include routers
    app.include_router(auth.router, prefix="/auth", tags=["Authentication"])
    app.include_router(users.router, prefix="/users", tags=["Users"])
    
    @app.get("/")
    async def root():
        return {
            "message": "JWT Authentication API is running!",
            "docs": "/docs",
            "redoc": "/redoc"
        }
    
    @app.get("/health")
    async def health_check():
        return {"status": "healthy", "timestamp": "2023-10-18T10:00:00Z"}
    
    if __name__ == "__main__":
        uvicorn.run(app, host="0.0.0.0", port=8000, reload=True)
    Python

    Authentication Flow Diagram

    sequenceDiagram
        participant C as Client
        participant API as FastAPI
        participant DB as Database
        participant JWT as JWT Handler
    
        Note over C,JWT: User Registration
        C->>API: POST /auth/register
        API->>DB: Check if user exists
        DB->>API: User not found
        API->>JWT: Hash password
        JWT->>API: Hashed password
        API->>DB: Create user
        DB->>API: User created
        API->>C: User response
    
        Note over C,JWT: User Login
        C->>API: POST /auth/login
        API->>DB: Find user by email
        DB->>API: User data
        API->>JWT: Verify password
        JWT->>API: Password valid
        API->>JWT: Create tokens
        JWT->>API: Access & refresh tokens
        API->>C: Token response
    
        Note over C,JWT: Protected Request
        C->>API: GET /users/profile (with token)
        API->>JWT: Verify token
        JWT->>API: Token valid + payload
        API->>DB: Get user details
        DB->>API: User data
        API->>C: Protected data

    Testing the Authentication API

    Manual Testing with curl

    # Register a new user
    curl -X POST "http://localhost:8000/auth/register" \
         -H "Content-Type: application/json" \
         -d '{
           "email": "john@example.com",
           "password": "password123",
           "full_name": "John Doe"
         }'
    
    # Login
    curl -X POST "http://localhost:8000/auth/login" \
         -H "Content-Type: application/x-www-form-urlencoded" \
         -d "username=john@example.com&password=password123"
    
    # Access protected route (replace TOKEN with actual token)
    curl -X GET "http://localhost:8000/users/profile" \
         -H "Authorization: Bearer TOKEN"
    
    # Refresh token
    curl -X POST "http://localhost:8000/auth/refresh" \
         -H "Content-Type: application/json" \
         -d '{"refresh_token": "REFRESH_TOKEN"}'
    Bash

    Automated Testing

    Create backend/tests/test_auth.py:

    import pytest
    from fastapi.testclient import TestClient
    from app.main import app
    from app.database.database import database, users_table
    
    client = TestClient(app)
    
    @pytest.fixture(scope="module")
    async def setup_db():
        await database.connect()
        yield
        # Cleanup
        await database.execute(users_table.delete())
        await database.disconnect()
    
    def test_register_user(setup_db):
        """Test user registration"""
        response = client.post(
            "/auth/register",
            json={
                "email": "test@example.com",
                "password": "testpassword123",
                "full_name": "Test User"
            }
        )
        assert response.status_code == 201
        data = response.json()
        assert data["email"] == "test@example.com"
        assert data["full_name"] == "Test User"
        assert "id" in data
    
    def test_register_duplicate_user():
        """Test registration with duplicate email"""
        # First registration
        client.post(
            "/auth/register",
            json={
                "email": "duplicate@example.com",
                "password": "password123",
                "full_name": "First User"
            }
        )
    
        # Duplicate registration
        response = client.post(
            "/auth/register",
            json={
                "email": "duplicate@example.com",
                "password": "password123",
                "full_name": "Second User"
            }
        )
        assert response.status_code == 400
        assert "already registered" in response.json()["detail"]
    
    def test_login_success():
        """Test successful login"""
        # First register a user
        client.post(
            "/auth/register",
            json={
                "email": "login@example.com",
                "password": "loginpassword",
                "full_name": "Login User"
            }
        )
    
        # Then login
        response = client.post(
            "/auth/login",
            data={
                "username": "login@example.com",
                "password": "loginpassword"
            }
        )
        assert response.status_code == 200
        data = response.json()
        assert "access_token" in data
        assert "refresh_token" in data
        assert data["token_type"] == "bearer"
    
    def test_login_invalid_credentials():
        """Test login with invalid credentials"""
        response = client.post(
            "/auth/login",
            data={
                "username": "nonexistent@example.com",
                "password": "wrongpassword"
            }
        )
        assert response.status_code == 401
        assert "Invalid email or password" in response.json()["detail"]
    
    def test_protected_route_with_valid_token():
        """Test accessing protected route with valid token"""
        # Register and login to get token
        client.post(
            "/auth/register",
            json={
                "email": "protected@example.com",
                "password": "password123",
                "full_name": "Protected User"
            }
        )
    
        login_response = client.post(
            "/auth/login",
            data={
                "username": "protected@example.com",
                "password": "password123"
            }
        )
        token = login_response.json()["access_token"]
    
        # Access protected route
        response = client.get(
            "/users/profile",
            headers={"Authorization": f"Bearer {token}"}
        )
        assert response.status_code == 200
        data = response.json()
        assert data["email"] == "protected@example.com"
    
    def test_protected_route_without_token():
        """Test accessing protected route without token"""
        response = client.get("/users/profile")
        assert response.status_code == 403  # FastAPI returns 403 for missing auth
    
    def test_protected_route_with_invalid_token():
        """Test accessing protected route with invalid token"""
        response = client.get(
            "/users/profile",
            headers={"Authorization": "Bearer invalid_token"}
        )
        assert response.status_code == 401
    Python

    7. Frontend Integration with JavaScript

    Now let’s build a complete frontend that integrates with our FastAPI JWT authentication system. This chapter covers client-side token management, API communication, and user interface components.

    API Communication Module

    Create frontend/js/api.js:

    /**
     * API Configuration and HTTP Client
     */
    class ApiClient {
        constructor(baseUrl = 'http://localhost:8000') {
            this.baseUrl = baseUrl;
            this.token = this.getStoredToken();
        }
    
        /**
         * Get stored token from localStorage
         */
        getStoredToken() {
            return localStorage.getItem('access_token');
        }
    
        /**
         * Store token in localStorage
         */
        storeToken(token) {
            localStorage.setItem('access_token', token);
            this.token = token;
        }
    
        /**
         * Remove token from localStorage
         */
        removeToken() {
            localStorage.removeItem('access_token');
            localStorage.removeItem('refresh_token');
            this.token = null;
        }
    
        /**
         * Get default headers for API requests
         */
        getHeaders(includeAuth = true) {
            const headers = {
                'Content-Type': 'application/json',
            };
    
            if (includeAuth && this.token) {
                headers['Authorization'] = `Bearer ${this.token}`;
            }
    
            return headers;
        }
    
        /**
         * Generic HTTP request method
         */
        async request(endpoint, options = {}) {
            const url = `${this.baseUrl}${endpoint}`;
            const config = {
                headers: this.getHeaders(options.includeAuth !== false),
                ...options,
            };
    
            try {
                const response = await fetch(url, config);
                const data = await response.json();
    
                if (!response.ok) {
                    throw new Error(data.detail || `HTTP error! status: ${response.status}`);
                }
    
                return data;
            } catch (error) {
                console.error('API request failed:', error);
                throw error;
            }
        }
    
        /**
         * GET request
         */
        async get(endpoint, options = {}) {
            return this.request(endpoint, { ...options, method: 'GET' });
        }
    
        /**
         * POST request
         */
        async post(endpoint, data = null, options = {}) {
            const config = {
                ...options,
                method: 'POST',
            };
    
            if (data) {
                config.body = JSON.stringify(data);
            }
    
            return this.request(endpoint, config);
        }
    
        /**
         * Register a new user
         */
        async register(userData) {
            return this.post('/auth/register', userData, { includeAuth: false });
        }
    
        /**
         * Login user
         */
        async login(email, password) {
            const formData = new FormData();
            formData.append('username', email);
            formData.append('password', password);
    
            const response = await fetch(`${this.baseUrl}/auth/login`, {
                method: 'POST',
                body: formData,
            });
    
            const data = await response.json();
    
            if (!response.ok) {
                throw new Error(data.detail || 'Login failed');
            }
    
            // Store tokens
            this.storeToken(data.access_token);
            localStorage.setItem('refresh_token', data.refresh_token);
    
            return data;
        }
    
        /**
         * Refresh access token
         */
        async refreshToken() {
            const refreshToken = localStorage.getItem('refresh_token');
    
            if (!refreshToken) {
                throw new Error('No refresh token available');
            }
    
            try {
                const response = await this.post('/auth/refresh', {
                    refresh_token: refreshToken
                }, { includeAuth: false });
    
                this.storeToken(response.access_token);
                return response;
            } catch (error) {
                // Refresh failed, remove all tokens
                this.removeToken();
                throw error;
            }
        }
    
        /**
         * Get current user profile
         */
        async getCurrentUser() {
            try {
                return await this.get('/auth/me');
            } catch (error) {
                // If token is invalid, try to refresh
                if (error.message.includes('401') || error.message.includes('Unauthorized')) {
                    try {
                        await this.refreshToken();
                        return await this.get('/auth/me');
                    } catch (refreshError) {
                        throw new Error('Authentication failed');
                    }
                }
                throw error;
            }
        }
    
        /**
         * Get user profile
         */
        async getUserProfile() {
            return this.get('/users/profile');
        }
    
        /**
         * Get protected data
         */
        async getProtectedData() {
            return this.get('/users/protected-data');
        }
    
        /**
         * Logout user
         */
        async logout() {
            try {
                await this.post('/auth/logout');
            } catch (error) {
                console.warn('Logout request failed:', error);
            } finally {
                this.removeToken();
            }
        }
    
        /**
         * Check if user is authenticated
         */
        isAuthenticated() {
            return !!this.token;
        }
    }
    
    // Create global API client instance
    const api = new ApiClient();
    JavaScript

    Authentication Management

    Create frontend/js/auth.js:

    /**
     * Authentication Management Class
     */
    class AuthManager {
        constructor(apiClient) {
            this.api = apiClient;
            this.currentUser = null;
            this.authCallbacks = [];
        }
    
        /**
         * Add callback for authentication state changes
         */
        onAuthStateChange(callback) {
            this.authCallbacks.push(callback);
        }
    
        /**
         * Notify all callbacks of authentication state change
         */
        notifyAuthStateChange(isAuthenticated, user = null) {
            this.authCallbacks.forEach(callback => {
                callback(isAuthenticated, user);
            });
        }
    
        /**
         * Initialize authentication state
         */
        async init() {
            if (this.api.isAuthenticated()) {
                try {
                    this.currentUser = await this.api.getCurrentUser();
                    this.notifyAuthStateChange(true, this.currentUser);
                    return true;
                } catch (error) {
                    console.error('Failed to get current user:', error);
                    this.api.removeToken();
                    this.notifyAuthStateChange(false);
                    return false;
                }
            } else {
                this.notifyAuthStateChange(false);
                return false;
            }
        }
    
        /**
         * Register a new user
         */
        async register(userData) {
            try {
                const user = await this.api.register(userData);
                this.showStatus('Registration successful! Please log in.', 'success');
                return user;
            } catch (error) {
                this.showStatus(`Registration failed: ${error.message}`, 'error');
                throw error;
            }
        }
    
        /**
         * Login user
         */
        async login(email, password) {
            try {
                const tokens = await this.api.login(email, password);
                this.currentUser = await this.api.getCurrentUser();
                this.notifyAuthStateChange(true, this.currentUser);
                this.showStatus('Login successful!', 'success');
                return tokens;
            } catch (error) {
                this.showStatus(`Login failed: ${error.message}`, 'error');
                throw error;
            }
        }
    
        /**
         * Logout user
         */
        async logout() {
            try {
                await this.api.logout();
                this.currentUser = null;
                this.notifyAuthStateChange(false);
                this.showStatus('Logged out successfully', 'info');
            } catch (error) {
                console.error('Logout error:', error);
                // Still clear local state even if API call fails
                this.currentUser = null;
                this.notifyAuthStateChange(false);
            }
        }
    
        /**
         * Get current user
         */
        getCurrentUser() {
            return this.currentUser;
        }
    
        /**
         * Check if user is authenticated
         */
        isAuthenticated() {
            return this.api.isAuthenticated() && this.currentUser !== null;
        }
    
        /**
         * Show status message to user
         */
        showStatus(message, type = 'info') {
            const statusElement = document.getElementById('status');
            if (statusElement) {
                statusElement.textContent = message;
                statusElement.className = `status-${type}`;
    
                // Clear status after 5 seconds
                setTimeout(() => {
                    statusElement.textContent = '';
                    statusElement.className = '';
                }, 5000);
            }
        }
    
        /**
         * Decode JWT token payload (client-side inspection only)
         */
        decodeToken(token) {
            try {
                const payload = token.split('.')[1];
                const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));
                return JSON.parse(decoded);
            } catch (error) {
                console.error('Failed to decode token:', error);
                return null;
            }
        }
    
        /**
         * Get token information for debugging
         */
        getTokenInfo() {
            const token = this.api.getStoredToken();
            if (!token) return null;
    
            const payload = this.decodeToken(token);
            if (!payload) return null;
    
            return {
                token: token,
                payload: payload,
                isExpired: payload.exp < Date.now() / 1000,
                expiresAt: new Date(payload.exp * 1000),
                issuedAt: new Date(payload.iat * 1000)
            };
        }
    }
    
    // Create global auth manager instance
    const auth = new AuthManager(api);
    JavaScript

    Main Application Logic

    Create frontend/js/main.js:

    /**
     * Main Application Class
     */
    class App {
        constructor() {
            this.currentView = 'login';
            this.init();
        }
    
        /**
         * Initialize the application
         */
        async init() {
            // Wait for DOM to be ready
            if (document.readyState === 'loading') {
                document.addEventListener('DOMContentLoaded', () => this.setup());
            } else {
                this.setup();
            }
        }
    
        /**
         * Setup the application
         */
        async setup() {
            console.log('Initializing JWT Demo App...');
    
            // Initialize authentication
            const isAuthenticated = await auth.init();
    
            // Setup authentication state change listener
            auth.onAuthStateChange((authenticated, user) => {
                this.handleAuthStateChange(authenticated, user);
            });
    
            // Setup event listeners
            this.setupEventListeners();
    
            // Show appropriate view
            if (isAuthenticated) {
                this.showDashboard();
            } else {
                this.showLogin();
            }
    
            console.log('App initialized successfully');
        }
    
        /**
         * Setup event listeners
         */
        setupEventListeners() {
            // Login form
            const loginForm = document.getElementById('login-form');
            if (loginForm) {
                loginForm.addEventListener('submit', (e) => this.handleLogin(e));
            }
    
            // Register form
            const registerForm = document.getElementById('register-form');
            if (registerForm) {
                registerForm.addEventListener('submit', (e) => this.handleRegister(e));
            }
    
            // Navigation buttons (will be created dynamically)
            document.addEventListener('click', (e) => {
                if (e.target.matches('[data-action]')) {
                    this.handleNavigation(e.target.dataset.action);
                }
            });
        }
    
        /**
         * Handle authentication state changes
         */
        handleAuthStateChange(authenticated, user) {
            console.log('Auth state changed:', { authenticated, user });
            this.updateNavigation(authenticated);
    
            if (authenticated) {
                this.showDashboard();
            } else {
                this.showLogin();
            }
        }
    
        /**
         * Update navigation based on authentication state
         */
        updateNavigation(authenticated) {
            const nav = document.getElementById('navigation');
    
            if (authenticated) {
                nav.innerHTML = `
                    <button data-action="dashboard" class="nav-btn">Dashboard</button>
                    <button data-action="profile" class="nav-btn">Profile</button>
                    <button data-action="logout" class="nav-btn">Logout</button>
                `;
            } else {
                nav.innerHTML = `
                    <button data-action="login" class="nav-btn">Login</button>
                    <button data-action="register" class="nav-btn">Register</button>
                `;
            }
    
            // Update active button
            this.updateActiveNavButton();
        }
    
        /**
         * Update active navigation button
         */
        updateActiveNavButton() {
            document.querySelectorAll('.nav-btn').forEach(btn => {
                btn.classList.remove('active');
                if (btn.dataset.action === this.currentView) {
                    btn.classList.add('active');
                }
            });
        }
    
        /**
         * Handle navigation actions
         */
        async handleNavigation(action) {
            switch (action) {
                case 'login':
                    this.showLogin();
                    break;
                case 'register':
                    this.showRegister();
                    break;
                case 'dashboard':
                    this.showDashboard();
                    break;
                case 'profile':
                    this.showProfile();
                    break;
                case 'logout':
                    await this.handleLogout();
                    break;
                default:
                    console.warn('Unknown navigation action:', action);
            }
        }
    
        /**
         * Handle login form submission
         */
        async handleLogin(event) {
            event.preventDefault();
    
            const email = document.getElementById('login-email').value;
            const password = document.getElementById('login-password').value;
    
            if (!email || !password) {
                auth.showStatus('Please fill in all fields', 'error');
                return;
            }
    
            try {
                this.setLoading(true);
                await auth.login(email, password);
    
                // Clear form
                document.getElementById('login-form').reset();
            } catch (error) {
                console.error('Login error:', error);
            } finally {
                this.setLoading(false);
            }
        }
    
        /**
         * Handle register form submission
         */
        async handleRegister(event) {
            event.preventDefault();
    
            const name = document.getElementById('register-name').value;
            const email = document.getElementById('register-email').value;
            const password = document.getElementById('register-password').value;
    
            if (!name || !email || !password) {
                auth.showStatus('Please fill in all fields', 'error');
                return;
            }
    
            if (password.length < 6) {
                auth.showStatus('Password must be at least 6 characters', 'error');
                return;
            }
    
            try {
                this.setLoading(true);
                await auth.register({
                    full_name: name,
                    email: email,
                    password: password
                });
    
                // Clear form and switch to login
                document.getElementById('register-form').reset();
                this.showLogin();
            } catch (error) {
                console.error('Registration error:', error);
            } finally {
                this.setLoading(false);
            }
        }
    
        /**
         * Handle logout
         */
        async handleLogout() {
            try {
                this.setLoading(true);
                await auth.logout();
            } catch (error) {
                console.error('Logout error:', error);
            } finally {
                this.setLoading(false);
            }
        }
    
        /**
         * Show login view
         */
        showLogin() {
            this.currentView = 'login';
            this.hideAllSections();
            document.getElementById('login-section').style.display = 'block';
            this.updateActiveNavButton();
        }
    
        /**
         * Show register view
         */
        showRegister() {
            this.currentView = 'register';
            this.hideAllSections();
            document.getElementById('register-section').style.display = 'block';
            this.updateActiveNavButton();
        }
    
        /**
         * Show dashboard view
         */
        async showDashboard() {
            this.currentView = 'dashboard';
            this.hideAllSections();
            document.getElementById('dashboard-section').style.display = 'block';
            this.updateActiveNavButton();
    
            try {
                await this.loadDashboardData();
            } catch (error) {
                console.error('Failed to load dashboard data:', error);
                auth.showStatus('Failed to load dashboard data', 'error');
            }
        }
    
        /**
         * Show profile view
         */
        async showProfile() {
            this.currentView = 'profile';
            await this.showDashboard(); // Reuse dashboard for now
        }
    
        /**
         * Load dashboard data
         */
        async loadDashboardData() {
            const userInfoElement = document.getElementById('user-info');
            const protectedContentElement = document.getElementById('protected-content');
    
            try {
                // Load user profile
                const userProfile = await api.getUserProfile();
                userInfoElement.innerHTML = `
                    <h3>User Information</h3>
                    <p><strong>Name:</strong> ${userProfile.full_name}</p>
                    <p><strong>Email:</strong> ${userProfile.email}</p>
                    <p><strong>ID:</strong> ${userProfile.id}</p>
                    <p><strong>Active:</strong> ${userProfile.is_active ? 'Yes' : 'No'}</p>
                    <p><strong>Member since:</strong> ${new Date(userProfile.created_at).toLocaleDateString()}</p>
                `;
    
                // Load protected data
                const protectedData = await api.getProtectedData();
                protectedContentElement.innerHTML = `
                    <h3>Protected Content</h3>
                    <p><strong>Message:</strong> ${protectedData.message}</p>
                    <p><strong>Secret Data:</strong> ${protectedData.data.secret_data}</p>
                    <p><strong>Timestamp:</strong> ${protectedData.data.timestamp}</p>
                `;
    
                // Show token information (for debugging)
                this.showTokenInfo();
    
            } catch (error) {
                userInfoElement.innerHTML = '<p class="error">Failed to load user information</p>';
                protectedContentElement.innerHTML = '<p class="error">Failed to load protected content</p>';
                throw error;
            }
        }
    
        /**
         * Show token information for debugging
         */
        showTokenInfo() {
            const tokenInfo = auth.getTokenInfo();
            if (!tokenInfo) return;
    
            const tokenDisplay = document.createElement('div');
            tokenDisplay.className = 'token-display';
            tokenDisplay.innerHTML = `
                <h4>Token Information (Debug)</h4>
                <p><strong>Expires:</strong> ${tokenInfo.expiresAt.toLocaleString()}</p>
                <p><strong>Issued:</strong> ${tokenInfo.issuedAt.toLocaleString()}</p>
                <p><strong>Expired:</strong> ${tokenInfo.isExpired ? 'Yes' : 'No'}</p>
                <details>
                    <summary>Token Payload</summary>
                    <pre>${JSON.stringify(tokenInfo.payload, null, 2)}</pre>
                </details>
            `;
    
            const protectedContentElement = document.getElementById('protected-content');
            protectedContentElement.appendChild(tokenDisplay);
        }
    
        /**
         * Hide all sections
         */
        hideAllSections() {
            document.querySelectorAll('.auth-section, .dashboard').forEach(section => {
                section.style.display = 'none';
            });
        }
    
        /**
         * Set loading state
         */
        setLoading(loading) {
            const buttons = document.querySelectorAll('button');
            buttons.forEach(button => {
                if (loading) {
                    button.disabled = true;
                    if (button.type === 'submit') {
                        button.innerHTML = '<span class="loading"></span> Loading...';
                    }
                } else {
                    button.disabled = false;
                    if (button.type === 'submit') {
                        button.textContent = button.textContent.includes('Login') ? 'Login' : 'Register';
                    }
                }
            });
        }
    }
    
    // Initialize the application
    const app = new App();
    JavaScript

    Token Visualization Component

    Let’s add a token visualization component to help understand JWT structure:

    /**
     * JWT Token Visualizer
     */
    class TokenVisualizer {
        constructor() {
            this.createVisualizerHTML();
        }
    
        /**
         * Create visualizer HTML structure
         */
        createVisualizerHTML() {
            const container = document.createElement('div');
            container.id = 'token-visualizer';
            container.className = 'token-visualizer';
            container.innerHTML = `
                <h3>JWT Token Visualizer</h3>
                <div class="token-input-section">
                    <textarea id="token-input" placeholder="Paste JWT token here..."></textarea>
                    <button onclick="tokenVisualizer.visualizeToken()">Analyze Token</button>
                </div>
                <div id="token-output" class="token-output"></div>
            `;
    
            // Add to dashboard
            const dashboard = document.getElementById('dashboard-section');
            if (dashboard) {
                dashboard.appendChild(container);
            }
        }
    
        /**
         * Visualize JWT token structure
         */
        visualizeToken() {
            const tokenInput = document.getElementById('token-input');
            const tokenOutput = document.getElementById('token-output');
            const token = tokenInput.value.trim();
    
            if (!token) {
                tokenOutput.innerHTML = '<p class="error">Please enter a JWT token</p>';
                return;
            }
    
            try {
                const parts = token.split('.');
                if (parts.length !== 3) {
                    throw new Error('Invalid JWT format - must have 3 parts');
                }
    
                const [headerB64, payloadB64, signature] = parts;
    
                // Decode header and payload
                const header = JSON.parse(atob(headerB64.replace(/-/g, '+').replace(/_/g, '/')));
                const payload = JSON.parse(atob(payloadB64.replace(/-/g, '+').replace(/_/g, '/')));
    
                // Format output
                tokenOutput.innerHTML = `
                    <div class="token-part">
                        <h4 class="token-header">Header</h4>
                        <pre>${JSON.stringify(header, null, 2)}</pre>
                        <p><strong>Base64:</strong> <code>${headerB64}</code></p>
                    </div>
    
                    <div class="token-part">
                        <h4 class="token-payload">Payload</h4>
                        <pre>${JSON.stringify(payload, null, 2)}</pre>
                        <p><strong>Base64:</strong> <code>${payloadB64}</code></p>
                        ${this.formatPayloadInfo(payload)}
                    </div>
    
                    <div class="token-part">
                        <h4 class="token-signature">Signature</h4>
                        <code>${signature}</code>
                        <p><em>Signature verification requires the secret key</em></p>
                    </div>
                `;
    
            } catch (error) {
                tokenOutput.innerHTML = `<p class="error">Error parsing token: ${error.message}</p>`;
            }
        }
    
        /**
         * Format payload information
         */
        formatPayloadInfo(payload) {
            let info = '<div class="payload-info"><h5>Claim Information:</h5><ul>';
    
            if (payload.exp) {
                const expDate = new Date(payload.exp * 1000);
                const isExpired = expDate < new Date();
                info += `<li><strong>Expires:</strong> ${expDate.toLocaleString()} ${isExpired ? '(EXPIRED)' : ''}</li>`;
            }
    
            if (payload.iat) {
                const iatDate = new Date(payload.iat * 1000);
                info += `<li><strong>Issued At:</strong> ${iatDate.toLocaleString()}</li>`;
            }
    
            if (payload.sub) {
                info += `<li><strong>Subject:</strong> ${payload.sub}</li>`;
            }
    
            if (payload.iss) {
                info += `<li><strong>Issuer:</strong> ${payload.iss}</li>`;
            }
    
            if (payload.aud) {
                info += `<li><strong>Audience:</strong> ${Array.isArray(payload.aud) ? payload.aud.join(', ') : payload.aud}</li>`;
            }
    
            info += '</ul></div>';
            return info;
        }
    }
    
    // Initialize token visualizer
    const tokenVisualizer = new TokenVisualizer();
    JavaScript

    Frontend Flow Diagram

    flowchart TD
        A[Page Load] --> B[Initialize App]
        B --> C[Check Stored Token]
        C --> D{Token Exists?}
    
        D -->|Yes| E[Validate Token with API]
        D -->|No| F[Show Login Form]
    
        E --> G{Token Valid?}
        G -->|Yes| H[Show Dashboard]
        G -->|No| I[Try Refresh Token]
    
        I --> J{Refresh Success?}
        J -->|Yes| H
        J -->|No| F
    
        F --> K[User Login/Register]
        K --> L[API Authentication]
        L --> M{Auth Success?}
        M -->|Yes| N[Store Tokens]
        M -->|No| O[Show Error]
    
        N --> H
        O --> F
    
        H --> P[Load Protected Data]
        P --> Q[Display User Interface]

    This frontend implementation provides:

    1. Token Management: Automatic storage, retrieval, and refresh of JWT tokens
    2. API Communication: Centralized HTTP client with authentication handling
    3. User Interface: Clean, responsive interface for authentication and dashboard
    4. Error Handling: Comprehensive error handling and user feedback
    5. Token Visualization: Debug tool to inspect JWT token structure
    6. Automatic Refresh: Transparent token refresh when needed
    7. Responsive Design: Mobile-friendly CSS layout

    The frontend seamlessly integrates with our FastAPI backend and provides a complete user experience for JWT authentication.


    8. Token Storage and Management

    Proper token storage is crucial for security. This chapter covers different storage options, their security implications, and best practices for token management.

    Storage Options Comparison

    graph TB
        A[JWT Storage Options] --> B[Browser Storage]
        A --> C[HTTP Cookies]
        A --> D[Memory Only]
    
        B --> E[localStorage]
        B --> F[sessionStorage]
        B --> G[IndexedDB]
    
        C --> H[HttpOnly Cookies]
        C --> I[Secure Cookies]
        C --> J[SameSite Cookies]
    
        D --> K[JavaScript Variables]
        D --> L[Web Workers]

    localStorage Implementation

    Pros:

    • Persists across browser sessions
    • Large storage capacity
    • Easy to implement

    Cons:

    • Vulnerable to XSS attacks
    • Accessible via JavaScript
    • No automatic expiration
    class LocalStorageTokenManager {
        constructor() {
            this.ACCESS_TOKEN_KEY = 'jwt_access_token';
            this.REFRESH_TOKEN_KEY = 'jwt_refresh_token';
            this.USER_KEY = 'jwt_user_data';
        }
    
        /**
         * Store tokens in localStorage
         */
        storeTokens(accessToken, refreshToken, userData = null) {
            try {
                localStorage.setItem(this.ACCESS_TOKEN_KEY, accessToken);
                localStorage.setItem(this.REFRESH_TOKEN_KEY, refreshToken);
    
                if (userData) {
                    localStorage.setItem(this.USER_KEY, JSON.stringify(userData));
                }
    
                // Set expiration timestamp
                const tokenPayload = this.decodeToken(accessToken);
                if (tokenPayload && tokenPayload.exp) {
                    localStorage.setItem(
                        'jwt_expires_at', 
                        (tokenPayload.exp * 1000).toString()
                    );
                }
    
                return true;
            } catch (error) {
                console.error('Failed to store tokens:', error);
                return false;
            }
        }
    
        /**
         * Get access token
         */
        getAccessToken() {
            try {
                const token = localStorage.getItem(this.ACCESS_TOKEN_KEY);
    
                // Check if token is expired
                if (token && this.isTokenExpired(token)) {
                    this.clearTokens();
                    return null;
                }
    
                return token;
            } catch (error) {
                console.error('Failed to get access token:', error);
                return null;
            }
        }
    
        /**
         * Get refresh token
         */
        getRefreshToken() {
            return localStorage.getItem(this.REFRESH_TOKEN_KEY);
        }
    
        /**
         * Get stored user data
         */
        getUserData() {
            try {
                const userData = localStorage.getItem(this.USER_KEY);
                return userData ? JSON.parse(userData) : null;
            } catch (error) {
                console.error('Failed to get user data:', error);
                return null;
            }
        }
    
        /**
         * Clear all stored tokens and data
         */
        clearTokens() {
            localStorage.removeItem(this.ACCESS_TOKEN_KEY);
            localStorage.removeItem(this.REFRESH_TOKEN_KEY);
            localStorage.removeItem(this.USER_KEY);
            localStorage.removeItem('jwt_expires_at');
        }
    
        /**
         * Check if token is expired
         */
        isTokenExpired(token) {
            try {
                const payload = this.decodeToken(token);
                if (!payload || !payload.exp) return true;
    
                return Date.now() >= payload.exp * 1000;
            } catch (error) {
                return true;
            }
        }
    
        /**
         * Decode JWT token payload
         */
        decodeToken(token) {
            try {
                const base64Payload = token.split('.')[1];
                const payload = atob(base64Payload.replace(/-/g, '+').replace(/_/g, '/'));
                return JSON.parse(payload);
            } catch (error) {
                return null;
            }
        }
    
        /**
         * Get time until token expiration
         */
        getTimeUntilExpiration() {
            const token = this.getAccessToken();
            if (!token) return 0;
    
            const payload = this.decodeToken(token);
            if (!payload || !payload.exp) return 0;
    
            const expirationTime = payload.exp * 1000;
            const currentTime = Date.now();
    
            return Math.max(0, expirationTime - currentTime);
        }
    }
    JavaScript

    Pros:

    • HttpOnly cookies prevent XSS access
    • Automatic inclusion in requests
    • Built-in expiration

    Cons:

    • Vulnerable to CSRF attacks
    • Size limitations
    • Complex SameSite configuration
    class CookieTokenManager {
        constructor() {
            this.COOKIE_NAME = 'jwt_token';
            this.REFRESH_COOKIE_NAME = 'jwt_refresh';
        }
    
        /**
         * Set secure HTTP-only cookies (requires server cooperation)
         */
        setCookieTokens(accessToken, refreshToken) {
            // This would typically be done server-side
            // Client-side cookies are less secure
    
            const accessTokenExpiry = this.getTokenExpiration(accessToken);
            const refreshTokenExpiry = this.getTokenExpiration(refreshToken);
    
            this.setCookie(this.COOKIE_NAME, accessToken, accessTokenExpiry, {
                httpOnly: false, // Can't set HttpOnly from client-side
                secure: window.location.protocol === 'https:',
                sameSite: 'Strict'
            });
    
            this.setCookie(this.REFRESH_COOKIE_NAME, refreshToken, refreshTokenExpiry, {
                httpOnly: false,
                secure: window.location.protocol === 'https:',
                sameSite: 'Strict'
            });
        }
    
        /**
         * Set cookie with options
         */
        setCookie(name, value, expiration, options = {}) {
            let cookieString = `${name}=${value}`;
    
            if (expiration) {
                cookieString += `; expires=${expiration.toUTCString()}`;
            }
    
            if (options.secure) {
                cookieString += '; Secure';
            }
    
            if (options.sameSite) {
                cookieString += `; SameSite=${options.sameSite}`;
            }
    
            if (options.path) {
                cookieString += `; Path=${options.path}`;
            } else {
                cookieString += '; Path=/';
            }
    
            document.cookie = cookieString;
        }
    
        /**
         * Get cookie value
         */
        getCookie(name) {
            const cookies = document.cookie.split(';');
    
            for (let cookie of cookies) {
                const [cookieName, cookieValue] = cookie.trim().split('=');
                if (cookieName === name) {
                    return cookieValue;
                }
            }
    
            return null;
        }
    
        /**
         * Get access token from cookie
         */
        getAccessToken() {
            return this.getCookie(this.COOKIE_NAME);
        }
    
        /**
         * Get refresh token from cookie
         */
        getRefreshToken() {
            return this.getCookie(this.REFRESH_COOKIE_NAME);
        }
    
        /**
         * Clear cookies
         */
        clearTokens() {
            this.setCookie(this.COOKIE_NAME, '', new Date(0));
            this.setCookie(this.REFRESH_COOKIE_NAME, '', new Date(0));
        }
    
        /**
         * Get token expiration date
         */
        getTokenExpiration(token) {
            try {
                const payload = JSON.parse(atob(token.split('.')[1]));
                return new Date(payload.exp * 1000);
            } catch (error) {
                return new Date(Date.now() + 60 * 60 * 1000); // 1 hour default
            }
        }
    }
    JavaScript

    Memory-Only Storage (Most Secure)

    Pros:

    • No persistence after page reload
    • Not accessible via XSS or storage APIs
    • Automatically cleared on navigation

    Cons:

    • Lost on page refresh
    • Complex state management
    • Poor user experience
    class MemoryTokenManager {
        constructor() {
            this.accessToken = null;
            this.refreshToken = null;
            this.userData = null;
            this.expirationTimer = null;
        }
    
        /**
         * Store tokens in memory
         */
        storeTokens(accessToken, refreshToken, userData = null) {
            this.accessToken = accessToken;
            this.refreshToken = refreshToken;
            this.userData = userData;
    
            // Set up automatic token refresh
            this.setupTokenRefresh();
    
            return true;
        }
    
        /**
         * Get access token
         */
        getAccessToken() {
            if (this.accessToken && this.isTokenExpired(this.accessToken)) {
                this.clearTokens();
                return null;
            }
            return this.accessToken;
        }
    
        /**
         * Get refresh token
         */
        getRefreshToken() {
            return this.refreshToken;
        }
    
        /**
         * Get user data
         */
        getUserData() {
            return this.userData;
        }
    
        /**
         * Clear tokens from memory
         */
        clearTokens() {
            this.accessToken = null;
            this.refreshToken = null;
            this.userData = null;
    
            if (this.expirationTimer) {
                clearTimeout(this.expirationTimer);
                this.expirationTimer = null;
            }
        }
    
        /**
         * Check if token is expired
         */
        isTokenExpired(token) {
            try {
                const payload = JSON.parse(atob(token.split('.')[1]));
                return Date.now() >= payload.exp * 1000;
            } catch (error) {
                return true;
            }
        }
    
        /**
         * Setup automatic token refresh
         */
        setupTokenRefresh() {
            if (this.expirationTimer) {
                clearTimeout(this.expirationTimer);
            }
    
            if (!this.accessToken) return;
    
            try {
                const payload = JSON.parse(atob(this.accessToken.split('.')[1]));
                const expirationTime = payload.exp * 1000;
                const currentTime = Date.now();
    
                // Refresh 5 minutes before expiration
                const refreshTime = expirationTime - currentTime - (5 * 60 * 1000);
    
                if (refreshTime > 0) {
                    this.expirationTimer = setTimeout(() => {
                        this.attemptTokenRefresh();
                    }, refreshTime);
                }
            } catch (error) {
                console.error('Failed to setup token refresh:', error);
            }
        }
    
        /**
         * Attempt to refresh token
         */
        async attemptTokenRefresh() {
            if (!this.refreshToken) return;
    
            try {
                const response = await fetch('/auth/refresh', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        refresh_token: this.refreshToken
                    })
                });
    
                if (response.ok) {
                    const data = await response.json();
                    this.accessToken = data.access_token;
                    this.setupTokenRefresh();
    
                    // Notify application of token refresh
                    window.dispatchEvent(new CustomEvent('tokenRefreshed', {
                        detail: { accessToken: this.accessToken }
                    }));
                } else {
                    // Refresh failed, clear tokens
                    this.clearTokens();
                    window.dispatchEvent(new CustomEvent('tokenExpired'));
                }
            } catch (error) {
                console.error('Token refresh failed:', error);
                this.clearTokens();
                window.dispatchEvent(new CustomEvent('tokenExpired'));
            }
        }
    }
    JavaScript

    Hybrid Storage Strategy

    Combine multiple storage methods for optimal security and user experience:

    class HybridTokenManager {
        constructor(options = {}) {
            this.useSecureCookies = options.useSecureCookies || false;
            this.fallbackToMemory = options.fallbackToMemory || false;
    
            // Initialize storage managers
            this.localStorage = new LocalStorageTokenManager();
            this.cookieStorage = new CookieTokenManager();
            this.memoryStorage = new MemoryTokenManager();
    
            // Choose primary storage method
            this.primaryStorage = this.choosePrimaryStorage();
        }
    
        /**
         * Choose the best storage method based on environment
         */
        choosePrimaryStorage() {
            // Check for HTTPS
            const isSecure = window.location.protocol === 'https:';
    
            // Check for storage availability
            const hasLocalStorage = this.isStorageAvailable('localStorage');
    
            if (this.useSecureCookies && isSecure) {
                return this.cookieStorage;
            } else if (hasLocalStorage && !this.fallbackToMemory) {
                return this.localStorage;
            } else {
                return this.memoryStorage;
            }
        }
    
        /**
         * Check if storage type is available
         */
        isStorageAvailable(type) {
            try {
                const storage = window[type];
                const test = '__storage_test__';
                storage.setItem(test, test);
                storage.removeItem(test);
                return true;
            } catch (error) {
                return false;
            }
        }
    
        /**
         * Store tokens using primary storage
         */
        storeTokens(accessToken, refreshToken, userData = null) {
            return this.primaryStorage.storeTokens(accessToken, refreshToken, userData);
        }
    
        /**
         * Get access token
         */
        getAccessToken() {
            return this.primaryStorage.getAccessToken();
        }
    
        /**
         * Get refresh token
         */
        getRefreshToken() {
            return this.primaryStorage.getRefreshToken();
        }
    
        /**
         * Get user data
         */
        getUserData() {
            return this.primaryStorage.getUserData();
        }
    
        /**
         * Clear all tokens
         */
        clearTokens() {
            // Clear from all storage methods to be safe
            this.localStorage.clearTokens();
            this.cookieStorage.clearTokens();
            this.memoryStorage.clearTokens();
        }
    
        /**
         * Get storage type being used
         */
        getStorageType() {
            if (this.primaryStorage === this.cookieStorage) return 'cookies';
            if (this.primaryStorage === this.memoryStorage) return 'memory';
            return 'localStorage';
        }
    }
    JavaScript

    Token Refresh Strategy

    class TokenRefreshManager {
        constructor(tokenManager, apiClient) {
            this.tokenManager = tokenManager;
            this.apiClient = apiClient;
            this.refreshPromise = null;
            this.refreshBuffer = 5 * 60 * 1000; // 5 minutes
        }
    
        /**
         * Check if token needs refresh
         */
        needsRefresh(token) {
            try {
                const payload = JSON.parse(atob(token.split('.')[1]));
                const expirationTime = payload.exp * 1000;
                const currentTime = Date.now();
    
                return (expirationTime - currentTime) < this.refreshBuffer;
            } catch (error) {
                return true;
            }
        }
    
        /**
         * Refresh token if needed
         */
        async refreshIfNeeded() {
            const accessToken = this.tokenManager.getAccessToken();
    
            if (!accessToken || !this.needsRefresh(accessToken)) {
                return accessToken;
            }
    
            // If refresh is already in progress, wait for it
            if (this.refreshPromise) {
                return this.refreshPromise;
            }
    
            this.refreshPromise = this.performRefresh();
    
            try {
                const result = await this.refreshPromise;
                return result;
            } finally {
                this.refreshPromise = null;
            }
        }
    
        /**
         * Perform token refresh
         */
        async performRefresh() {
            const refreshToken = this.tokenManager.getRefreshToken();
    
            if (!refreshToken) {
                throw new Error('No refresh token available');
            }
    
            try {
                const response = await this.apiClient.post('/auth/refresh', {
                    refresh_token: refreshToken
                });
    
                const newAccessToken = response.access_token;
    
                // Update stored token
                this.tokenManager.storeTokens(
                    newAccessToken,
                    refreshToken,
                    this.tokenManager.getUserData()
                );
    
                return newAccessToken;
            } catch (error) {
                // Refresh failed, clear all tokens
                this.tokenManager.clearTokens();
                throw new Error('Token refresh failed');
            }
        }
    
        /**
         * Setup automatic refresh
         */
        setupAutoRefresh() {
            setInterval(async () => {
                try {
                    await this.refreshIfNeeded();
                } catch (error) {
                    console.warn('Auto refresh failed:', error);
                    // Emit event for application to handle
                    window.dispatchEvent(new CustomEvent('autoRefreshFailed', {
                        detail: { error }
                    }));
                }
            }, 60000); // Check every minute
        }
    }
    JavaScript

    Security Comparison Table

    Storage MethodXSS ResistanceCSRF ResistancePersistencePerformanceComplexity
    localStorage❌ Low✅ High✅ High✅ High✅ Low
    sessionStorage❌ Low✅ High⚠️ Medium✅ High✅ Low
    HttpOnly Cookies✅ High❌ Low*✅ High✅ High⚠️ Medium
    Memory Only✅ High✅ High❌ None✅ High❌ High
    Hybrid Approach⚠️ Medium⚠️ Medium✅ High✅ High❌ High

    *Can be mitigated with CSRF tokens and SameSite cookies

    Best Practices for Token Storage

    1. Use HTTPS Always: Never transmit tokens over HTTP
    2. Implement Token Rotation: Regularly refresh access tokens
    3. Set Appropriate Expiration: Short-lived access tokens (15-30 minutes)
    4. Sanitize All Inputs: Prevent XSS attacks
    5. Implement CSRF Protection: When using cookies
    6. Monitor for Suspicious Activity: Log and alert on unusual patterns
    7. Have a Revocation Strategy: Ability to invalidate tokens immediately

    9. Authorization and Role-Based Access Control

    Authorization determines what authenticated users can do. This chapter covers implementing role-based access control (RBAC) and fine-grained permissions with JWT.

    RBAC Architecture

    graph TB
        A[User] --> B[Assigned Roles]
        B --> C[Role 1: Admin]
        B --> D[Role 2: Manager]
        B --> E[Role 3: User]
    
        C --> F[Admin Permissions]
        D --> G[Manager Permissions]
        E --> H[User Permissions]
    
        F --> I[Create Users]
        F --> J[Delete Users]
        F --> K[System Config]
    
        G --> L[Manage Team]
        G --> M[View Reports]
        G --> N[Approve Requests]
    
        H --> O[View Profile]
        H --> P[Update Profile]
        H --> Q[Submit Requests]

    Enhanced User Model with Roles

    Update backend/app/database/database.py:

    from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, Boolean, DateTime, ForeignKey, Text
    from sqlalchemy.sql import func
    from databases import Database
    from decouple import config
    
    DATABASE_URL = config("DATABASE_URL", default="sqlite:///./app.db")
    
    # Database instance
    database = Database(DATABASE_URL)
    
    # SQLAlchemy metadata
    metadata = MetaData()
    
    # Roles table
    roles_table = Table(
        "roles",
        metadata,
        Column("id", Integer, primary_key=True, index=True),
        Column("name", String(50), unique=True, nullable=False),
        Column("description", Text),
        Column("created_at", DateTime(timezone=True), server_default=func.now()),
    )
    
    # Permissions table
    permissions_table = Table(
        "permissions",
        metadata,
        Column("id", Integer, primary_key=True, index=True),
        Column("name", String(100), unique=True, nullable=False),
        Column("description", Text),
        Column("resource", String(50), nullable=False),
        Column("action", String(50), nullable=False),
        Column("created_at", DateTime(timezone=True), server_default=func.now()),
    )
    
    # Role-Permission mapping table
    role_permissions_table = Table(
        "role_permissions",
        metadata,
        Column("role_id", Integer, ForeignKey("roles.id"), primary_key=True),
        Column("permission_id", Integer, ForeignKey("permissions.id"), primary_key=True),
    )
    
    # Updated Users table
    users_table = Table(
        "users",
        metadata,
        Column("id", Integer, primary_key=True, index=True),
        Column("email", String, unique=True, index=True, nullable=False),
        Column("full_name", String, nullable=False),
        Column("hashed_password", String, nullable=False),
        Column("role_id", Integer, ForeignKey("roles.id"), default=1),  # Default to 'user' role
        Column("is_active", Boolean, default=True),
        Column("created_at", DateTime(timezone=True), server_default=func.now()),
        Column("updated_at", DateTime(timezone=True), onupdate=func.now()),
    )
    
    # Create engine
    engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
    
    # Create tables
    metadata.create_all(bind=engine)
    
    async def connect_db():
        """Connect to database"""
        await database.connect()
    
    async def disconnect_db():
        """Disconnect from database"""
        await database.disconnect()
    
    async def create_default_roles_and_permissions():
        """Create default roles and permissions"""
    
        # Check if roles already exist
        existing_roles = await database.fetch_all("SELECT * FROM roles")
        if existing_roles:
            return
    
        # Create default roles
        admin_role_id = await database.execute(
            roles_table.insert().values(
                name="admin",
                description="System administrator with full access"
            )
        )
    
        manager_role_id = await database.execute(
            roles_table.insert().values(
                name="manager",
                description="Manager with team oversight capabilities"
            )
        )
    
        user_role_id = await database.execute(
            roles_table.insert().values(
                name="user",
                description="Regular user with basic access"
            )
        )
    
        # Create default permissions
        permissions = [
            # User management
            {"name": "user:create", "description": "Create new users", "resource": "user", "action": "create"},
            {"name": "user:read", "description": "View user information", "resource": "user", "action": "read"},
            {"name": "user:update", "description": "Update user information", "resource": "user", "action": "update"},
            {"name": "user:delete", "description": "Delete users", "resource": "user", "action": "delete"},
    
            # Profile management
            {"name": "profile:read", "description": "View own profile", "resource": "profile", "action": "read"},
            {"name": "profile:update", "description": "Update own profile", "resource": "profile", "action": "update"},
    
            # Reports
            {"name": "report:read", "description": "View reports", "resource": "report", "action": "read"},
            {"name": "report:create", "description": "Create reports", "resource": "report", "action": "create"},
    
            # System administration
            {"name": "system:config", "description": "Configure system settings", "resource": "system", "action": "config"},
            {"name": "system:monitor", "description": "Monitor system health", "resource": "system", "action": "monitor"},
        ]
    
        permission_ids = {}
        for perm in permissions:
            perm_id = await database.execute(permissions_table.insert().values(**perm))
            permission_ids[perm["name"]] = perm_id
    
        # Assign permissions to roles
    
        # Admin gets all permissions
        admin_permissions = list(permission_ids.values())
        for perm_id in admin_permissions:
            await database.execute(
                role_permissions_table.insert().values(
                    role_id=admin_role_id,
                    permission_id=perm_id
                )
            )
    
        # Manager permissions
        manager_permissions = [
            "user:read", "user:update", "profile:read", "profile:update",
            "report:read", "report:create", "system:monitor"
        ]
        for perm_name in manager_permissions:
            if perm_name in permission_ids:
                await database.execute(
                    role_permissions_table.insert().values(
                        role_id=manager_role_id,
                        permission_id=permission_ids[perm_name]
                    )
                )
    
        # User permissions
        user_permissions = ["profile:read", "profile:update"]
        for perm_name in user_permissions:
            if perm_name in permission_ids:
                await database.execute(
                    role_permissions_table.insert().values(
                        role_id=user_role_id,
                        permission_id=permission_ids[perm_name]
                    )
                )
    Python

    Enhanced JWT Handler with Permissions

    Update backend/app/auth/jwt_handler.py:

    from datetime import datetime, timedelta
    from typing import Optional, Dict, Any, List
    import jwt
    from decouple import config
    from passlib.context import CryptContext
    from sqlalchemy import select
    from app.database.database import database, roles_table, permissions_table, role_permissions_table
    
    class JWTHandler:
        def __init__(self):
            self.secret_key = config("SECRET_KEY")
            self.algorithm = config("ALGORITHM", default="HS256")
            self.access_token_expire_minutes = int(config("ACCESS_TOKEN_EXPIRE_MINUTES", default=30))
            self.refresh_token_expire_days = int(config("REFRESH_TOKEN_EXPIRE_DAYS", default=7))
    
            # Password hashing
            self.pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
    
        async def get_user_permissions(self, user_id: int) -> List[str]:
            """Get user permissions based on their role"""
            query = """
            SELECT p.name
            FROM users u
            JOIN roles r ON u.role_id = r.id
            JOIN role_permissions rp ON r.id = rp.role_id
            JOIN permissions p ON rp.permission_id = p.id
            WHERE u.id = :user_id AND u.is_active = true
            """
    
            result = await database.fetch_all(query, {"user_id": user_id})
            return [row["name"] for row in result]
    
        async def get_user_role(self, user_id: int) -> Optional[str]:
            """Get user role name"""
            query = """
            SELECT r.name
            FROM users u
            JOIN roles r ON u.role_id = r.id
            WHERE u.id = :user_id AND u.is_active = true
            """
    
            result = await database.fetch_one(query, {"user_id": user_id})
            return result["name"] if result else None
    
        async def create_access_token(self, user_data: Dict[str, Any]) -> str:
            """Create a new access token with user permissions"""
            user_id = user_data.get("id") or user_data.get("sub")
    
            # Get user permissions and role
            permissions = await self.get_user_permissions(user_id) if user_id else []
            role = await self.get_user_role(user_id) if user_id else "user"
    
            to_encode = {
                "sub": str(user_id),
                "email": user_data.get("email"),
                "full_name": user_data.get("full_name"),
                "role": role,
                "permissions": permissions,
                "exp": datetime.utcnow() + timedelta(minutes=self.access_token_expire_minutes),
                "iat": datetime.utcnow(),
                "type": "access"
            }
    
            encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
            return encoded_jwt
    
        def create_refresh_token(self, data: Dict[str, Any]) -> str:
            """Create a new refresh token"""
            to_encode = data.copy()
            expire = datetime.utcnow() + timedelta(days=self.refresh_token_expire_days)
    
            to_encode.update({
                "exp": expire,
                "iat": datetime.utcnow(),
                "type": "refresh"
            })
    
            encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
            return encoded_jwt
    
        def verify_token(self, token: str) -> Optional[Dict[str, Any]]:
            """Verify and decode a JWT token"""
            try:
                payload = jwt.decode(
                    token, 
                    self.secret_key, 
                    algorithms=[self.algorithm]
                )
                return payload
            except jwt.ExpiredSignatureError:
                return None
            except jwt.JWTError:
                return None
    
        def has_permission(self, token_payload: Dict[str, Any], required_permission: str) -> bool:
            """Check if user has required permission"""
            user_permissions = token_payload.get("permissions", [])
            user_role = token_payload.get("role", "")
    
            # Admin has all permissions
            if user_role == "admin":
                return True
    
            # Check specific permission
            return required_permission in user_permissions
    
        def has_role(self, token_payload: Dict[str, Any], required_role: str) -> bool:
            """Check if user has required role"""
            user_role = token_payload.get("role", "")
            return user_role == required_role
    
        def hash_password(self, password: str) -> str:
            """Hash a password"""
            return self.pwd_context.hash(password)
    
        def verify_password(self, plain_password: str, hashed_password: str) -> bool:
            """Verify a password against its hash"""
            return self.pwd_context.verify(plain_password, hashed_password)
    
        async def refresh_access_token(self, refresh_token: str) -> Optional[str]:
            """Create a new access token from a refresh token"""
            payload = self.verify_token(refresh_token)
    
            if not payload or payload.get("type") != "refresh":
                return None
    
            # Get updated user data
            user_data = {
                "id": payload.get("sub"),
                "email": payload.get("email"),
                "full_name": payload.get("full_name")
            }
    
            return await self.create_access_token(user_data)
    Python

    Permission Decorators and Dependencies

    Create backend/app/auth/permissions.py:

    from functools import wraps
    from typing import List, Union
    from fastapi import HTTPException, status, Depends
    from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
    
    from app.auth.jwt_handler import JWTHandler
    
    security = HTTPBearer()
    jwt_handler = JWTHandler()
    
    class PermissionChecker:
        def __init__(self, required_permissions: Union[str, List[str]], require_all: bool = True):
            """
            Initialize permission checker
    
            Args:
                required_permissions: Single permission or list of permissions
                require_all: If True, user must have ALL permissions. If False, ANY permission is sufficient.
            """
            if isinstance(required_permissions, str):
                self.required_permissions = [required_permissions]
            else:
                self.required_permissions = required_permissions
            self.require_all = require_all
    
        async def __call__(self, credentials: HTTPAuthorizationCredentials = Depends(security)):
            """Check if user has required permissions"""
            token = credentials.credentials
            payload = jwt_handler.verify_token(token)
    
            if not payload:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Invalid token"
                )
    
            # Check permissions
            if self.require_all:
                # User must have ALL required permissions
                for permission in self.required_permissions:
                    if not jwt_handler.has_permission(payload, permission):
                        raise HTTPException(
                            status_code=status.HTTP_403_FORBIDDEN,
                            detail=f"Missing required permission: {permission}"
                        )
            else:
                # User must have ANY of the required permissions
                has_any_permission = any(
                    jwt_handler.has_permission(payload, permission)
                    for permission in self.required_permissions
                )
    
                if not has_any_permission:
                    raise HTTPException(
                        status_code=status.HTTP_403_FORBIDDEN,
                        detail=f"Missing any of required permissions: {', '.join(self.required_permissions)}"
                    )
    
            return payload
    
    class RoleChecker:
        def __init__(self, required_roles: Union[str, List[str]]):
            """
            Initialize role checker
    
            Args:
                required_roles: Single role or list of roles
            """
            if isinstance(required_roles, str):
                self.required_roles = [required_roles]
            else:
                self.required_roles = required_roles
    
        async def __call__(self, credentials: HTTPAuthorizationCredentials = Depends(security)):
            """Check if user has required role"""
            token = credentials.credentials
            payload = jwt_handler.verify_token(token)
    
            if not payload:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Invalid token"
                )
    
            user_role = payload.get("role", "")
    
            if user_role not in self.required_roles:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail=f"Required role: {' or '.join(self.required_roles)}"
                )
    
            return payload
    
    # Convenience functions for common permission checks
    RequireAdmin = RoleChecker("admin")
    RequireManager = RoleChecker(["admin", "manager"])
    
    # Common permission dependencies
    RequireUserRead = PermissionChecker("user:read")
    RequireUserWrite = PermissionChecker(["user:create", "user:update"], require_all=False)
    RequireUserDelete = PermissionChecker("user:delete")
    
    RequireProfileAccess = PermissionChecker(["profile:read", "profile:update"], require_all=False)
    
    RequireReportAccess = PermissionChecker("report:read")
    RequireReportCreate = PermissionChecker("report:create")
    
    RequireSystemConfig = PermissionChecker("system:config")
    RequireSystemMonitor = PermissionChecker("system:monitor")
    Python

    Protected Routes with RBAC

    Update backend/app/routers/users.py:

    from fastapi import APIRouter, Depends, HTTPException, status
    from sqlalchemy import select
    from typing import List
    
    from app.auth.jwt_handler import JWTHandler
    from app.auth.permissions import (
        RequireAdmin, RequireManager, RequireUserRead, RequireUserWrite, 
        RequireUserDelete, RequireProfileAccess, PermissionChecker
    )
    from app.auth.models import UserResponse, UserCreate
    from app.database.database import database, users_table, roles_table
    
    router = APIRouter()
    jwt_handler = JWTHandler()
    
    @router.get("/profile", response_model=UserResponse)
    async def get_user_profile(current_user = Depends(RequireProfileAccess)):
        """Get current user's profile"""
        user_id = current_user.get("sub")
    
        query = select(users_table).where(users_table.c.id == int(user_id))
        user = await database.fetch_one(query)
    
        if not user:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="User not found"
            )
    
        return UserResponse(**user)
    
    @router.get("/all", response_model=List[UserResponse])
    async def get_all_users(current_user = Depends(RequireUserRead)):
        """Get all users (requires user:read permission)"""
        query = """
        SELECT u.*, r.name as role_name 
        FROM users u 
        LEFT JOIN roles r ON u.role_id = r.id
        """
        users = await database.fetch_all(query)
    
        return [UserResponse(**user) for user in users]
    
    @router.post("/create", response_model=UserResponse)
    async def create_user(
        user_data: UserCreate,
        current_user = Depends(PermissionChecker("user:create"))
    ):
        """Create a new user (admin only)"""
    
        # Check if user already exists
        query = select(users_table).where(users_table.c.email == user_data.email)
        existing_user = await database.fetch_one(query)
    
        if existing_user:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Email already registered"
            )
    
        # Hash password
        hashed_password = jwt_handler.hash_password(user_data.password)
    
        # Insert new user
        query = users_table.insert().values(
            email=user_data.email,
            full_name=user_data.full_name,
            hashed_password=hashed_password,
            role_id=1  # Default to user role
        )
    
        user_id = await database.execute(query)
    
        # Fetch and return the created user
        query = select(users_table).where(users_table.c.id == user_id)
        created_user = await database.fetch_one(query)
    
        return UserResponse(**created_user)
    
    @router.delete("/{user_id}")
    async def delete_user(
        user_id: int,
        current_user = Depends(RequireUserDelete)
    ):
        """Delete a user (admin only)"""
    
        # Prevent admin from deleting themselves
        current_user_id = int(current_user.get("sub"))
        if current_user_id == user_id:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Cannot delete your own account"
            )
    
        # Check if user exists
        query = select(users_table).where(users_table.c.id == user_id)
        user = await database.fetch_one(query)
    
        if not user:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="User not found"
            )
    
        # Delete user (or mark as inactive)
        delete_query = users_table.update().where(
            users_table.c.id == user_id
        ).values(is_active=False)
    
        await database.execute(delete_query)
    
        return {"message": "User deleted successfully"}
    
    @router.put("/{user_id}/role")
    async def update_user_role(
        user_id: int,
        role_name: str,
        current_user = Depends(RequireAdmin)
    ):
        """Update user role (admin only)"""
    
        # Get role ID
        role_query = select(roles_table).where(roles_table.c.name == role_name)
        role = await database.fetch_one(role_query)
    
        if not role:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Role not found"
            )
    
        # Update user role
        update_query = users_table.update().where(
            users_table.c.id == user_id
        ).values(role_id=role.id)
    
        result = await database.execute(update_query)
    
        if result == 0:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="User not found"
            )
    
        return {"message": f"User role updated to {role_name}"}
    
    @router.get("/admin/stats")
    async def get_admin_stats(current_user = Depends(RequireAdmin)):
        """Get admin statistics"""
    
        # Get user count by role
        stats_query = """
        SELECT r.name as role, COUNT(u.id) as count
        FROM roles r
        LEFT JOIN users u ON r.id = u.role_id AND u.is_active = true
        GROUP BY r.id, r.name
        ORDER BY r.name
        """
    
        role_stats = await database.fetch_all(stats_query)
    
        # Get total active users
        total_users = await database.fetch_val(
            "SELECT COUNT(*) FROM users WHERE is_active = true"
        )
    
        return {
            "total_users": total_users,
            "role_distribution": [
                {"role": stat["role"], "count": stat["count"]}
                for stat in role_stats
            ]
        }
    
    @router.get("/manager/team")
    async def get_team_overview(current_user = Depends(RequireManager)):
        """Get team overview (manager and admin only)"""
    
        team_query = """
        SELECT u.id, u.full_name, u.email, r.name as role, u.created_at
        FROM users u
        JOIN roles r ON u.role_id = r.id
        WHERE u.is_active = true
        ORDER BY u.created_at DESC
        """
    
        team_members = await database.fetch_all(team_query)
    
        return {
            "team_members": [
                {
                    "id": member["id"],
                    "name": member["full_name"],
                    "email": member["email"],
                    "role": member["role"],
                    "joined": member["created_at"].isoformat()
                }
                for member in team_members
            ]
        }
    Python

    Frontend Permission Handling

    Create frontend/js/permissions.js:

    /**
     * Permission Management for Frontend
     */
    class PermissionManager {
        constructor(authManager) {
            this.auth = authManager;
            this.userPermissions = [];
            this.userRole = '';
    
            // Listen for auth state changes
            this.auth.onAuthStateChange((authenticated, user) => {
                if (authenticated) {
                    this.updatePermissions();
                } else {
                    this.clearPermissions();
                }
            });
        }
    
        /**
         * Update permissions from current token
         */
        updatePermissions() {
            const tokenInfo = this.auth.getTokenInfo();
            if (tokenInfo && tokenInfo.payload) {
                this.userPermissions = tokenInfo.payload.permissions || [];
                this.userRole = tokenInfo.payload.role || '';
            }
        }
    
        /**
         * Clear permissions
         */
        clearPermissions() {
            this.userPermissions = [];
            this.userRole = '';
        }
    
        /**
         * Check if user has specific permission
         */
        hasPermission(permission) {
            // Admin has all permissions
            if (this.userRole === 'admin') {
                return true;
            }
    
            return this.userPermissions.includes(permission);
        }
    
        /**
         * Check if user has any of the specified permissions
         */
        hasAnyPermission(permissions) {
            if (this.userRole === 'admin') {
                return true;
            }
    
            return permissions.some(permission => this.userPermissions.includes(permission));
        }
    
        /**
         * Check if user has all of the specified permissions
         */
        hasAllPermissions(permissions) {
            if (this.userRole === 'admin') {
                return true;
            }
    
            return permissions.every(permission => this.userPermissions.includes(permission));
        }
    
        /**
         * Check if user has specific role
         */
        hasRole(role) {
            return this.userRole === role;
        }
    
        /**
         * Check if user has any of the specified roles
         */
        hasAnyRole(roles) {
            return roles.includes(this.userRole);
        }
    
        /**
         * Get user's current role
         */
        getRole() {
            return this.userRole;
        }
    
        /**
         * Get user's permissions
         */
        getPermissions() {
            return [...this.userPermissions];
        }
    
        /**
         * Show/hide elements based on permissions
         */
        applyPermissionVisibility() {
            // Handle data-permission attributes
            document.querySelectorAll('[data-permission]').forEach(element => {
                const requiredPermission = element.dataset.permission;
                if (this.hasPermission(requiredPermission)) {
                    element.style.display = '';
                    element.removeAttribute('disabled');
                } else {
                    element.style.display = 'none';
                    element.setAttribute('disabled', 'true');
                }
            });
    
            // Handle data-role attributes
            document.querySelectorAll('[data-role]').forEach(element => {
                const requiredRoles = element.dataset.role.split(',').map(r => r.trim());
                if (this.hasAnyRole(requiredRoles)) {
                    element.style.display = '';
                    element.removeAttribute('disabled');
                } else {
                    element.style.display = 'none';
                    element.setAttribute('disabled', 'true');
                }
            });
    
            // Handle data-any-permission attributes
            document.querySelectorAll('[data-any-permission]').forEach(element => {
                const requiredPermissions = element.dataset.anyPermission.split(',').map(p => p.trim());
                if (this.hasAnyPermission(requiredPermissions)) {
                    element.style.display = '';
                    element.removeAttribute('disabled');
                } else {
                    element.style.display = 'none';
                    element.setAttribute('disabled', 'true');
                }
            });
    
            // Handle data-all-permissions attributes
            document.querySelectorAll('[data-all-permissions]').forEach(element => {
                const requiredPermissions = element.dataset.allPermissions.split(',').map(p => p.trim());
                if (this.hasAllPermissions(requiredPermissions)) {
                    element.style.display = '';
                    element.removeAttribute('disabled');
                } else {
                    element.style.display = 'none';
                    element.setAttribute('disabled', 'true');
                }
            });
        }
    
        /**
         * Create permission-aware button
         */
        createPermissionButton(text, permission, clickHandler, className = '') {
            const button = document.createElement('button');
            button.textContent = text;
            button.className = className;
    
            if (this.hasPermission(permission)) {
                button.addEventListener('click', clickHandler);
            } else {
                button.disabled = true;
                button.title = `Requires ${permission} permission`;
            }
    
            return button;
        }
    
        /**
         * Create role-aware navigation
         */
        createRoleNavigation() {
            const navItems = [];
    
            // Basic navigation for all authenticated users
            navItems.push({
                text: 'Profile',
                permission: 'profile:read',
                action: () => app.showProfile()
            });
    
            // Manager navigation
            if (this.hasAnyRole(['manager', 'admin'])) {
                navItems.push({
                    text: 'Team',
                    permission: 'user:read',
                    action: () => app.showTeamManagement()
                });
    
                navItems.push({
                    text: 'Reports',
                    permission: 'report:read',
                    action: () => app.showReports()
                });
            }
    
            // Admin navigation
            if (this.hasRole('admin')) {
                navItems.push({
                    text: 'User Management',
                    permission: 'user:create',
                    action: () => app.showUserManagement()
                });
    
                navItems.push({
                    text: 'System Config',
                    permission: 'system:config',
                    action: () => app.showSystemConfig()
                });
            }
    
            return navItems;
        }
    }
    
    // Create global permission manager
    const permissions = new PermissionManager(auth);
    JavaScript

    Updated HTML with Permission Attributes

    Update frontend/index.html to include permission-based elements:

    <!-- Add to dashboard section -->
    <div id="dashboard-section" class="dashboard" style="display: none;">
        <h2>Dashboard</h2>
        <div id="user-info"></div>
    
        <!-- Role-based navigation -->
        <div class="role-navigation">
            <button data-permission="profile:read" onclick="showUserProfile()">My Profile</button>
            <button data-any-role="manager,admin" onclick="showTeamManagement()">Team Management</button>
            <button data-permission="report:read" onclick="showReports()">Reports</button>
            <button data-role="admin" onclick="showUserManagement()">User Management</button>
            <button data-permission="system:config" onclick="showSystemConfig()">System Settings</button>
        </div>
    
        <!-- Permission-specific content -->
        <div id="user-management" data-permission="user:create" style="display: none;">
            <h3>User Management</h3>
            <button data-permission="user:create" onclick="createNewUser()">Create User</button>
            <div id="user-list"></div>
        </div>
    
        <div id="team-management" data-any-role="manager,admin" style="display: none;">
            <h3>Team Overview</h3>
            <div id="team-list"></div>
        </div>
    
        <div id="protected-content"></div>
    </div>
    HTML

    This comprehensive RBAC implementation provides:

    1. Database Schema: Roles, permissions, and role-permission mappings
    2. JWT Enhancement: Include permissions and roles in JWT payload
    3. Backend Protection: Route-level permission and role checking
    4. Frontend Integration: Permission-aware UI components
    5. Flexible Authorization: Support for both role-based and permission-based access control
    6. Scalable Design: Easy to add new roles and permissions

    The system allows for fine-grained access control while maintaining simplicity for common use cases.


    10. Error Handling and Validation

    Robust error handling and input validation are essential for secure JWT implementations. This chapter covers comprehensive error handling strategies and security validation.

    JWT Error Types and Handling

    graph TB
        A[JWT Error Types] --> B[Token Errors]
        A --> C[Validation Errors]
        A --> D[Security Errors]
    
        B --> E[Expired Token]
        B --> F[Invalid Signature]
        B --> G[Malformed Token]
    
        C --> H[Missing Claims]
        C --> I[Invalid Claims]
        C --> J[Audience Mismatch]
    
        D --> K[Algorithm Confusion]
        D --> L[Key Compromise]
        D --> M[Replay Attacks]

    Backend Error Handling

    Create backend/app/auth/exceptions.py:

    from fastapi import HTTPException, status
    from typing import Optional, Dict, Any
    
    class JWTError(Exception):
        """Base JWT exception"""
        pass
    
    class TokenExpiredError(JWTError):
        """Token has expired"""
        pass
    
    class InvalidTokenError(JWTError):
        """Token is invalid"""
        pass
    
    class InvalidSignatureError(JWTError):
        """Token signature is invalid"""
        pass
    
    class MissingClaimError(JWTError):
        """Required claim is missing"""
        pass
    
    class InvalidClaimError(JWTError):
        """Claim value is invalid"""
        pass
    
    class InsufficientPermissionsError(JWTError):
        """User lacks required permissions"""
        pass
    
    class SecurityError(JWTError):
        """Security-related error"""
        pass
    
    def create_http_exception(
        error_type: str,
        message: str,
        status_code: int = status.HTTP_401_UNAUTHORIZED,
        headers: Optional[Dict[str, str]] = None
    ) -> HTTPException:
        """Create standardized HTTP exception"""
    
        detail = {
            "error": error_type,
            "message": message,
            "code": status_code
        }
    
        return HTTPException(
            status_code=status_code,
            detail=detail,
            headers=headers or {}
        )
    
    # Predefined HTTP exceptions
    class AuthenticationExceptions:
        INVALID_TOKEN = create_http_exception(
            "INVALID_TOKEN",
            "The provided token is invalid or malformed",
            status.HTTP_401_UNAUTHORIZED,
            {"WWW-Authenticate": "Bearer"}
        )
    
        EXPIRED_TOKEN = create_http_exception(
            "EXPIRED_TOKEN",
            "The token has expired",
            status.HTTP_401_UNAUTHORIZED,
            {"WWW-Authenticate": "Bearer"}
        )
    
        MISSING_TOKEN = create_http_exception(
            "MISSING_TOKEN",
            "Authentication token is required",
            status.HTTP_401_UNAUTHORIZED,
            {"WWW-Authenticate": "Bearer"}
        )
    
        INSUFFICIENT_PERMISSIONS = create_http_exception(
            "INSUFFICIENT_PERMISSIONS",
            "Insufficient permissions to access this resource",
            status.HTTP_403_FORBIDDEN
        )
    
        INVALID_CREDENTIALS = create_http_exception(
            "INVALID_CREDENTIALS",
            "Invalid username or password",
            status.HTTP_401_UNAUTHORIZED
        )
    
        ACCOUNT_DISABLED = create_http_exception(
            "ACCOUNT_DISABLED",
            "User account is disabled",
            status.HTTP_401_UNAUTHORIZED
        )
    
        RATE_LIMITED = create_http_exception(
            "RATE_LIMITED",
            "Too many requests. Please try again later.",
            status.HTTP_429_TOO_MANY_REQUESTS
        )
    Python

    Enhanced JWT Handler with Validation

    Update backend/app/auth/jwt_handler.py to include comprehensive validation:

    from datetime import datetime, timedelta
    from typing import Optional, Dict, Any, List
    import jwt
    from decouple import config
    from passlib.context import CryptContext
    from sqlalchemy import select
    import re
    import ipaddress
    import hashlib
    
    from app.database.database import database, roles_table, permissions_table, role_permissions_table
    from app.auth.exceptions import (
        TokenExpiredError, InvalidTokenError, InvalidSignatureError,
        MissingClaimError, InvalidClaimError, SecurityError
    )
    
    class EnhancedJWTHandler:
        def __init__(self):
            self.secret_key = config("SECRET_KEY")
            self.algorithm = config("ALGORITHM", default="HS256")
            self.access_token_expire_minutes = int(config("ACCESS_TOKEN_EXPIRE_MINUTES", default=30))
            self.refresh_token_expire_days = int(config("REFRESH_TOKEN_EXPIRE_DAYS", default=7))
            self.issuer = config("JWT_ISSUER", default="jwt-auth-api")
            self.audience = config("JWT_AUDIENCE", default="jwt-auth-client")
    
            # Security settings
            self.max_token_age_days = int(config("MAX_TOKEN_AGE_DAYS", default=30))
            self.require_issued_at = config("REQUIRE_ISSUED_AT", default=True, cast=bool)
            self.clock_skew_seconds = int(config("CLOCK_SKEW_SECONDS", default=60))
    
            # Password hashing
            self.pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
    
            # Token blacklist (in production, use Redis or database)
            self.token_blacklist = set()
    
        def validate_token_structure(self, token: str) -> bool:
            """Validate JWT token structure"""
            if not token:
                return False
    
            # Check basic structure
            parts = token.split('.')
            if len(parts) != 3:
                return False
    
            # Validate each part is valid base64
            try:
                for part in parts:
                    # Add padding if needed
                    padding = 4 - len(part) % 4
                    if padding != 4:
                        part += '=' * padding
                    jwt.utils.base64url_decode(part)
            except Exception:
                return False
    
            return True
    
        def validate_algorithm(self, token: str) -> bool:
            """Validate token uses allowed algorithm"""
            try:
                header = jwt.get_unverified_header(token)
                algorithm = header.get('alg')
    
                # Never allow 'none' algorithm
                if algorithm == 'none':
                    raise SecurityError("Algorithm 'none' is not allowed")
    
                # Check against allowed algorithms
                allowed_algorithms = ['HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512']
                if algorithm not in allowed_algorithms:
                    raise SecurityError(f"Algorithm '{algorithm}' is not allowed")
    
                return True
            except Exception as e:
                raise SecurityError(f"Invalid token header: {str(e)}")
    
        def validate_claims(self, payload: Dict[str, Any], client_ip: Optional[str] = None) -> bool:
            """Validate JWT claims"""
            current_time = datetime.utcnow()
    
            # Validate expiration (exp)
            if 'exp' not in payload:
                raise MissingClaimError("Missing 'exp' claim")
    
            exp_time = datetime.fromtimestamp(payload['exp'])
            if current_time > exp_time:
                raise TokenExpiredError("Token has expired")
    
            # Validate issued at (iat)
            if self.require_issued_at:
                if 'iat' not in payload:
                    raise MissingClaimError("Missing 'iat' claim")
    
                iat_time = datetime.fromtimestamp(payload['iat'])
                if iat_time > current_time + timedelta(seconds=self.clock_skew_seconds):
                    raise InvalidClaimError("Token issued in the future")
    
                # Check maximum token age
                max_age = timedelta(days=self.max_token_age_days)
                if current_time - iat_time > max_age:
                    raise InvalidClaimError("Token is too old")
    
            # Validate not before (nbf)
            if 'nbf' in payload:
                nbf_time = datetime.fromtimestamp(payload['nbf'])
                if current_time < nbf_time - timedelta(seconds=self.clock_skew_seconds):
                    raise InvalidClaimError("Token not yet valid")
    
            # Validate issuer (iss)
            if 'iss' in payload:
                if payload['iss'] != self.issuer:
                    raise InvalidClaimError(f"Invalid issuer: {payload['iss']}")
    
            # Validate audience (aud)
            if 'aud' in payload:
                audience = payload['aud']
                if isinstance(audience, list):
                    if self.audience not in audience:
                        raise InvalidClaimError("Invalid audience")
                else:
                    if audience != self.audience:
                        raise InvalidClaimError("Invalid audience")
    
            # Validate subject (sub)
            if 'sub' not in payload:
                raise MissingClaimError("Missing 'sub' claim")
    
            # Validate subject format (should be user ID)
            try:
                int(payload['sub'])
            except ValueError:
                raise InvalidClaimError("Invalid subject format")
    
            # Validate JWT ID (jti) if present
            if 'jti' in payload:
                jti = payload['jti']
                if jti in self.token_blacklist:
                    raise SecurityError("Token has been revoked")
    
            # Validate IP binding if enabled
            if 'ip' in payload and client_ip:
                token_ip = payload['ip']
                if not self.validate_ip_binding(token_ip, client_ip):
                    raise SecurityError("IP address mismatch")
    
            return True
    
        def validate_ip_binding(self, token_ip: str, client_ip: str) -> bool:
            """Validate IP address binding"""
            try:
                token_addr = ipaddress.ip_address(token_ip)
                client_addr = ipaddress.ip_address(client_ip)
    
                # For IPv4, allow same /24 subnet
                if token_addr.version == 4 and client_addr.version == 4:
                    token_network = ipaddress.IPv4Network(f"{token_ip}/24", strict=False)
                    return client_addr in token_network
    
                # For IPv6, exact match required
                return token_addr == client_addr
    
            except ValueError:
                return False
    
        def validate_user_session(self, payload: Dict[str, Any]) -> bool:
            """Validate user session is still active"""
            user_id = payload.get('sub')
            if not user_id:
                return False
    
            # Check if user is still active in database
            # This would typically involve checking a sessions table
            # For this example, we'll just check if user exists and is active
            return True
    
        def create_secure_token(
            self,
            user_data: Dict[str, Any],
            client_ip: Optional[str] = None,
            bind_ip: bool = False
        ) -> str:
            """Create a secure JWT token with comprehensive validation"""
    
            user_id = user_data.get("id") or user_data.get("sub")
            current_time = datetime.utcnow()
    
            # Get user permissions and role
            permissions = await self.get_user_permissions(user_id) if user_id else []
            role = await self.get_user_role(user_id) if user_id else "user"
    
            # Generate unique token ID
            jti = self.generate_jti(user_id, current_time)
    
            payload = {
                "sub": str(user_id),
                "email": user_data.get("email"),
                "full_name": user_data.get("full_name"),
                "role": role,
                "permissions": permissions,
                "iss": self.issuer,
                "aud": self.audience,
                "exp": current_time + timedelta(minutes=self.access_token_expire_minutes),
                "iat": current_time,
                "jti": jti,
                "type": "access"
            }
    
            # Add IP binding if requested
            if bind_ip and client_ip:
                payload["ip"] = client_ip
    
            # Add custom security claims
            payload["sec"] = {
                "v": "1.0",  # Security version
                "alg_verified": True,
                "claims_verified": True
            }
    
            try:
                encoded_jwt = jwt.encode(payload, self.secret_key, algorithm=self.algorithm)
                return encoded_jwt
            except Exception as e:
                raise SecurityError(f"Failed to create token: {str(e)}")
    
        def verify_secure_token(
            self,
            token: str,
            client_ip: Optional[str] = None,
            verify_session: bool = True
        ) -> Optional[Dict[str, Any]]:
            """Verify JWT token with comprehensive security checks"""
    
            if not token:
                raise InvalidTokenError("Token is required")
    
            # Step 1: Validate token structure
            if not self.validate_token_structure(token):
                raise InvalidTokenError("Invalid token structure")
    
            # Step 2: Validate algorithm
            self.validate_algorithm(token)
    
            # Step 3: Verify signature and decode
            try:
                payload = jwt.decode(
                    token,
                    self.secret_key,
                    algorithms=[self.algorithm],
                    options={
                        "verify_signature": True,
                        "verify_exp": False,  # We'll do custom validation
                        "verify_nbf": False,
                        "verify_iat": False,
                        "verify_aud": False,
                        "verify_iss": False,
                    }
                )
            except jwt.ExpiredSignatureError:
                raise TokenExpiredError("Token has expired")
            except jwt.InvalidSignatureError:
                raise InvalidSignatureError("Invalid token signature")
            except jwt.DecodeError:
                raise InvalidTokenError("Token decode error")
            except Exception as e:
                raise InvalidTokenError(f"Token verification failed: {str(e)}")
    
            # Step 4: Validate claims
            self.validate_claims(payload, client_ip)
    
            # Step 5: Validate user session
            if verify_session and not self.validate_user_session(payload):
                raise SecurityError("User session is no longer valid")
    
            return payload
    
        def generate_jti(self, user_id: Any, timestamp: datetime) -> str:
            """Generate unique JWT ID"""
            data = f"{user_id}:{timestamp.isoformat()}:{self.secret_key}"
            return hashlib.sha256(data.encode()).hexdigest()[:16]
    
        def revoke_token(self, token: str) -> bool:
            """Add token to blacklist"""
            try:
                payload = jwt.decode(
                    token,
                    self.secret_key,
                    algorithms=[self.algorithm],
                    options={"verify_exp": False}
                )
    
                jti = payload.get('jti')
                if jti:
                    self.token_blacklist.add(jti)
                    return True
            except Exception:
                pass
    
            return False
    
        def is_token_revoked(self, token: str) -> bool:
            """Check if token is revoked"""
            try:
                payload = jwt.decode(
                    token,
                    self.secret_key,
                    algorithms=[self.algorithm],
                    options={"verify_exp": False}
                )
    
                jti = payload.get('jti')
                return jti in self.token_blacklist if jti else False
            except Exception:
                return True
    
        # ... (include other methods from previous JWTHandler implementation)
    Python

    Input Validation

    Create backend/app/auth/validators.py:

    import re
    from typing import Optional, Dict, Any
    from pydantic import BaseModel, validator, EmailStr
    from fastapi import HTTPException, status
    
    class StrictUserCreate(BaseModel):
        email: EmailStr
        password: str
        full_name: str
    
        @validator('password')
        def validate_password(cls, v):
            """Validate password strength"""
            if len(v) < 8:
                raise ValueError('Password must be at least 8 characters long')
    
            if len(v) > 128:
                raise ValueError('Password must be less than 128 characters')
    
            # Check for required character types
            if not re.search(r'[A-Z]', v):
                raise ValueError('Password must contain at least one uppercase letter')
    
            if not re.search(r'[a-z]', v):
                raise ValueError('Password must contain at least one lowercase letter')
    
            if not re.search(r'\d', v):
                raise ValueError('Password must contain at least one digit')
    
            if not re.search(r'[!@#$%^&*(),.?":{}|<>]', v):
                raise ValueError('Password must contain at least one special character')
    
            # Check for common weak passwords
            weak_passwords = [
                'password', '12345678', 'qwerty', 'abc123',
                'password123', 'admin', 'user', 'test'
            ]
    
            if v.lower() in weak_passwords:
                raise ValueError('Password is too weak')
    
            return v
    
        @validator('full_name')
        def validate_full_name(cls, v):
            """Validate full name"""
            if len(v.strip()) < 2:
                raise ValueError('Full name must be at least 2 characters')
    
            if len(v) > 100:
                raise ValueError('Full name must be less than 100 characters')
    
            # Allow only letters, spaces, hyphens, and apostrophes
            if not re.match(r"^[a-zA-Z\s\-']+$", v):
                raise ValueError('Full name contains invalid characters')
    
            return v.strip()
    
        @validator('email')
        def validate_email_format(cls, v):
            """Additional email validation"""
            # Check email length
            if len(v) > 254:
                raise ValueError('Email address is too long')
    
            # Check for suspicious patterns
            suspicious_patterns = [
                r'\.{2,}',  # Multiple consecutive dots
                r'^\.|\.$',  # Starting or ending with dot
                r'@.*@',  # Multiple @ symbols
            ]
    
            for pattern in suspicious_patterns:
                if re.search(pattern, v):
                    raise ValueError('Invalid email format')
    
            return v.lower()
    
    class SecureLoginRequest(BaseModel):
        username: EmailStr
        password: str
        remember_me: Optional[bool] = False
        client_info: Optional[Dict[str, Any]] = None
    
        @validator('password')
        def validate_password_not_empty(cls, v):
            if not v or len(v.strip()) == 0:
                raise ValueError('Password cannot be empty')
    
            if len(v) > 128:
                raise ValueError('Password is too long')
    
            return v
    
        @validator('client_info')
        def validate_client_info(cls, v):
            """Validate client information for security logging"""
            if v is None:
                return v
    
            allowed_fields = ['user_agent', 'ip_address', 'browser', 'os', 'device_type']
    
            # Remove any fields not in allowed list
            filtered_info = {k: v[k] for k in v if k in allowed_fields}
    
            # Validate field lengths
            for field, value in filtered_info.items():
                if isinstance(value, str) and len(value) > 500:
                    filtered_info[field] = value[:500]
    
            return filtered_info
    
    class TokenRefreshRequest(BaseModel):
        refresh_token: str
    
        @validator('refresh_token')
        def validate_refresh_token(cls, v):
            if not v or len(v.strip()) == 0:
                raise ValueError('Refresh token cannot be empty')
    
            # Basic JWT structure validation
            parts = v.split('.')
            if len(parts) != 3:
                raise ValueError('Invalid token format')
    
            return v.strip()
    
    def validate_request_rate_limit(client_ip: str, action: str) -> bool:
        """
        Rate limiting validation
        In production, use Redis or similar for distributed rate limiting
        """
        # This is a simplified example
        # You would implement proper rate limiting logic here
        return True
    
    def validate_request_origin(request_headers: Dict[str, str]) -> bool:
        """Validate request origin for CSRF protection"""
        origin = request_headers.get('origin')
        referer = request_headers.get('referer')
    
        if not origin and not referer:
            return False
    
        # Check against allowed origins
        allowed_origins = [
            'http://localhost:3000',
            'http://localhost:8080',
            'https://yourdomain.com'
        ]
    
        if origin and origin not in allowed_origins:
            return False
    
        return True
    
    class SecurityValidator:
        """Comprehensive security validation class"""
    
        @staticmethod
        def validate_jwt_claims(payload: Dict[str, Any]) -> bool:
            """Validate JWT payload structure"""
            required_claims = ['sub', 'exp', 'iat', 'type']
    
            for claim in required_claims:
                if claim not in payload:
                    raise ValueError(f"Missing required claim: {claim}")
    
            # Validate claim types
            if not isinstance(payload['sub'], str):
                raise ValueError("Subject must be a string")
    
            if not isinstance(payload['exp'], (int, float)):
                raise ValueError("Expiration must be a number")
    
            if not isinstance(payload['iat'], (int, float)):
                raise ValueError("Issued at must be a number")
    
            return True
    
        @staticmethod
        def validate_user_input(data: Dict[str, Any], max_length: int = 1000) -> Dict[str, Any]:
            """Sanitize and validate user input"""
            cleaned_data = {}
    
            for key, value in data.items():
                if isinstance(value, str):
                    # Remove potentially dangerous characters
                    value = re.sub(r'[<>"\']', '', value)
    
                    # Limit length
                    if len(value) > max_length:
                        value = value[:max_length]
    
                    # Strip whitespace
                    value = value.strip()
    
                cleaned_data[key] = value
    
            return cleaned_data
    
        @staticmethod
        def validate_file_upload(file_data: bytes, allowed_types: list = None) -> bool:
            """Validate file uploads"""
            if not file_data:
                return False
    
            # Check file size (10MB limit)
            if len(file_data) > 10 * 1024 * 1024:
                return False
    
            # Check file signatures if types specified
            if allowed_types:
                # This is a simplified example
                # In production, use python-magic or similar for proper MIME detection
                return True
    
            return True
    Python

    Frontend Error Handling

    Create frontend/js/errorHandler.js:

    /**
     * Comprehensive Error Handling for JWT Authentication
     */
    class ErrorHandler {
        constructor() {
            this.errorLog = [];
            this.maxLogSize = 100;
            this.setupGlobalErrorHandling();
        }
    
        /**
         * Setup global error handling
         */
        setupGlobalErrorHandling() {
            // Handle unhandled promise rejections
            window.addEventListener('unhandledrejection', (event) => {
                this.logError('Unhandled Promise Rejection', event.reason);
                event.preventDefault();
            });
    
            // Handle global JavaScript errors
            window.addEventListener('error', (event) => {
                this.logError('JavaScript Error', {
                    message: event.message,
                    filename: event.filename,
                    lineno: event.lineno,
                    colno: event.colno,
                    error: event.error
                });
            });
        }
    
        /**
         * Handle API errors with specific JWT error types
         */
        handleApiError(error, context = '') {
            let errorType = 'UNKNOWN_ERROR';
            let message = 'An unexpected error occurred';
            let shouldRetry = false;
            let shouldLogout = false;
    
            if (error.response) {
                // Server responded with error status
                const status = error.response.status;
                const data = error.response.data;
    
                switch (status) {
                    case 401:
                        errorType = data?.error || 'AUTHENTICATION_ERROR';
                        shouldLogout = true;
    
                        if (errorType === 'EXPIRED_TOKEN') {
                            message = 'Your session has expired. Please log in again.';
                            shouldRetry = true;
                        } else if (errorType === 'INVALID_TOKEN') {
                            message = 'Invalid authentication token. Please log in again.';
                        } else {
                            message = 'Authentication failed. Please check your credentials.';
                        }
                        break;
    
                    case 403:
                        errorType = 'INSUFFICIENT_PERMISSIONS';
                        message = 'You do not have permission to access this resource.';
                        break;
    
                    case 422:
                        errorType = 'VALIDATION_ERROR';
                        message = this.formatValidationErrors(data?.detail || []);
                        break;
    
                    case 429:
                        errorType = 'RATE_LIMITED';
                        message = 'Too many requests. Please try again later.';
                        shouldRetry = true;
                        break;
    
                    case 500:
                        errorType = 'SERVER_ERROR';
                        message = 'Server error occurred. Please try again later.';
                        shouldRetry = true;
                        break;
    
                    default:
                        message = data?.message || `HTTP ${status} error occurred`;
                }
            } else if (error.request) {
                // Network error
                errorType = 'NETWORK_ERROR';
                message = 'Network error. Please check your connection and try again.';
                shouldRetry = true;
            } else {
                // Client-side error
                errorType = 'CLIENT_ERROR';
                message = error.message || 'An unexpected error occurred';
            }
    
            const errorInfo = {
                type: errorType,
                message: message,
                context: context,
                timestamp: new Date().toISOString(),
                shouldRetry: shouldRetry,
                shouldLogout: shouldLogout,
                originalError: error
            };
    
            this.logError(errorType, errorInfo);
    
            return errorInfo;
        }
    
        /**
         * Format validation errors from FastAPI
         */
        formatValidationErrors(details) {
            if (!Array.isArray(details)) {
                return 'Validation error occurred';
            }
    
            const errors = details.map(detail => {
                const field = detail.loc ? detail.loc.join('.') : 'unknown';
                return `${field}: ${detail.msg}`;
            });
    
            return errors.join(', ');
        }
    
        /**
         * Handle JWT specific errors
         */
        handleJWTError(error) {
            const jwtErrors = {
                'TOKEN_EXPIRED': {
                    message: 'Your session has expired',
                    action: 'refresh_token',
                    severity: 'warning'
                },
                'INVALID_TOKEN': {
                    message: 'Invalid authentication token',
                    action: 'logout',
                    severity: 'error'
                },
                'MISSING_TOKEN': {
                    message: 'Authentication required',
                    action: 'login',
                    severity: 'info'
                },
                'INSUFFICIENT_PERMISSIONS': {
                    message: 'Access denied - insufficient permissions',
                    action: 'show_error',
                    severity: 'error'
                },
                'INVALID_SIGNATURE': {
                    message: 'Token signature verification failed',
                    action: 'logout',
                    severity: 'error'
                },
                'ALGORITHM_CONFUSION': {
                    message: 'Security error - please contact support',
                    action: 'logout',
                    severity: 'critical'
                }
            };
    
            const errorInfo = jwtErrors[error.type] || {
                message: 'Authentication error occurred',
                action: 'logout',
                severity: 'error'
            };
    
            this.showUserError(errorInfo.message, errorInfo.severity);
    
            switch (errorInfo.action) {
                case 'refresh_token':
                    return this.attemptTokenRefresh();
                case 'logout':
                    return auth.logout();
                case 'login':
                    return app.showLogin();
                case 'show_error':
                    return Promise.resolve();
            }
        }
    
        /**
         * Attempt automatic token refresh
         */
        async attemptTokenRefresh() {
            try {
                await auth.api.refreshToken();
                this.showUserError('Session refreshed successfully', 'success');
                return true;
            } catch (error) {
                this.showUserError('Failed to refresh session. Please log in again.', 'error');
                await auth.logout();
                return false;
            }
        }
    
        /**
         * Show error to user
         */
        showUserError(message, severity = 'error') {
            const statusElement = document.getElementById('status');
            if (statusElement) {
                statusElement.textContent = message;
                statusElement.className = `status-${severity}`;
    
                // Auto-clear after appropriate time based on severity
                const clearTime = severity === 'critical' ? 10000 : 5000;
                setTimeout(() => {
                    statusElement.textContent = '';
                    statusElement.className = '';
                }, clearTime);
            }
    
            // Also log to console for debugging
            console[severity === 'critical' ? 'error' : severity](message);
        }
    
        /**
         * Log error for debugging and monitoring
         */
        logError(type, details) {
            const logEntry = {
                type: type,
                details: details,
                timestamp: new Date().toISOString(),
                userAgent: navigator.userAgent,
                url: window.location.href
            };
    
            this.errorLog.push(logEntry);
    
            // Keep log size manageable
            if (this.errorLog.length > this.maxLogSize) {
                this.errorLog.shift();
            }
    
            // In production, send critical errors to monitoring service
            if (type === 'CRITICAL_ERROR') {
                this.sendToMonitoring(logEntry);
            }
    
            console.error('Error logged:', logEntry);
        }
    
        /**
         * Send error to monitoring service
         */
        async sendToMonitoring(logEntry) {
            try {
                // Replace with your monitoring service endpoint
                await fetch('/api/monitoring/errors', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify(logEntry)
                });
            } catch (error) {
                console.error('Failed to send error to monitoring service:', error);
            }
        }
    
        /**
         * Validate input on client side
         */
        validateInput(value, type = 'text', options = {}) {
            const errors = [];
    
            if (options.required && (!value || value.toString().trim().length === 0)) {
                errors.push('This field is required');
                return errors;
            }
    
            if (!value) return errors;
    
            switch (type) {
                case 'email':
                    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
                    if (!emailRegex.test(value)) {
                        errors.push('Invalid email format');
                    }
                    break;
    
                case 'password':
                    if (value.length < 8) {
                        errors.push('Password must be at least 8 characters long');
                    }
                    if (!/[A-Z]/.test(value)) {
                        errors.push('Password must contain at least one uppercase letter');
                    }
                    if (!/[a-z]/.test(value)) {
                        errors.push('Password must contain at least one lowercase letter');
                    }
                    if (!/\d/.test(value)) {
                        errors.push('Password must contain at least one number');
                    }
                    if (!/[!@#$%^&*(),.?":{}|<>]/.test(value)) {
                        errors.push('Password must contain at least one special character');
                    }
                    break;
    
                case 'name':
                    if (value.length < 2) {
                        errors.push('Name must be at least 2 characters long');
                    }
                    if (!/^[a-zA-Z\s\-']+$/.test(value)) {
                        errors.push('Name contains invalid characters');
                    }
                    break;
    
                case 'text':
                    if (options.maxLength && value.length > options.maxLength) {
                        errors.push(`Text must be less than ${options.maxLength} characters`);
                    }
                    break;
            }
    
            return errors;
        }
    
        /**
         * Get error log for debugging
         */
        getErrorLog() {
            return [...this.errorLog];
        }
    
        /**
         * Clear error log
         */
        clearErrorLog() {
            this.errorLog = [];
        }
    
        /**
         * Export error log for support
         */
        exportErrorLog() {
            const logData = {
                errors: this.errorLog,
                timestamp: new Date().toISOString(),
                userAgent: navigator.userAgent,
                url: window.location.href
            };
    
            const blob = new Blob([JSON.stringify(logData, null, 2)], {
                type: 'application/json'
            });
    
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = `error-log-${Date.now()}.json`;
            a.click();
            URL.revokeObjectURL(url);
        }
    }
    
    // Create global error handler instance
    const errorHandler = new ErrorHandler();
    
    // Enhance API client with error handling
    if (typeof api !== 'undefined') {
        // Wrap API methods to include error handling
        const originalRequest = api.request.bind(api);
    
        api.request = async function(endpoint, options = {}) {
            try {
                return await originalRequest(endpoint, options);
            } catch (error) {
                const errorInfo = errorHandler.handleApiError(error, endpoint);
    
                // Handle JWT specific errors
                if (errorInfo.type.includes('TOKEN') || errorInfo.type === 'INSUFFICIENT_PERMISSIONS') {
                    await errorHandler.handleJWTError(errorInfo);
                }
    
                throw errorInfo;
            }
        };
    }
    JavaScript

    This comprehensive error handling and validation implementation provides:

    1. Backend Validation: Strict input validation, JWT claim validation, and security checks
    2. Structured Error Handling: Consistent error types and responses
    3. Frontend Error Management: User-friendly error messages and automatic recovery
    4. Security Validation: Protection against common attacks and vulnerabilities
    5. Monitoring Integration: Error logging and reporting capabilities
    6. Client-side Validation: Real-time input validation for better UX

    The system ensures robust security while providing clear feedback to users and developers for debugging and monitoring purposes.


    11. JWE (JSON Web Encryption)

    JSON Web Encryption (JWE) provides confidentiality by encrypting the JWT payload, ensuring that sensitive information cannot be read even if the token is intercepted.

    JWE vs JWS Comparison

    graph TB
        A[JWT Security Options] --> B[JWS - Signed Only]
        A --> C[JWE - Encrypted]
        A --> D[JWS + JWE - Signed & Encrypted]
    
        B --> E[Integrity ✓Authenticity ✓Confidentiality ✗]
        C --> F[Integrity ✓Authenticity ✗Confidentiality ✓]
        D --> G[Integrity ✓Authenticity ✓Confidentiality ✓]

    JWE Structure

    JWE tokens have 5 parts separated by dots:

    header.encrypted_key.initialization_vector.ciphertext.authentication_tag
    JSON

    JWE Implementation with FastAPI

    Create backend/app/auth/jwe_handler.py:

    from cryptography.hazmat.primitives.ciphers.aead import AESGCM
    from cryptography.hazmat.primitives import hashes, serialization
    from cryptography.hazmat.primitives.asymmetric import rsa, padding
    from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
    import os
    import json
    import base64
    from typing import Dict, Any, Optional
    from datetime import datetime, timedelta
    
    class JWEHandler:
        def __init__(self):
            self.content_encryption_algorithm = "A256GCM"
            self.key_encryption_algorithm = "RSA-OAEP"
    
            # Generate or load RSA key pair for key encryption
            self.private_key, self.public_key = self.generate_rsa_keypair()
    
        def generate_rsa_keypair(self):
            """Generate RSA key pair for JWE"""
            private_key = rsa.generate_private_key(
                public_exponent=65537,
                key_size=2048,
            )
            public_key = private_key.public_key()
            return private_key, public_key
    
        def create_jwe_token(self, payload: Dict[str, Any], recipient_public_key: Optional[rsa.RSAPublicKey] = None) -> str:
            """Create JWE token with encrypted payload"""
    
            # Use provided public key or default
            pub_key = recipient_public_key or self.public_key
    
            # Step 1: Create JWE Header
            header = {
                "alg": self.key_encryption_algorithm,
                "enc": self.content_encryption_algorithm,
                "typ": "JWT"
            }
    
            # Step 2: Generate Content Encryption Key (CEK)
            cek = AESGCM.generate_key(bit_length=256)
    
            # Step 3: Encrypt CEK with recipient's public key
            encrypted_key = pub_key.encrypt(
                cek,
                padding.OAEP(
                    mgf=padding.MGF1(algorithm=hashes.SHA256()),
                    algorithm=hashes.SHA256(),
                    label=None
                )
            )
    
            # Step 4: Generate Initialization Vector
            iv = os.urandom(12)  # 96 bits for GCM
    
            # Step 5: Create Additional Authenticated Data (AAD)
            header_b64 = base64.urlsafe_b64encode(
                json.dumps(header, separators=(',', ':')).encode()
            ).decode().rstrip('=')
    
            aad = header_b64.encode()
    
            # Step 6: Encrypt payload
            aesgcm = AESGCM(cek)
            payload_json = json.dumps(payload, separators=(',', ':')).encode()
    
            ciphertext_and_tag = aesgcm.encrypt(iv, payload_json, aad)
            ciphertext = ciphertext_and_tag[:-16]  # All but last 16 bytes
            auth_tag = ciphertext_and_tag[-16:]    # Last 16 bytes
    
            # Step 7: Base64URL encode all components
            header_b64 = base64.urlsafe_b64encode(
                json.dumps(header, separators=(',', ':')).encode()
            ).decode().rstrip('=')
    
            encrypted_key_b64 = base64.urlsafe_b64encode(encrypted_key).decode().rstrip('=')
            iv_b64 = base64.urlsafe_b64encode(iv).decode().rstrip('=')
            ciphertext_b64 = base64.urlsafe_b64encode(ciphertext).decode().rstrip('=')
            auth_tag_b64 = base64.urlsafe_b64encode(auth_tag).decode().rstrip('=')
    
            # Step 8: Concatenate to form JWE
            jwe_token = f"{header_b64}.{encrypted_key_b64}.{iv_b64}.{ciphertext_b64}.{auth_tag_b64}"
    
            return jwe_token
    
        def decrypt_jwe_token(self, jwe_token: str, recipient_private_key: Optional[rsa.RSAPrivateKey] = None) -> Optional[Dict[str, Any]]:
            """Decrypt JWE token and return payload"""
    
            try:
                # Use provided private key or default
                priv_key = recipient_private_key or self.private_key
    
                # Split JWE into components
                parts = jwe_token.split('.')
                if len(parts) != 5:
                    raise ValueError("Invalid JWE format")
    
                header_b64, encrypted_key_b64, iv_b64, ciphertext_b64, auth_tag_b64 = parts
    
                # Decode components
                header = json.loads(self._base64url_decode(header_b64))
                encrypted_key = self._base64url_decode(encrypted_key_b64)
                iv = self._base64url_decode(iv_b64)
                ciphertext = self._base64url_decode(ciphertext_b64)
                auth_tag = self._base64url_decode(auth_tag_b64)
    
                # Verify algorithms
                if header.get('alg') != self.key_encryption_algorithm:
                    raise ValueError("Unsupported key encryption algorithm")
    
                if header.get('enc') != self.content_encryption_algorithm:
                    raise ValueError("Unsupported content encryption algorithm")
    
                # Decrypt CEK
                cek = priv_key.decrypt(
                    encrypted_key,
                    padding.OAEP(
                        mgf=padding.MGF1(algorithm=hashes.SHA256()),
                        algorithm=hashes.SHA256(),
                        label=None
                    )
                )
    
                # Prepare AAD
                aad = header_b64.encode()
    
                # Decrypt payload
                aesgcm = AESGCM(cek)
                ciphertext_with_tag = ciphertext + auth_tag
    
                payload_json = aesgcm.decrypt(iv, ciphertext_with_tag, aad)
                payload = json.loads(payload_json.decode())
    
                return payload
    
            except Exception as e:
                print(f"JWE decryption failed: {e}")
                return None
    
        def _base64url_decode(self, data: str) -> bytes:
            """Decode base64url encoded data"""
            # Add padding if needed
            padding = 4 - len(data) % 4
            if padding != 4:
                data += '=' * padding
    
            return base64.urlsafe_b64decode(data)
    
        def create_nested_jwt(self, payload: Dict[str, Any], sign_key: str, encrypt_key: Optional[rsa.RSAPublicKey] = None) -> str:
            """Create nested JWT (JWS inside JWE)"""
    
            # First, create a signed JWT
            from app.auth.jwt_handler import JWTHandler
            jwt_handler = JWTHandler()
    
            # Create signed token
            signed_token = jwt_handler.create_access_token(payload)
    
            # Then encrypt the signed token
            nested_payload = {"jwt": signed_token}
            encrypted_token = self.create_jwe_token(nested_payload, encrypt_key)
    
            return encrypted_token
    
        def verify_nested_jwt(self, nested_token: str, decrypt_key: Optional[rsa.RSAPrivateKey] = None) -> Optional[Dict[str, Any]]:
            """Verify nested JWT (decrypt then verify signature)"""
    
            # First decrypt
            decrypted_payload = self.decrypt_jwe_token(nested_token, decrypt_key)
            if not decrypted_payload or 'jwt' not in decrypted_payload:
                return None
    
            # Then verify signature
            from app.auth.jwt_handler import JWTHandler
            jwt_handler = JWTHandler()
    
            signed_token = decrypted_payload['jwt']
            return jwt_handler.verify_token(signed_token)
    
    # Example usage in FastAPI
    class SecureJWTHandler:
        def __init__(self):
            self.jwe_handler = JWEHandler()
            from app.auth.jwt_handler import JWTHandler
            self.jwt_handler = JWTHandler()
    
        async def create_secure_token(self, user_data: Dict[str, Any], encryption_required: bool = False) -> str:
            """Create secure token with optional encryption"""
    
            if encryption_required:
                # Create encrypted token for sensitive data
                sensitive_payload = {
                    "sub": str(user_data.get("id")),
                    "email": user_data.get("email"),
                    "full_name": user_data.get("full_name"),
                    "sensitive_data": {
                        "ssn": user_data.get("ssn"),  # Example sensitive data
                        "bank_account": user_data.get("bank_account"),
                        "medical_info": user_data.get("medical_info")
                    },
                    "exp": (datetime.utcnow() + timedelta(minutes=30)).timestamp(),
                    "iat": datetime.utcnow().timestamp(),
                    "type": "secure_access"
                }
    
                return self.jwe_handler.create_jwe_token(sensitive_payload)
            else:
                # Create regular signed token
                return await self.jwt_handler.create_access_token(user_data)
    
        def verify_secure_token(self, token: str) -> Optional[Dict[str, Any]]:
            """Verify secure token (try JWE first, then JWS)"""
    
            # Try to decrypt as JWE first
            payload = self.jwe_handler.decrypt_jwe_token(token)
            if payload:
                # Validate expiration for JWE tokens
                exp = payload.get('exp')
                if exp and datetime.utcnow().timestamp() > exp:
                    return None
                return payload
    
            # Fall back to JWS verification
            return self.jwt_handler.verify_token(token)
    Python

    JWE Route Implementation

    Create backend/app/routers/secure.py:

    from fastapi import APIRouter, Depends, HTTPException, status, Request
    from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
    from app.auth.jwe_handler import SecureJWTHandler
    from app.auth.models import UserResponse
    from typing import Dict, Any
    
    router = APIRouter()
    security = HTTPBearer()
    secure_jwt = SecureJWTHandler()
    
    @router.post("/secure-login")
    async def secure_login(request: Request, credentials: dict):
        """Login with JWE token for sensitive applications"""
    
        # Validate credentials (implementation similar to regular login)
        # ... credential validation logic ...
    
        # Create encrypted token for sensitive data
        user_data = {
            "id": 123,
            "email": credentials.get("email"),
            "full_name": "John Doe",
            "ssn": "123-45-6789",  # This will be encrypted
            "bank_account": "12345678",
            "medical_info": {"condition": "example"}
        }
    
        encrypted_token = await secure_jwt.create_secure_token(user_data, encryption_required=True)
    
        return {
            "access_token": encrypted_token,
            "token_type": "JWE",
            "message": "Secure token created with encrypted payload"
        }
    
    @router.get("/secure-profile")
    async def get_secure_profile(credentials: HTTPAuthorizationCredentials = Depends(security)):
        """Get user profile from encrypted token"""
    
        token = credentials.credentials
        payload = secure_jwt.verify_secure_token(token)
    
        if not payload:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid or expired secure token"
            )
    
        # Remove sensitive data for response
        response_data = {
            "user_id": payload.get("sub"),
            "email": payload.get("email"),
            "full_name": payload.get("full_name"),
            "has_sensitive_data": "sensitive_data" in payload
        }
    
        return response_data
    
    @router.get("/sensitive-data")
    async def get_sensitive_data(credentials: HTTPAuthorizationCredentials = Depends(security)):
        """Access sensitive data from encrypted token"""
    
        token = credentials.credentials
        payload = secure_jwt.verify_secure_token(token)
    
        if not payload:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid or expired secure token"
            )
    
        sensitive_data = payload.get("sensitive_data")
        if not sensitive_data:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="No sensitive data available"
            )
    
        return {
            "message": "Sensitive data accessed",
            "data": sensitive_data,
            "notice": "This data was encrypted in the JWT token"
        }
    JSON

    12. OAuth 2.0 Integration

    OAuth 2.0 provides a framework for authorization that works seamlessly with JWT tokens. This chapter covers implementing OAuth 2.0 flows with JWT.

    OAuth 2.0 + JWT Architecture

    sequenceDiagram
        participant C as Client App
        participant AS as Authorization Server
        participant RS as Resource Server
        participant U as User
    
        Note over C,U: Authorization Code Flow with JWT
    
        C->>U: Redirect to Authorization Server
        U->>AS: Login & Consent
        AS->>C: Authorization Code
        C->>AS: Exchange Code for Tokens
        AS->>C: JWT Access Token + Refresh Token
    
        Note over C,RS: API Access with JWT
    
        C->>RS: API Request + JWT Token
        RS->>RS: Verify JWT Signature
        RS->>RS: Validate Claims
        RS->>C: Protected Resource

    OAuth 2.0 Server Implementation

    Create backend/app/oauth/oauth_server.py:

    from fastapi import APIRouter, Depends, HTTPException, status, Request, Form
    from fastapi.responses import RedirectResponse, HTMLResponse
    from fastapi.security import HTTPBearer
    import secrets
    import hashlib
    import base64
    from datetime import datetime, timedelta
    from typing import Dict, Any, Optional, List
    from urllib.parse import urlencode, parse_qs
    import json
    
    from app.auth.jwt_handler import JWTHandler
    from app.database.database import database
    
    class OAuth2Server:
        def __init__(self):
            self.jwt_handler = JWTHandler()
    
            # OAuth 2.0 configuration
            self.supported_grant_types = [
                "authorization_code",
                "refresh_token",
                "client_credentials"
            ]
    
            self.supported_response_types = ["code"]
            self.supported_scopes = [
                "read", "write", "admin", 
                "profile", "email", "openid"
            ]
    
            # In production, store these in database
            self.clients = {
                "web_app_client": {
                    "client_secret": "web_app_secret_key",
                    "redirect_uris": [
                        "http://localhost:3000/callback",
                        "http://localhost:8080/callback"
                    ],
                    "scopes": ["read", "write", "profile", "email"],
                    "grant_types": ["authorization_code", "refresh_token"]
                },
                "mobile_app": {
                    "client_secret": None,  # Public client
                    "redirect_uris": ["com.example.app://callback"],
                    "scopes": ["read", "profile"],
                    "grant_types": ["authorization_code"]
                }
            }
    
            # Temporary storage (use Redis in production)
            self.authorization_codes = {}
            self.access_tokens = {}
    
        def generate_authorization_code(self, client_id: str, user_id: str, scopes: List[str], redirect_uri: str) -> str:
            """Generate authorization code"""
            code = secrets.token_urlsafe(32)
    
            self.authorization_codes[code] = {
                "client_id": client_id,
                "user_id": user_id,
                "scopes": scopes,
                "redirect_uri": redirect_uri,
                "expires_at": datetime.utcnow() + timedelta(minutes=10),
                "used": False
            }
    
            return code
    
        def validate_client(self, client_id: str, client_secret: Optional[str] = None) -> bool:
            """Validate OAuth client"""
            if client_id not in self.clients:
                return False
    
            client = self.clients[client_id]
    
            # Public clients don't have secrets
            if client["client_secret"] is None:
                return client_secret is None
    
            return client["client_secret"] == client_secret
    
        def validate_redirect_uri(self, client_id: str, redirect_uri: str) -> bool:
            """Validate redirect URI"""
            if client_id not in self.clients:
                return False
    
            return redirect_uri in self.clients[client_id]["redirect_uris"]
    
        def validate_scope(self, client_id: str, requested_scopes: List[str]) -> bool:
            """Validate requested scopes"""
            if client_id not in self.clients:
                return False
    
            client_scopes = self.clients[client_id]["scopes"]
            return all(scope in client_scopes for scope in requested_scopes)
    
        async def create_oauth_tokens(self, user_id: str, client_id: str, scopes: List[str]) -> Dict[str, Any]:
            """Create OAuth tokens with JWT"""
    
            # Get user data
            user_query = """
            SELECT u.*, r.name as role_name 
            FROM users u 
            LEFT JOIN roles r ON u.role_id = r.id 
            WHERE u.id = :user_id AND u.is_active = true
            """
            user = await database.fetch_one(user_query, {"user_id": user_id})
    
            if not user:
                raise HTTPException(status_code=404, detail="User not found")
    
            # Create JWT access token with OAuth scopes
            token_data = {
                "sub": str(user["id"]),
                "email": user["email"],
                "full_name": user["full_name"],
                "role": user["role_name"] or "user",
                "client_id": client_id,
                "scope": " ".join(scopes),
                "aud": client_id,
                "iss": "oauth2-server"
            }
    
            access_token = await self.jwt_handler.create_access_token(token_data)
    
            # Create refresh token
            refresh_token_data = {
                "sub": str(user["id"]),
                "client_id": client_id,
                "scope": " ".join(scopes),
                "token_type": "refresh"
            }
    
            refresh_token = self.jwt_handler.create_refresh_token(refresh_token_data)
    
            return {
                "access_token": access_token,
                "refresh_token": refresh_token,
                "token_type": "Bearer",
                "expires_in": self.jwt_handler.access_token_expire_minutes * 60,
                "scope": " ".join(scopes)
            }
    
    # OAuth 2.0 Router
    oauth_router = APIRouter(prefix="/oauth")
    oauth_server = OAuth2Server()
    
    @oauth_router.get("/authorize")
    async def authorize(
        request: Request,
        response_type: str,
        client_id: str,
        redirect_uri: str,
        scope: str = "",
        state: Optional[str] = None
    ):
        """OAuth 2.0 Authorization endpoint"""
    
        # Validate parameters
        if response_type != "code":
            error_params = {
                "error": "unsupported_response_type",
                "error_description": "Only 'code' response type is supported"
            }
            if state:
                error_params["state"] = state
    
            error_url = f"{redirect_uri}?{urlencode(error_params)}"
            return RedirectResponse(url=error_url)
    
        # Validate client
        if not oauth_server.validate_client(client_id):
            raise HTTPException(status_code=400, detail="Invalid client_id")
    
        # Validate redirect URI
        if not oauth_server.validate_redirect_uri(client_id, redirect_uri):
            raise HTTPException(status_code=400, detail="Invalid redirect_uri")
    
        # Parse and validate scopes
        requested_scopes = scope.split() if scope else ["read"]
        if not oauth_server.validate_scope(client_id, requested_scopes):
            error_params = {
                "error": "invalid_scope",
                "error_description": "Invalid or unauthorized scopes requested"
            }
            if state:
                error_params["state"] = state
    
            error_url = f"{redirect_uri}?{urlencode(error_params)}"
            return RedirectResponse(url=error_url)
    
        # In a real implementation, check if user is authenticated
        # For this example, we'll assume user_id = "123"
        user_id = "123"  # This would come from session/authentication
    
        # Generate authorization code
        auth_code = oauth_server.generate_authorization_code(
            client_id, user_id, requested_scopes, redirect_uri
        )
    
        # Redirect back to client with authorization code
        success_params = {"code": auth_code}
        if state:
            success_params["state"] = state
    
        callback_url = f"{redirect_uri}?{urlencode(success_params)}"
        return RedirectResponse(url=callback_url)
    
    @oauth_router.post("/token")
    async def token(
        grant_type: str = Form(...),
        client_id: str = Form(...),
        client_secret: Optional[str] = Form(None),
        code: Optional[str] = Form(None),
        redirect_uri: Optional[str] = Form(None),
        refresh_token: Optional[str] = Form(None),
        scope: Optional[str] = Form(None)
    ):
        """OAuth 2.0 Token endpoint"""
    
        # Validate client credentials
        if not oauth_server.validate_client(client_id, client_secret):
            raise HTTPException(
                status_code=401,
                detail="Invalid client credentials",
                headers={"WWW-Authenticate": "Basic"}
            )
    
        if grant_type == "authorization_code":
            # Authorization Code Grant
            if not code or not redirect_uri:
                raise HTTPException(
                    status_code=400,
                    detail="Missing required parameters for authorization_code grant"
                )
    
            # Validate authorization code
            if code not in oauth_server.authorization_codes:
                raise HTTPException(status_code=400, detail="Invalid authorization code")
    
            auth_data = oauth_server.authorization_codes[code]
    
            # Check if code is expired or used
            if auth_data["used"] or datetime.utcnow() > auth_data["expires_at"]:
                raise HTTPException(status_code=400, detail="Authorization code expired or used")
    
            # Validate client and redirect URI
            if auth_data["client_id"] != client_id or auth_data["redirect_uri"] != redirect_uri:
                raise HTTPException(status_code=400, detail="Client or redirect URI mismatch")
    
            # Mark code as used
            oauth_server.authorization_codes[code]["used"] = True
    
            # Create tokens
            tokens = await oauth_server.create_oauth_tokens(
                auth_data["user_id"],
                client_id,
                auth_data["scopes"]
            )
    
            return tokens
    
        elif grant_type == "refresh_token":
            # Refresh Token Grant
            if not refresh_token:
                raise HTTPException(
                    status_code=400,
                    detail="Missing refresh_token parameter"
                )
    
            # Verify refresh token
            payload = oauth_server.jwt_handler.verify_token(refresh_token)
            if not payload or payload.get("token_type") != "refresh":
                raise HTTPException(status_code=400, detail="Invalid refresh token")
    
            # Validate client
            if payload.get("client_id") != client_id:
                raise HTTPException(status_code=400, detail="Client mismatch")
    
            # Create new access token
            scopes = payload.get("scope", "").split()
            tokens = await oauth_server.create_oauth_tokens(
                payload["sub"],
                client_id,
                scopes
            )
    
            # Don't return new refresh token unless scope changed
            if not scope or scope == payload.get("scope"):
                del tokens["refresh_token"]
    
            return tokens
    
        else:
            raise HTTPException(
                status_code=400,
                detail="Unsupported grant type"
            )
    
    @oauth_router.get("/.well-known/oauth-authorization-server")
    async def oauth_metadata():
        """OAuth 2.0 Authorization Server Metadata"""
        return {
            "issuer": "https://your-auth-server.com",
            "authorization_endpoint": "/oauth/authorize",
            "token_endpoint": "/oauth/token",
            "response_types_supported": ["code"],
            "grant_types_supported": [
                "authorization_code",
                "refresh_token",
                "client_credentials"
            ],
            "scopes_supported": [
                "read", "write", "admin", 
                "profile", "email", "openid"
            ],
            "token_endpoint_auth_methods_supported": [
                "client_secret_basic",
                "client_secret_post"
            ]
        }
    Python

    OpenID Connect Extension

    Create backend/app/oauth/openid_connect.py:

    from typing import Dict, Any, Optional
    import hashlib
    import base64
    from app.oauth.oauth_server import OAuth2Server
    
    class OpenIDConnectServer(OAuth2Server):
        def __init__(self):
            super().__init__()
    
            # Add OpenID Connect scopes
            self.supported_scopes.extend(["openid", "profile", "email", "address", "phone"])
    
        async def create_id_token(self, user_id: str, client_id: str, scopes: List[str], access_token: str) -> str:
            """Create OpenID Connect ID Token"""
    
            # Get user data
            user_query = """
            SELECT u.*, r.name as role_name 
            FROM users u 
            LEFT JOIN roles r ON u.role_id = r.id 
            WHERE u.id = :user_id AND u.is_active = true
            """
            user = await database.fetch_one(user_query, {"user_id": user_id})
    
            if not user:
                raise HTTPException(status_code=404, detail="User not found")
    
            # Create ID token claims
            id_token_claims = {
                "iss": "https://your-auth-server.com",
                "sub": str(user["id"]),
                "aud": client_id,
                "exp": datetime.utcnow() + timedelta(hours=1),
                "iat": datetime.utcnow(),
                "auth_time": datetime.utcnow(),
                "nonce": self.generate_nonce()  # Should come from auth request
            }
    
            # Add claims based on requested scopes
            if "profile" in scopes:
                id_token_claims.update({
                    "name": user["full_name"],
                    "given_name": user["full_name"].split()[0] if user["full_name"] else "",
                    "family_name": " ".join(user["full_name"].split()[1:]) if len(user["full_name"].split()) > 1 else "",
                    "preferred_username": user["email"],
                    "updated_at": user["updated_at"].timestamp() if user["updated_at"] else None
                })
    
            if "email" in scopes:
                id_token_claims.update({
                    "email": user["email"],
                    "email_verified": True  # Assume emails are verified
                })
    
            # Create access token hash for at_hash claim
            if access_token:
                at_hash = self.create_at_hash(access_token)
                id_token_claims["at_hash"] = at_hash
    
            # Create and return ID token
            return self.jwt_handler.create_access_token(id_token_claims)
    
        def create_at_hash(self, access_token: str) -> str:
            """Create access token hash for ID token"""
            # Hash the access token with SHA256
            hash_bytes = hashlib.sha256(access_token.encode()).digest()
    
            # Take the left-most half of the hash
            half_hash = hash_bytes[:len(hash_bytes)//2]
    
            # Base64url encode
            return base64.urlsafe_b64encode(half_hash).decode().rstrip('=')
    
        def generate_nonce(self) -> str:
            """Generate nonce for ID token"""
            return secrets.token_urlsafe(16)
    
        async def create_oauth_tokens_with_id_token(self, user_id: str, client_id: str, scopes: List[str]) -> Dict[str, Any]:
            """Create OAuth tokens including ID token for OpenID Connect"""
    
            # Create standard OAuth tokens
            tokens = await self.create_oauth_tokens(user_id, client_id, scopes)
    
            # Add ID token if OpenID Connect scope is requested
            if "openid" in scopes:
                id_token = await self.create_id_token(
                    user_id, 
                    client_id, 
                    scopes, 
                    tokens["access_token"]
                )
                tokens["id_token"] = id_token
    
            return tokens
    
    # Update token endpoint to support OpenID Connect
    oidc_server = OpenIDConnectServer()
    
    @oauth_router.post("/token")
    async def enhanced_token(
        grant_type: str = Form(...),
        client_id: str = Form(...),
        client_secret: Optional[str] = Form(None),
        code: Optional[str] = Form(None),
        redirect_uri: Optional[str] = Form(None),
        refresh_token: Optional[str] = Form(None),
        scope: Optional[str] = Form(None)
    ):
        """Enhanced OAuth 2.0 Token endpoint with OpenID Connect support"""
    
        # ... (same validation logic as before) ...
    
        if grant_type == "authorization_code":
            # Create tokens with ID token if OpenID Connect
            tokens = await oidc_server.create_oauth_tokens_with_id_token(
                auth_data["user_id"],
                client_id,
                auth_data["scopes"]
            )
    
            return tokens
    
        # ... (rest of the implementation) ...
    
    @oauth_router.get("/.well-known/openid_configuration")
    async def openid_configuration():
        """OpenID Connect Discovery Document"""
        return {
            "issuer": "https://your-auth-server.com",
            "authorization_endpoint": "/oauth/authorize",
            "token_endpoint": "/oauth/token",
            "userinfo_endpoint": "/oauth/userinfo",
            "jwks_uri": "/oauth/jwks",
            "response_types_supported": ["code", "id_token", "code id_token"],
            "grant_types_supported": [
                "authorization_code",
                "refresh_token",
                "implicit"
            ],
            "subject_types_supported": ["public"],
            "id_token_signing_alg_values_supported": ["RS256", "HS256"],
            "scopes_supported": [
                "openid", "profile", "email", "address", "phone",
                "read", "write", "admin"
            ],
            "token_endpoint_auth_methods_supported": [
                "client_secret_basic",
                "client_secret_post"
            ],
            "claims_supported": [
                "sub", "name", "given_name", "family_name", "preferred_username",
                "email", "email_verified", "picture", "updated_at"
            ]
        }
    
    @oauth_router.get("/userinfo")
    async def userinfo(credentials: HTTPAuthorizationCredentials = Depends(HTTPBearer())):
        """OpenID Connect UserInfo endpoint"""
    
        token = credentials.credentials
        payload = oidc_server.jwt_handler.verify_token(token)
    
        if not payload:
            raise HTTPException(
                status_code=401,
                detail="Invalid access token"
            )
    
        # Check if token has appropriate scope
        token_scopes = payload.get("scope", "").split()
        if "openid" not in token_scopes:
            raise HTTPException(
                status_code=403,
                detail="Token does not have openid scope"
            )
    
        # Get user information
        user_id = payload.get("sub")
        user_query = """
        SELECT * FROM users WHERE id = :user_id AND is_active = true
        """
        user = await database.fetch_one(user_query, {"user_id": user_id})
    
        if not user:
            raise HTTPException(status_code=404, detail="User not found")
    
        # Build response based on scopes
        userinfo = {"sub": str(user["id"])}
    
        if "profile" in token_scopes:
            userinfo.update({
                "name": user["full_name"],
                "preferred_username": user["email"],
                "updated_at": user["updated_at"].timestamp() if user["updated_at"] else None
            })
    
        if "email" in token_scopes:
            userinfo.update({
                "email": user["email"],
                "email_verified": True
            })
    
        return userinfo
    Python

    13. Key Management and Rotation

    Proper key management is crucial for JWT security. This chapter covers key rotation strategies, JWKS endpoints, and automated key management.

    Key Rotation Strategy

    graph TB
        A[Key Rotation Process] --> B[Generate New Key]
        B --> C[Add to JWKS]
        C --> D[Start Using for Signing]
        D --> E[Keep Old Key for Verification]
        E --> F[Remove Old Key After Grace Period]
    
        G[JWKS Endpoint] --> H[Current Signing Key]
        G --> I[Previous Key for Verification]
        G --> J[Next Key for Preparation]

    Key Management Implementation

    Create backend/app/auth/key_manager.py:

    from cryptography.hazmat.primitives.asymmetric import rsa
    from cryptography.hazmat.primitives import serialization, hashes
    from cryptography.hazmat.primitives.serialization import load_pem_private_key, load_pem_public_key
    import json
    import base64
    import hashlib
    from datetime import datetime, timedelta
    from typing import Dict, List, Optional, Tuple
    import os
    from dataclasses import dataclass, asdict
    
    @dataclass
    class JWTKey:
        """JWT Key information"""
        kid: str  # Key ID
        use: str  # Key use (sig for signing)
        kty: str  # Key type (RSA, EC)
        alg: str  # Algorithm
        n: str    # RSA modulus (for RSA keys)
        e: str    # RSA exponent (for RSA keys)
        created_at: datetime
        expires_at: Optional[datetime] = None
        private_key: Optional[str] = None  # PEM encoded private key
    
        def to_jwk(self) -> Dict:
            """Convert to JWK format"""
            jwk = {
                "kid": self.kid,
                "use": self.use,
                "kty": self.kty,
                "alg": self.alg,
                "n": self.n,
                "e": self.e
            }
            return jwk
    
    class KeyManager:
        def __init__(self, key_directory: str = "keys"):
            self.key_directory = key_directory
            self.keys: Dict[str, JWTKey] = {}
            self.current_signing_key: Optional[str] = None
    
            # Key rotation settings
            self.key_rotation_interval = timedelta(days=30)
            self.key_grace_period = timedelta(days=7)
    
            # Ensure key directory exists
            os.makedirs(key_directory, exist_ok=True)
    
            # Load existing keys
            self.load_keys()
    
            # Ensure we have at least one key
            if not self.keys:
                self.generate_initial_key()
    
        def generate_key_id(self) -> str:
            """Generate a unique key ID"""
            timestamp = datetime.utcnow().strftime("%Y%m%d%H%M%S")
            random_part = hashlib.sha256(os.urandom(32)).hexdigest()[:8]
            return f"{timestamp}_{random_part}"
    
        def generate_rsa_key_pair(self) -> Tuple[rsa.RSAPrivateKey, rsa.RSAPublicKey]:
            """Generate RSA key pair"""
            private_key = rsa.generate_private_key(
                public_exponent=65537,
                key_size=2048
            )
            public_key = private_key.public_key()
            return private_key, public_key
    
        def rsa_key_to_jwk_components(self, public_key: rsa.RSAPublicKey) -> Tuple[str, str]:
            """Extract JWK components from RSA public key"""
            public_numbers = public_key.public_key().public_numbers()
    
            # Convert to base64url-encoded format
            n = self.int_to_base64url(public_numbers.n)
            e = self.int_to_base64url(public_numbers.e)
    
            return n, e
    
        def int_to_base64url(self, value: int) -> str:
            """Convert integer to base64url string"""
            # Calculate byte length needed
            byte_length = (value.bit_length() + 7) // 8
    
            # Convert to bytes
            value_bytes = value.to_bytes(byte_length, byteorder='big')
    
            # Base64url encode
            return base64.urlsafe_b64encode(value_bytes).decode().rstrip('=')
    
        def create_jwt_key(self, algorithm: str = "RS256") -> JWTKey:
            """Create a new JWT key"""
            kid = self.generate_key_id()
            private_key, public_key = self.generate_rsa_key_pair()
    
            # Get JWK components
            n, e = self.rsa_key_to_jwk_components(public_key)
    
            # Serialize private key
            private_key_pem = private_key.private_bytes(
                encoding=serialization.Encoding.PEM,
                format=serialization.PrivateFormat.PKCS8,
                encryption_algorithm=serialization.NoEncryption()
            ).decode()
    
            jwt_key = JWTKey(
                kid=kid,
                use="sig",
                kty="RSA",
                alg=algorithm,
                n=n,
                e=e,
                created_at=datetime.utcnow(),
                private_key=private_key_pem
            )
    
            return jwt_key
    
        def save_key(self, jwt_key: JWTKey):
            """Save key to disk"""
            key_file = os.path.join(self.key_directory, f"{jwt_key.kid}.json")
    
            key_data = asdict(jwt_key)
            key_data['created_at'] = jwt_key.created_at.isoformat()
            if jwt_key.expires_at:
                key_data['expires_at'] = jwt_key.expires_at.isoformat()
    
            with open(key_file, 'w') as f:
                json.dump(key_data, f, indent=2)
    
        def load_keys(self):
            """Load keys from disk"""
            if not os.path.exists(self.key_directory):
                return
    
            for filename in os.listdir(self.key_directory):
                if filename.endswith('.json'):
                    try:
                        key_file = os.path.join(self.key_directory, filename)
                        with open(key_file, 'r') as f:
                            key_data = json.load(f)
    
                        # Parse datetime fields
                        key_data['created_at'] = datetime.fromisoformat(key_data['created_at'])
                        if key_data.get('expires_at'):
                            key_data['expires_at'] = datetime.fromisoformat(key_data['expires_at'])
    
                        jwt_key = JWTKey(**key_data)
                        self.keys[jwt_key.kid] = jwt_key
    
                        # Set as current signing key if none set
                        if not self.current_signing_key:
                            self.current_signing_key = jwt_key.kid
    
                    except Exception as e:
                        print(f"Failed to load key {filename}: {e}")
    
        def generate_initial_key(self):
            """Generate initial key if none exist"""
            jwt_key = self.create_jwt_key()
            self.keys[jwt_key.kid] = jwt_key
            self.current_signing_key = jwt_key.kid
            self.save_key(jwt_key)
    
        def rotate_keys(self):
            """Rotate signing keys"""
            if not self.current_signing_key:
                self.generate_initial_key()
                return
    
            current_key = self.keys[self.current_signing_key]
    
            # Check if rotation is needed
            if datetime.utcnow() - current_key.created_at < self.key_rotation_interval:
                return
    
            # Generate new key
            new_key = self.create_jwt_key()
            self.keys[new_key.kid] = new_key
            self.save_key(new_key)
    
            # Set expiration for old key
            current_key.expires_at = datetime.utcnow() + self.key_grace_period
            self.save_key(current_key)
    
            # Update current signing key
            self.current_signing_key = new_key.kid
    
            print(f"Key rotation completed. New signing key: {new_key.kid}")
    
        def cleanup_expired_keys(self):
            """Remove expired keys"""
            expired_keys = []
    
            for kid, jwt_key in self.keys.items():
                if jwt_key.expires_at and datetime.utcnow() > jwt_key.expires_at:
                    expired_keys.append(kid)
    
            for kid in expired_keys:
                # Remove from memory
                del self.keys[kid]
    
                # Remove from disk
                key_file = os.path.join(self.key_directory, f"{kid}.json")
                if os.path.exists(key_file):
                    os.remove(key_file)
    
                print(f"Expired key removed: {kid}")
    
        def get_signing_key(self) -> Optional[JWTKey]:
            """Get current signing key"""
            if self.current_signing_key and self.current_signing_key in self.keys:
                return self.keys[self.current_signing_key]
            return None
    
        def get_verification_key(self, kid: str) -> Optional[JWTKey]:
            """Get key for verification by kid"""
            return self.keys.get(kid)
    
        def get_jwks(self) -> Dict:
            """Get JWKS (JSON Web Key Set) for public distribution"""
            keys = []
    
            for jwt_key in self.keys.values():
                # Only include non-expired keys
                if not jwt_key.expires_at or datetime.utcnow() <= jwt_key.expires_at:
                    keys.append(jwt_key.to_jwk())
    
            return {"keys": keys}
    
        def get_private_key(self, kid: str) -> Optional[rsa.RSAPrivateKey]:
            """Get private key for signing"""
            jwt_key = self.keys.get(kid)
            if not jwt_key or not jwt_key.private_key:
                return None
    
            try:
                private_key = load_pem_private_key(
                    jwt_key.private_key.encode(),
                    password=None
                )
                return private_key
            except Exception as e:
                print(f"Failed to load private key {kid}: {e}")
                return None
    
        def perform_maintenance(self):
            """Perform regular maintenance tasks"""
            self.rotate_keys()
            self.cleanup_expired_keys()
    
    # Global key manager instance
    key_manager = KeyManager()
    Python

    Enhanced JWT Handler with Key Rotation

    Update the JWT Handler to use the key manager:

    # In jwt_handler.py
    import jwt
    from app.auth.key_manager import key_manager
    
    class RotatingJWTHandler:
        def __init__(self):
            self.key_manager = key_manager
            # ... other initialization ...
    
        def create_access_token(self, data: Dict[str, Any]) -> str:
            """Create access token with current signing key"""
    
            # Get current signing key
            signing_key_info = self.key_manager.get_signing_key()
            if not signing_key_info:
                raise Exception("No signing key available")
    
            private_key = self.key_manager.get_private_key(signing_key_info.kid)
            if not private_key:
                raise Exception("Failed to load signing key")
    
            # Prepare payload
            to_encode = data.copy()
            expire = datetime.utcnow() + timedelta(minutes=self.access_token_expire_minutes)
    
            to_encode.update({
                "exp": expire,
                "iat": datetime.utcnow(),
                "type": "access"
            })
    
            # Create token with key ID in header
            encoded_jwt = jwt.encode(
                to_encode,
                private_key,
                algorithm=signing_key_info.alg,
                headers={"kid": signing_key_info.kid}
            )
    
            return encoded_jwt
    
        def verify_token(self, token: str) -> Optional[Dict[str, Any]]:
            """Verify token using appropriate key"""
    
            try:
                # Get key ID from header
                header = jwt.get_unverified_header(token)
                kid = header.get("kid")
    
                if not kid:
                    raise jwt.InvalidTokenError("Missing key ID in token header")
    
                # Get verification key
                key_info = self.key_manager.get_verification_key(kid)
                if not key_info:
                    raise jwt.InvalidTokenError(f"Unknown key ID: {kid}")
    
                # Get private key (we'll extract public key from it)
                private_key = self.key_manager.get_private_key(kid)
                if not private_key:
                    raise jwt.InvalidTokenError(f"Key not available: {kid}")
    
                public_key = private_key.public_key()
    
                # Verify token
                payload = jwt.decode(
                    token,
                    public_key,
                    algorithms=[key_info.alg]
                )
    
                return payload
    
            except jwt.ExpiredSignatureError:
                return None
            except jwt.InvalidTokenError:
                return None
            except Exception as e:
                print(f"Token verification failed: {e}")
                return None
    Python

    JWKS Endpoint

    Create backend/app/routers/jwks.py:

    from fastapi import APIRouter
    from app.auth.key_manager import key_manager
    
    router = APIRouter()
    
    @router.get("/.well-known/jwks.json")
    async def jwks():
        """JSON Web Key Set endpoint for public key distribution"""
        return key_manager.get_jwks()
    
    @router.get("/jwks")
    async def jwks_alt():
        """Alternative JWKS endpoint"""
        return key_manager.get_jwks()
    
    @router.post("/admin/rotate-keys")
    async def rotate_keys():
        """Manual key rotation endpoint (admin only)"""
        # In production, add proper authentication and authorization
        key_manager.rotate_keys()
        return {"message": "Key rotation completed"}
    
    @router.get("/admin/key-status")
    async def key_status():
        """Get key rotation status (admin only)"""
        signing_key = key_manager.get_signing_key()
    
        return {
            "current_signing_key": signing_key.kid if signing_key else None,
            "total_keys": len(key_manager.keys),
            "keys": [
                {
                    "kid": k.kid,
                    "created_at": k.created_at.isoformat(),
                    "expires_at": k.expires_at.isoformat() if k.expires_at else None,
                    "is_current": k.kid == key_manager.current_signing_key
                }
                for k in key_manager.keys.values()
            ]
        }
    Python

    Automated Key Rotation with Background Tasks

    Create backend/app/tasks/key_rotation.py:

    import asyncio
    from datetime import datetime, timedelta
    from app.auth.key_manager import key_manager
    import logging
    
    logger = logging.getLogger(__name__)
    
    class KeyRotationScheduler:
        def __init__(self):
            self.running = False
            self.task = None
    
        async def start(self):
            """Start the key rotation scheduler"""
            if self.running:
                return
    
            self.running = True
            self.task = asyncio.create_task(self._rotation_loop())
            logger.info("Key rotation scheduler started")
    
        async def stop(self):
            """Stop the key rotation scheduler"""
            self.running = False
            if self.task:
                self.task.cancel()
                try:
                    await self.task
                except asyncio.CancelledError:
                    pass
            logger.info("Key rotation scheduler stopped")
    
        async def _rotation_loop(self):
            """Main rotation loop"""
            while self.running:
                try:
                    # Perform maintenance every hour
                    key_manager.perform_maintenance()
    
                    # Wait for next check (1 hour)
                    await asyncio.sleep(3600)
    
                except asyncio.CancelledError:
                    break
                except Exception as e:
                    logger.error(f"Key rotation error: {e}")
                    # Wait a bit before retrying
                    await asyncio.sleep(300)  # 5 minutes
    
    # Global scheduler instance
    key_rotation_scheduler = KeyRotationScheduler()
    
    # Add to FastAPI startup/shutdown events
    # In main.py:
    # @app.on_event("startup")
    # async def startup():
    #     await key_rotation_scheduler.start()
    # 
    # @app.on_event("shutdown")
    # async def shutdown():
    #     await key_rotation_scheduler.stop()
    Python

    This key management system provides:

    1. Automatic Key Rotation: Regular generation of new signing keys
    2. JWKS Endpoint: Public distribution of verification keys
    3. Graceful Key Transitions: Old keys remain valid during grace period
    4. Key Persistence: Keys are stored securely on disk
    5. Background Tasks: Automated maintenance and rotation
    6. Admin Interface: Manual control and monitoring of key rotation

    The system ensures continuous operation while maintaining security through regular key rotation.


    14. Token Revocation and Blacklisting

    Token revocation is crucial for immediate security response. This chapter covers various approaches to token revocation and blacklisting strategies.

    Token Revocation Strategies

    graph TB
        A[Token Revocation Methods] --> B[Server-Side Blacklist]
        A --> C[Database Tracking]
        A --> D[Redis Cache]
        A --> E[Short Token Lifetimes]
    
        B --> F[In-Memory Set]
        B --> G[File-Based Storage]
    
        C --> H[Token Table]
        C --> I[User Sessions Table]
    
        D --> J[Distributed Cache]
        D --> K[Pub/Sub Notifications]

    Redis-Based Token Blacklist

    Create backend/app/auth/token_blacklist.py:

    import redis
    import json
    from typing import Optional, Set
    from datetime import datetime, timedelta
    from decouple import config
    import asyncio
    import aioredis
    
    class TokenBlacklist:
        def __init__(self):
            # Redis configuration
            self.redis_url = config("REDIS_URL", default="redis://localhost:6379")
            self.redis_client = None
            self.blacklist_prefix = "jwt_blacklist:"
            self.user_sessions_prefix = "user_sessions:"
    
            # Fallback in-memory storage
            self.memory_blacklist: Set[str] = set()
            self.use_redis = config("USE_REDIS", default=True, cast=bool)
    
        async def initialize(self):
            """Initialize Redis connection"""
            if self.use_redis:
                try:
                    self.redis_client = await aioredis.from_url(
                        self.redis_url,
                        encoding="utf-8",
                        decode_responses=True
                    )
                    # Test connection
                    await self.redis_client.ping()
                    print("Redis connected for token blacklist")
                except Exception as e:
                    print(f"Redis connection failed: {e}. Using in-memory blacklist.")
                    self.use_redis = False
    
        async def blacklist_token(self, jti: str, exp_timestamp: int, user_id: Optional[str] = None):
            """Add token to blacklist"""
    
            if self.use_redis and self.redis_client:
                try:
                    key = f"{self.blacklist_prefix}{jti}"
    
                    # Store with expiration based on token's exp claim
                    current_timestamp = int(datetime.utcnow().timestamp())
                    ttl = max(exp_timestamp - current_timestamp, 60)  # At least 1 minute
    
                    token_info = {
                        "jti": jti,
                        "blacklisted_at": current_timestamp,
                        "user_id": user_id,
                        "expires_at": exp_timestamp
                    }
    
                    await self.redis_client.setex(
                        key,
                        ttl,
                        json.dumps(token_info)
                    )
    
                    return True
                except Exception as e:
                    print(f"Redis blacklist failed: {e}")
                    # Fall back to memory
                    self.memory_blacklist.add(jti)
                    return True
            else:
                # Use in-memory storage
                self.memory_blacklist.add(jti)
                return True
    
        async def is_blacklisted(self, jti: str) -> bool:
            """Check if token is blacklisted"""
    
            if self.use_redis and self.redis_client:
                try:
                    key = f"{self.blacklist_prefix}{jti}"
                    result = await self.redis_client.get(key)
                    return result is not None
                except Exception as e:
                    print(f"Redis check failed: {e}")
                    # Fall back to memory
                    return jti in self.memory_blacklist
            else:
                return jti in self.memory_blacklist
    
        async def blacklist_user_tokens(self, user_id: str):
            """Blacklist all tokens for a specific user"""
    
            if self.use_redis and self.redis_client:
                try:
                    # Store user blacklist timestamp
                    key = f"{self.user_sessions_prefix}{user_id}:blacklisted_at"
                    current_timestamp = int(datetime.utcnow().timestamp())
    
                    # Set with reasonable TTL (e.g., max token lifetime)
                    await self.redis_client.setex(
                        key,
                        86400 * 7,  # 7 days
                        current_timestamp
                    )
    
                    return True
                except Exception as e:
                    print(f"User token blacklist failed: {e}")
                    return False
            else:
                # For in-memory, we would need to track user tokens separately
                # This is simplified for the example
                return False
    
        async def is_user_blacklisted(self, user_id: str, token_issued_at: int) -> bool:
            """Check if user was blacklisted after token issuance"""
    
            if self.use_redis and self.redis_client:
                try:
                    key = f"{self.user_sessions_prefix}{user_id}:blacklisted_at"
                    blacklisted_at = await self.redis_client.get(key)
    
                    if blacklisted_at:
                        return int(blacklisted_at) > token_issued_at
    
                    return False
                except Exception as e:
                    print(f"User blacklist check failed: {e}")
                    return False
            else:
                return False
    
        async def get_blacklist_stats(self) -> dict:
            """Get blacklist statistics"""
    
            if self.use_redis and self.redis_client:
                try:
                    # Count blacklisted tokens
                    keys = await self.redis_client.keys(f"{self.blacklist_prefix}*")
                    token_count = len(keys)
    
                    # Count blacklisted users
                    user_keys = await self.redis_client.keys(f"{self.user_sessions_prefix}*:blacklisted_at")
                    user_count = len(user_keys)
    
                    return {
                        "blacklisted_tokens": token_count,
                        "blacklisted_users": user_count,
                        "storage": "redis"
                    }
                except Exception as e:
                    print(f"Stats retrieval failed: {e}")
                    return {"error": str(e)}
            else:
                return {
                    "blacklisted_tokens": len(self.memory_blacklist),
                    "blacklisted_users": 0,
                    "storage": "memory"
                }
    
        async def cleanup_expired(self):
            """Clean up expired blacklist entries (Redis handles this automatically)"""
            if not self.use_redis:
                # For in-memory storage, we would need manual cleanup
                # This is simplified for the example
                pass
    
    # Global blacklist instance
    token_blacklist = TokenBlacklist()
    Python

    Enhanced JWT Handler with Blacklist Support

    Update the JWT handler to support token revocation:

    # Enhanced jwt_handler.py with blacklist support
    
    from app.auth.token_blacklist import token_blacklist
    import uuid
    
    class BlacklistAwareJWTHandler:
        def __init__(self):
            # ... existing initialization ...
            self.blacklist = token_blacklist
    
        async def create_access_token(self, data: Dict[str, Any]) -> str:
            """Create access token with unique JTI for blacklisting"""
    
            to_encode = data.copy()
            expire = datetime.utcnow() + timedelta(minutes=self.access_token_expire_minutes)
    
            # Generate unique token ID for blacklisting
            jti = str(uuid.uuid4())
    
            to_encode.update({
                "exp": expire,
                "iat": datetime.utcnow(),
                "jti": jti,
                "type": "access"
            })
    
            encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
            return encoded_jwt
    
        async def verify_token(self, token: str) -> Optional[Dict[str, Any]]:
            """Verify token and check blacklist"""
    
            try:
                payload = jwt.decode(
                    token,
                    self.secret_key,
                    algorithms=[self.algorithm]
                )
    
                # Check if token is blacklisted
                jti = payload.get("jti")
                if jti and await self.blacklist.is_blacklisted(jti):
                    return None
    
                # Check if user is blacklisted after token issuance
                user_id = payload.get("sub")
                token_iat = payload.get("iat")
    
                if user_id and token_iat:
                    if await self.blacklist.is_user_blacklisted(user_id, token_iat):
                        return None
    
                return payload
    
            except jwt.ExpiredSignatureError:
                return None
            except jwt.JWTError:
                return None
    
        async def revoke_token(self, token: str) -> bool:
            """Revoke a specific token"""
    
            try:
                # Decode token without verification to get claims
                payload = jwt.decode(
                    token,
                    options={"verify_signature": False}
                )
    
                jti = payload.get("jti")
                exp = payload.get("exp")
                user_id = payload.get("sub")
    
                if jti and exp:
                    await self.blacklist.blacklist_token(jti, exp, user_id)
                    return True
    
                return False
    
            except Exception as e:
                print(f"Token revocation failed: {e}")
                return False
    
        async def revoke_user_tokens(self, user_id: str) -> bool:
            """Revoke all tokens for a specific user"""
            return await self.blacklist.blacklist_user_tokens(user_id)
    Python

    Token Revocation API Endpoints

    Create backend/app/routers/revocation.py:

    from fastapi import APIRouter, Depends, HTTPException, status, Form
    from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
    from app.auth.jwt_handler import BlacklistAwareJWTHandler
    from app.auth.permissions import RequireAdmin
    from typing import Dict
    
    router = APIRouter()
    security = HTTPBearer()
    jwt_handler = BlacklistAwareJWTHandler()
    
    @router.post("/revoke")
    async def revoke_token(
        token: str = Form(...),
        credentials: HTTPAuthorizationCredentials = Depends(security)
    ):
        """Revoke a specific token"""
    
        # Verify the requesting user is authenticated
        payload = await jwt_handler.verify_token(credentials.credentials)
        if not payload:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid authentication token"
            )
    
        # Users can only revoke their own tokens unless admin
        token_payload = jwt.decode(token, options={"verify_signature": False})
        token_user_id = token_payload.get("sub")
        current_user_id = payload.get("sub")
        user_role = payload.get("role")
    
        if token_user_id != current_user_id and user_role != "admin":
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Can only revoke your own tokens"
            )
    
        success = await jwt_handler.revoke_token(token)
    
        if success:
            return {"message": "Token revoked successfully"}
        else:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Failed to revoke token"
            )
    
    @router.post("/revoke-user/{user_id}")
    async def revoke_user_tokens(
        user_id: str,
        current_user = Depends(RequireAdmin)
    ):
        """Revoke all tokens for a specific user (admin only)"""
    
        success = await jwt_handler.revoke_user_tokens(user_id)
    
        if success:
            return {"message": f"All tokens revoked for user {user_id}"}
        else:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Failed to revoke user tokens"
            )
    
    @router.post("/logout")
    async def logout(credentials: HTTPAuthorizationCredentials = Depends(security)):
        """Logout and revoke current token"""
    
        token = credentials.credentials
        payload = await jwt_handler.verify_token(token)
    
        if not payload:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid token"
            )
    
        # Revoke the current token
        success = await jwt_handler.revoke_token(token)
    
        return {
            "message": "Logged out successfully",
            "token_revoked": success
        }
    
    @router.get("/blacklist/stats")
    async def get_blacklist_stats(current_user = Depends(RequireAdmin)):
        """Get blacklist statistics (admin only)"""
    
        stats = await jwt_handler.blacklist.get_blacklist_stats()
        return stats
    
    @router.post("/blacklist/cleanup")
    async def cleanup_blacklist(current_user = Depends(RequireAdmin)):
        """Clean up expired blacklist entries (admin only)"""
    
        await jwt_handler.blacklist.cleanup_expired()
        return {"message": "Blacklist cleanup completed"}
    Python

    Frontend Token Revocation

    Create frontend/js/tokenRevocation.js:

    /**
     * Token Revocation Management
     */
    class TokenRevocationManager {
        constructor(apiClient) {
            this.api = apiClient;
        }
    
        /**
         * Revoke current access token (logout)
         */
        async logout() {
            try {
                const response = await this.api.post('/revocation/logout');
    
                // Clear local storage
                this.api.removeToken();
    
                return response;
            } catch (error) {
                console.error('Logout failed:', error);
                // Clear tokens anyway
                this.api.removeToken();
                throw error;
            }
        }
    
        /**
         * Revoke a specific token
         */
        async revokeToken(token) {
            try {
                const formData = new FormData();
                formData.append('token', token);
    
                const response = await fetch(`${this.api.baseUrl}/revocation/revoke`, {
                    method: 'POST',
                    headers: {
                        'Authorization': `Bearer ${this.api.getStoredToken()}`
                    },
                    body: formData
                });
    
                if (!response.ok) {
                    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
                }
    
                return await response.json();
            } catch (error) {
                console.error('Token revocation failed:', error);
                throw error;
            }
        }
    
        /**
         * Revoke all tokens for a user (admin only)
         */
        async revokeUserTokens(userId) {
            try {
                return await this.api.post(`/revocation/revoke-user/${userId}`);
            } catch (error) {
                console.error('User token revocation failed:', error);
                throw error;
            }
        }
    
        /**
         * Get blacklist statistics (admin only)
         */
        async getBlacklistStats() {
            try {
                return await this.api.get('/revocation/blacklist/stats');
            } catch (error) {
                console.error('Failed to get blacklist stats:', error);
                throw error;
            }
        }
    
        /**
         * Emergency logout - clear all local data
         */
        emergencyLogout() {
            // Clear all possible token storage locations
            localStorage.removeItem('access_token');
            localStorage.removeItem('refresh_token');
            localStorage.removeItem('jwt_user_data');
            sessionStorage.clear();
    
            // Clear any cookies (if using cookie storage)
            document.cookie.split(";").forEach((c) => {
                const eqPos = c.indexOf("=");
                const name = eqPos > -1 ? c.substr(0, eqPos) : c;
                document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/";
            });
    
            // Redirect to login
            window.location.href = '/login';
        }
    
        /**
         * Setup automatic token validation
         */
        setupTokenValidation() {
            setInterval(async () => {
                const token = this.api.getStoredToken();
                if (token) {
                    try {
                        // Try to use the token
                        await this.api.get('/auth/me');
                    } catch (error) {
                        if (error.status === 401) {
                            // Token is invalid or revoked
                            this.emergencyLogout();
                        }
                    }
                }
            }, 60000); // Check every minute
        }
    }
    
    // Add to global API client
    if (typeof api !== 'undefined') {
        api.revocation = new TokenRevocationManager(api);
    
        // Setup automatic validation
        api.revocation.setupTokenValidation();
    }
    JavaScript

    15. Security Best Practices

    This chapter covers comprehensive security practices for JWT implementation, including protection against common attacks and vulnerabilities.

    Security Checklist

    graph TB
        A[JWT Security Best Practices] --> B[Token Security]
        A --> C[Storage Security]
        A --> D[Transport Security]
        A --> E[Application Security]
    
        B --> F[Strong Algorithms]
        B --> G[Key Management]
        B --> H[Short Lifetimes]
        B --> I[Token Validation]
    
        C --> J[Secure Storage]
        C --> K[HttpOnly Cookies]
        C --> L[Same-Site Policies]
    
        D --> M[HTTPS Only]
        D --> N[HSTS Headers]
        D --> O[Certificate Pinning]
    
        E --> P[CSRF Protection]
        E --> Q[XSS Prevention]
        E --> R[Input Validation]
        E --> S[Rate Limiting]

    Comprehensive Security Implementation

    Create backend/app/security/security_middleware.py:

    from fastapi import Request, Response, HTTPException, status
    from fastapi.middleware.base import BaseHTTPMiddleware
    from starlette.responses import JSONResponse
    import time
    import hashlib
    import hmac
    import re
    from typing import Dict, Set
    from collections import defaultdict, deque
    from datetime import datetime, timedelta
    import asyncio
    
    class SecurityMiddleware(BaseHTTPMiddleware):
        def __init__(self, app, config: Dict = None):
            super().__init__(app)
    
            self.config = config or {}
    
            # Rate limiting
            self.rate_limit_requests = self.config.get('rate_limit_requests', 100)
            self.rate_limit_window = self.config.get('rate_limit_window', 3600)  # 1 hour
            self.request_counts = defaultdict(deque)
    
            # CSRF protection
            self.csrf_secret = self.config.get('csrf_secret', 'your-csrf-secret-key')
            self.csrf_token_header = self.config.get('csrf_header', 'X-CSRF-Token')
    
            # Security headers
            self.security_headers = {
                'X-Content-Type-Options': 'nosniff',
                'X-Frame-Options': 'DENY',
                'X-XSS-Protection': '1; mode=block',
                'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
                'Referrer-Policy': 'strict-origin-when-cross-origin',
                'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'",
            }
    
            # Blocked IPs and suspicious patterns
            self.blocked_ips: Set[str] = set()
            self.suspicious_patterns = [
                r'<script',
                r'javascript:',
                r'onload=',
                r'onerror=',
                r'eval\(',
                r'exec\(',
            ]
    
        async def dispatch(self, request: Request, call_next):
            start_time = time.time()
    
            # Get client IP
            client_ip = self.get_client_ip(request)
    
            # Security checks
            try:
                # 1. Check if IP is blocked
                if client_ip in self.blocked_ips:
                    return JSONResponse(
                        status_code=403,
                        content={"detail": "Access denied"}
                    )
    
                # 2. Rate limiting
                if self.is_rate_limited(client_ip):
                    return JSONResponse(
                        status_code=429,
                        content={"detail": "Rate limit exceeded"}
                    )
    
                # 3. HTTPS enforcement
                if not self.is_https(request) and self.config.get('enforce_https', True):
                    return JSONResponse(
                        status_code=426,
                        content={"detail": "HTTPS required"}
                    )
    
                # 4. CSRF protection for state-changing requests
                if request.method in ['POST', 'PUT', 'DELETE', 'PATCH']:
                    if not self.validate_csrf_token(request):
                        return JSONResponse(
                            status_code=403,
                            content={"detail": "CSRF token missing or invalid"}
                        )
    
                # 5. Input validation
                if not await self.validate_request_data(request):
                    return JSONResponse(
                        status_code=400,
                        content={"detail": "Invalid request data"}
                    )
    
                # Process request
                response = await call_next(request)
    
                # Add security headers
                for header, value in self.security_headers.items():
                    response.headers[header] = value
    
                # Add processing time header
                process_time = time.time() - start_time
                response.headers["X-Process-Time"] = str(process_time)
    
                return response
    
            except Exception as e:
                # Log security incident
                await self.log_security_incident(client_ip, str(e), request)
    
                return JSONResponse(
                    status_code=500,
                    content={"detail": "Internal server error"}
                )
    
        def get_client_ip(self, request: Request) -> str:
            """Get real client IP address"""
            # Check for proxy headers
            forwarded_for = request.headers.get('X-Forwarded-For')
            if forwarded_for:
                return forwarded_for.split(',')[0].strip()
    
            real_ip = request.headers.get('X-Real-IP')
            if real_ip:
                return real_ip
    
            return request.client.host
    
        def is_rate_limited(self, client_ip: str) -> bool:
            """Check if client is rate limited"""
            current_time = time.time()
            cutoff_time = current_time - self.rate_limit_window
    
            # Clean old requests
            while (self.request_counts[client_ip] and 
                   self.request_counts[client_ip][0] < cutoff_time):
                self.request_counts[client_ip].popleft()
    
            # Check current request count
            if len(self.request_counts[client_ip]) >= self.rate_limit_requests:
                return True
    
            # Add current request
            self.request_counts[client_ip].append(current_time)
            return False
    
        def is_https(self, request: Request) -> bool:
            """Check if request is over HTTPS"""
            return request.url.scheme == 'https'
    
        def generate_csrf_token(self, session_id: str) -> str:
            """Generate CSRF token"""
            timestamp = str(int(time.time()))
            message = f"{session_id}:{timestamp}"
            signature = hmac.new(
                self.csrf_secret.encode(),
                message.encode(),
                hashlib.sha256
            ).hexdigest()
    
            return f"{timestamp}:{signature}"
    
        def validate_csrf_token(self, request: Request) -> bool:
            """Validate CSRF token"""
            # Skip CSRF for API calls with proper JWT authentication
            auth_header = request.headers.get('Authorization')
            if auth_header and auth_header.startswith('Bearer '):
                return True
    
            # Check for CSRF token in header
            csrf_token = request.headers.get(self.csrf_token_header)
            if not csrf_token:
                return False
    
            try:
                timestamp, signature = csrf_token.split(':')
    
                # Check token age (valid for 1 hour)
                token_time = int(timestamp)
                if time.time() - token_time > 3600:
                    return False
    
                # Validate signature
                session_id = request.cookies.get('session_id', 'anonymous')
                message = f"{session_id}:{timestamp}"
                expected_signature = hmac.new(
                    self.csrf_secret.encode(),
                    message.encode(),
                    hashlib.sha256
                ).hexdigest()
    
                return hmac.compare_digest(signature, expected_signature)
    
            except (ValueError, TypeError):
                return False
    
        async def validate_request_data(self, request: Request) -> bool:
            """Validate request data for suspicious content"""
    
            # Check query parameters
            for key, value in request.query_params.items():
                if self.contains_suspicious_content(str(value)):
                    return False
    
            # Check headers
            for key, value in request.headers.items():
                if self.contains_suspicious_content(str(value)):
                    return False
    
            # Check body for certain content types
            content_type = request.headers.get('content-type', '')
            if content_type.startswith('application/json'):
                try:
                    body = await request.body()
                    if body and self.contains_suspicious_content(body.decode()):
                        return False
                except Exception:
                    # If we can't read the body, let it through
                    # (it will likely fail later validation)
                    pass
    
            return True
    
        def contains_suspicious_content(self, content: str) -> bool:
            """Check if content contains suspicious patterns"""
            content_lower = content.lower()
    
            for pattern in self.suspicious_patterns:
                if re.search(pattern, content_lower):
                    return True
    
            return False
    
        async def log_security_incident(self, client_ip: str, error: str, request: Request):
            """Log security incidents"""
            incident = {
                'timestamp': datetime.utcnow().isoformat(),
                'client_ip': client_ip,
                'error': error,
                'method': request.method,
                'url': str(request.url),
                'user_agent': request.headers.get('user-agent', ''),
                'headers': dict(request.headers)
            }
    
            # In production, send to security monitoring system
            print(f"Security incident: {incident}")
    
        def block_ip(self, ip_address: str):
            """Block an IP address"""
            self.blocked_ips.add(ip_address)
    
        def unblock_ip(self, ip_address: str):
            """Unblock an IP address"""
            self.blocked_ips.discard(ip_address)
    Python

    XSS Protection

    Create backend/app/security/xss_protection.py:

    import html
    import re
    from typing import Any, Dict, List
    import bleach
    from markupsafe import Markup
    
    class XSSProtection:
        def __init__(self):
            # Allowed HTML tags and attributes for rich content
            self.allowed_tags = [
                'p', 'br', 'strong', 'em', 'u', 'ol', 'ul', 'li',
                'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote'
            ]
    
            self.allowed_attributes = {
                '*': ['class'],
                'a': ['href', 'title'],
                'img': ['src', 'alt', 'width', 'height']
            }
    
            # Dangerous patterns to remove
            self.dangerous_patterns = [
                r'javascript:',
                r'vbscript:',
                r'onload=',
                r'onerror=',
                r'onclick=',
                r'onmouseover=',
                r'onfocus=',
                r'onblur=',
                r'<script',
                r'</script>',
                r'<iframe',
                r'<object',
                r'<embed',
                r'<form',
                r'<input',
                r'<textarea',
                r'<select',
                r'<button'
            ]
    
        def sanitize_html(self, content: str, allow_rich_content: bool = False) -> str:
            """Sanitize HTML content"""
    
            if not content:
                return content
    
            if allow_rich_content:
                # Use bleach for rich content sanitization
                return bleach.clean(
                    content,
                    tags=self.allowed_tags,
                    attributes=self.allowed_attributes,
                    strip=True
                )
            else:
                # Escape all HTML for plain text
                return html.escape(content)
    
        def sanitize_user_input(self, data: Any) -> Any:
            """Recursively sanitize user input"""
    
            if isinstance(data, str):
                return self.sanitize_string(data)
            elif isinstance(data, dict):
                return {key: self.sanitize_user_input(value) for key, value in data.items()}
            elif isinstance(data, list):
                return [self.sanitize_user_input(item) for item in data]
            else:
                return data
    
        def sanitize_string(self, text: str) -> str:
            """Sanitize a string for dangerous content"""
    
            if not text:
                return text
    
            # Remove dangerous patterns
            sanitized = text
            for pattern in self.dangerous_patterns:
                sanitized = re.sub(pattern, '', sanitized, flags=re.IGNORECASE)
    
            # Remove null bytes and control characters
            sanitized = re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]', '', sanitized)
    
            return sanitized
    
        def validate_url(self, url: str) -> bool:
            """Validate URL for safety"""
    
            if not url:
                return True
    
            # Check for dangerous protocols
            dangerous_protocols = ['javascript:', 'vbscript:', 'data:', 'file:']
            url_lower = url.lower().strip()
    
            for protocol in dangerous_protocols:
                if url_lower.startswith(protocol):
                    return False
    
            # Only allow HTTP and HTTPS
            if not (url_lower.startswith('http://') or url_lower.startswith('https://')):
                return False
    
            return True
    
        def create_content_security_policy(self, nonce: str = None) -> str:
            """Create Content Security Policy header"""
    
            policy_parts = [
                "default-src 'self'",
                "script-src 'self' 'unsafe-inline'",
                "style-src 'self' 'unsafe-inline'",
                "img-src 'self' data: https:",
                "font-src 'self'",
                "connect-src 'self'",
                "frame-ancestors 'none'",
                "base-uri 'self'",
                "form-action 'self'"
            ]
    
            if nonce:
                # Add nonce for inline scripts
                policy_parts[1] = f"script-src 'self' 'nonce-{nonce}'"
    
            return "; ".join(policy_parts)
    
    # Global XSS protection instance
    xss_protection = XSSProtection()
    Python

    CSRF Protection

    Create backend/app/security/csrf_protection.py:

    import secrets
    import hmac
    import hashlib
    import time
    from typing import Optional
    from fastapi import Request, HTTPException, status
    
    class CSRFProtection:
        def __init__(self, secret_key: str):
            self.secret_key = secret_key
            self.token_lifetime = 3600  # 1 hour
    
        def generate_csrf_token(self, session_id: str) -> str:
            """Generate CSRF token for session"""
    
            timestamp = str(int(time.time()))
            nonce = secrets.token_urlsafe(16)
    
            # Create message to sign
            message = f"{session_id}:{timestamp}:{nonce}"
    
            # Create signature
            signature = hmac.new(
                self.secret_key.encode(),
                message.encode(),
                hashlib.sha256
            ).hexdigest()
    
            # Return token
            return f"{timestamp}:{nonce}:{signature}"
    
        def validate_csrf_token(self, token: str, session_id: str) -> bool:
            """Validate CSRF token"""
    
            if not token:
                return False
    
            try:
                timestamp_str, nonce, signature = token.split(':')
                timestamp = int(timestamp_str)
    
                # Check token age
                if time.time() - timestamp > self.token_lifetime:
                    return False
    
                # Recreate message
                message = f"{session_id}:{timestamp_str}:{nonce}"
    
                # Verify signature
                expected_signature = hmac.new(
                    self.secret_key.encode(),
                    message.encode(),
                    hashlib.sha256
                ).hexdigest()
    
                return hmac.compare_digest(signature, expected_signature)
    
            except (ValueError, TypeError):
                return False
    
        def get_csrf_token_from_request(self, request: Request) -> Optional[str]:
            """Extract CSRF token from request"""
    
            # Check header first
            token = request.headers.get('X-CSRF-Token')
            if token:
                return token
    
            # Check form data
            if hasattr(request, 'form'):
                form_data = request.form()
                return form_data.get('csrf_token')
    
            return None
    
        def require_csrf_token(self, request: Request, session_id: str):
            """Middleware function to require CSRF token"""
    
            # Skip for safe methods
            if request.method in ['GET', 'HEAD', 'OPTIONS']:
                return
    
            # Skip for API requests with JWT
            auth_header = request.headers.get('Authorization')
            if auth_header and auth_header.startswith('Bearer '):
                return
    
            # Get and validate CSRF token
            csrf_token = self.get_csrf_token_from_request(request)
    
            if not csrf_token or not self.validate_csrf_token(csrf_token, session_id):
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="CSRF token missing or invalid"
                )
    Python

    Secure Headers Implementation

    Create backend/app/security/secure_headers.py:

    from fastapi import Response
    from typing import Dict, Optional
    
    class SecureHeaders:
        def __init__(self):
            self.default_headers = {
                # Prevent MIME type sniffing
                'X-Content-Type-Options': 'nosniff',
    
                # Prevent clickjacking
                'X-Frame-Options': 'DENY',
    
                # XSS protection
                'X-XSS-Protection': '1; mode=block',
    
                # HTTPS enforcement
                'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload',
    
                # Referrer policy
                'Referrer-Policy': 'strict-origin-when-cross-origin',
    
                # Permissions policy
                'Permissions-Policy': 'geolocation=(), microphone=(), camera=()',
    
                # Cache control for sensitive pages
                'Cache-Control': 'no-cache, no-store, must-revalidate',
                'Pragma': 'no-cache',
                'Expires': '0'
            }
    
        def get_csp_header(self, nonce: Optional[str] = None, report_uri: Optional[str] = None) -> str:
            """Generate Content Security Policy header"""
    
            directives = [
                "default-src 'self'",
                "script-src 'self'",
                "style-src 'self' 'unsafe-inline'",
                "img-src 'self' data: https:",
                "font-src 'self'",
                "connect-src 'self'",
                "media-src 'self'",
                "object-src 'none'",
                "child-src 'none'",
                "frame-ancestors 'none'",
                "base-uri 'self'",
                "form-action 'self'",
                "upgrade-insecure-requests"
            ]
    
            # Add nonce for inline scripts if provided
            if nonce:
                directives[1] = f"script-src 'self' 'nonce-{nonce}'"
    
            # Add report URI if provided
            if report_uri:
                directives.append(f"report-uri {report_uri}")
    
            return "; ".join(directives)
    
        def apply_security_headers(
            self, 
            response: Response, 
            csp_nonce: Optional[str] = None,
            custom_headers: Optional[Dict[str, str]] = None
        ) -> Response:
            """Apply all security headers to response"""
    
            # Apply default headers
            for header, value in self.default_headers.items():
                response.headers[header] = value
    
            # Add CSP header
            csp_header = self.get_csp_header(nonce=csp_nonce)
            response.headers['Content-Security-Policy'] = csp_header
    
            # Apply custom headers
            if custom_headers:
                for header, value in custom_headers.items():
                    response.headers[header] = value
    
            return response
    
        def get_cookie_security_attributes(self, is_https: bool = True) -> Dict[str, any]:
            """Get secure cookie attributes"""
    
            attributes = {
                'httponly': True,
                'samesite': 'strict',
                'max_age': 3600  # 1 hour
            }
    
            if is_https:
                attributes['secure'] = True
    
            return attributes
    
    # Global secure headers instance
    secure_headers = SecureHeaders()
    Python

    Security Monitoring

    Create backend/app/security/security_monitor.py:

    import asyncio
    import json
    from datetime import datetime, timedelta
    from typing import Dict, List, Optional
    from collections import defaultdict, deque
    import logging
    
    class SecurityMonitor:
        def __init__(self):
            self.logger = logging.getLogger('security')
    
            # Security events tracking
            self.failed_logins = defaultdict(deque)
            self.suspicious_activities = defaultdict(deque)
            self.blocked_ips = set()
    
            # Thresholds
            self.max_failed_logins = 5
            self.failed_login_window = 300  # 5 minutes
            self.suspicious_activity_threshold = 10
            self.monitoring_window = 3600  # 1 hour
    
        async def log_failed_login(self, ip_address: str, username: str, user_agent: str):
            """Log failed login attempt"""
    
            event = {
                'timestamp': datetime.utcnow().isoformat(),
                'ip_address': ip_address,
                'username': username,
                'user_agent': user_agent,
                'event_type': 'failed_login'
            }
    
            self.logger.warning(f"Failed login attempt: {json.dumps(event)}")
    
            # Track failed logins
            current_time = datetime.utcnow()
            self.failed_logins[ip_address].append(current_time)
    
            # Clean old entries
            cutoff_time = current_time - timedelta(seconds=self.failed_login_window)
            while (self.failed_logins[ip_address] and 
                   self.failed_logins[ip_address][0] < cutoff_time):
                self.failed_logins[ip_address].popleft()
    
            # Check if IP should be blocked
            if len(self.failed_logins[ip_address]) >= self.max_failed_logins:
                await self.block_ip(ip_address, reason="Too many failed login attempts")
    
        async def log_suspicious_activity(self, ip_address: str, activity: str, details: Dict):
            """Log suspicious activity"""
    
            event = {
                'timestamp': datetime.utcnow().isoformat(),
                'ip_address': ip_address,
                'activity': activity,
                'details': details,
                'event_type': 'suspicious_activity'
            }
    
            self.logger.warning(f"Suspicious activity: {json.dumps(event)}")
    
            # Track suspicious activities
            current_time = datetime.utcnow()
            self.suspicious_activities[ip_address].append(current_time)
    
            # Clean old entries
            cutoff_time = current_time - timedelta(seconds=self.monitoring_window)
            while (self.suspicious_activities[ip_address] and 
                   self.suspicious_activities[ip_address][0] < cutoff_time):
                self.suspicious_activities[ip_address].popleft()
    
            # Check threshold
            if len(self.suspicious_activities[ip_address]) >= self.suspicious_activity_threshold:
                await self.block_ip(ip_address, reason="Suspicious activity pattern detected")
    
        async def log_security_event(self, event_type: str, details: Dict):
            """Log general security event"""
    
            event = {
                'timestamp': datetime.utcnow().isoformat(),
                'event_type': event_type,
                'details': details
            }
    
            self.logger.info(f"Security event: {json.dumps(event)}")
    
        async def block_ip(self, ip_address: str, reason: str, duration: int = 3600):
            """Block IP address"""
    
            self.blocked_ips.add(ip_address)
    
            event = {
                'timestamp': datetime.utcnow().isoformat(),
                'ip_address': ip_address,
                'reason': reason,
                'duration': duration,
                'event_type': 'ip_blocked'
            }
    
            self.logger.error(f"IP blocked: {json.dumps(event)}")
    
            # Schedule unblocking
            asyncio.create_task(self._unblock_ip_after_delay(ip_address, duration))
    
        async def _unblock_ip_after_delay(self, ip_address: str, delay: int):
            """Unblock IP after delay"""
            await asyncio.sleep(delay)
            self.blocked_ips.discard(ip_address)
    
            event = {
                'timestamp': datetime.utcnow().isoformat(),
                'ip_address': ip_address,
                'event_type': 'ip_unblocked'
            }
    
            self.logger.info(f"IP unblocked: {json.dumps(event)}")
    
        def is_ip_blocked(self, ip_address: str) -> bool:
            """Check if IP is blocked"""
            return ip_address in self.blocked_ips
    
        async def get_security_stats(self) -> Dict:
            """Get security statistics"""
    
            current_time = datetime.utcnow()
            hour_ago = current_time - timedelta(hours=1)
    
            # Count recent events
            recent_failed_logins = sum(
                len([t for t in times if t > hour_ago])
                for times in self.failed_logins.values()
            )
    
            recent_suspicious = sum(
                len([t for t in times if t > hour_ago])
                for times in self.suspicious_activities.values()
            )
    
            return {
                'blocked_ips': len(self.blocked_ips),
                'recent_failed_logins': recent_failed_logins,
                'recent_suspicious_activities': recent_suspicious,
                'monitoring_since': (current_time - timedelta(hours=1)).isoformat()
            }
    
    # Global security monitor instance
    security_monitor = SecurityMonitor()
    Python

    This comprehensive security implementation provides:

    1. Middleware Protection: Rate limiting, CSRF, input validation, and security headers
    2. XSS Prevention: Content sanitization and CSP headers
    3. CSRF Protection: Token-based CSRF protection for state-changing requests
    4. Secure Headers: Comprehensive security headers for all responses
    5. Security Monitoring: Real-time monitoring of security events and automatic IP blocking
    6. Attack Prevention: Protection against common web vulnerabilities

    The system provides defense-in-depth security while maintaining good user experience and performance.


    16. Complete Project Examples

    This chapter provides complete, production-ready examples that combine all the concepts covered in this guide.

    E-commerce Platform with JWT Authentication

    Project Structure

    ecommerce-jwt/
    ├── backend/
       ├── app/
          ├── __init__.py
          ├── main.py
          ├── config.py
          ├── database.py
          ├── models/
             ├── __init__.py
             ├── user.py
             ├── product.py
             └── order.py
          ├── auth/
             ├── __init__.py
             ├── jwt_handler.py
             ├── permissions.py
             └── oauth.py
          ├── routers/
             ├── __init__.py
             ├── auth.py
             ├── users.py
             ├── products.py
             └── orders.py
          └── security/
              ├── __init__.py
              ├── middleware.py
              └── validators.py
       ├── requirements.txt
       ├── .env
       └── docker-compose.yml
    └── frontend/
        ├── index.html
        ├── login.html
        ├── dashboard.html
        ├── css/
           └── styles.css
        ├── js/
           ├── api.js
           ├── auth.js
           ├── products.js
           └── orders.js
        └── assets/
            └── images/
    Bash

    Backend Implementation

    Complete main.py:

    from fastapi import FastAPI, Depends, HTTPException, status
    from fastapi.middleware.cors import CORSMiddleware
    from fastapi.security import HTTPBearer
    from contextlib import asynccontextmanager
    import uvicorn
    
    from app.config import settings
    from app.database import engine, Base, get_db
    from app.auth.jwt_handler import jwt_handler
    from app.auth.token_blacklist import token_blacklist
    from app.security.security_middleware import SecurityMiddleware
    from app.routers import auth, users, products, orders
    from app.models import user, product, order
    
    # Create tables
    @asynccontextmanager
    async def lifespan(app: FastAPI):
        # Startup
        Base.metadata.create_all(bind=engine)
        await token_blacklist.initialize()
        yield
        # Shutdown
        pass
    
    app = FastAPI(
        title="E-commerce JWT API",
        description="Complete e-commerce platform with JWT authentication",
        version="1.0.0",
        lifespan=lifespan
    )
    
    # Security middleware
    app.add_middleware(
        SecurityMiddleware,
        config={
            'rate_limit_requests': 100,
            'rate_limit_window': 3600,
            'enforce_https': settings.ENVIRONMENT == 'production',
            'csrf_secret': settings.SECRET_KEY
        }
    )
    
    # CORS middleware
    app.add_middleware(
        CORSMiddleware,
        allow_origins=settings.ALLOWED_ORIGINS,
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )
    
    # Include routers
    app.include_router(auth.router, prefix="/api/auth", tags=["authentication"])
    app.include_router(users.router, prefix="/api/users", tags=["users"])
    app.include_router(products.router, prefix="/api/products", tags=["products"])
    app.include_router(orders.router, prefix="/api/orders", tags=["orders"])
    
    # Health check
    @app.get("/health")
    async def health_check():
        return {
            "status": "healthy",
            "environment": settings.ENVIRONMENT,
            "version": "1.0.0"
        }
    
    # Root endpoint
    @app.get("/")
    async def root():
        return {
            "message": "E-commerce JWT API",
            "docs": "/docs",
            "version": "1.0.0"
        }
    
    if __name__ == "__main__":
        uvicorn.run(
            "main:app",
            host="0.0.0.0",
            port=8000,
            reload=settings.ENVIRONMENT == "development"
        )
    Python

    Product Model (app/models/product.py):

    from sqlalchemy import Column, Integer, String, Float, Text, DateTime, Boolean, ForeignKey
    from sqlalchemy.orm import relationship
    from sqlalchemy.sql import func
    from app.database import Base
    
    class Product(Base):
        __tablename__ = "products"
    
        id = Column(Integer, primary_key=True, index=True)
        name = Column(String(255), nullable=False, index=True)
        description = Column(Text)
        price = Column(Float, nullable=False)
        category_id = Column(Integer, ForeignKey("categories.id"))
        stock_quantity = Column(Integer, default=0)
        sku = Column(String(100), unique=True, index=True)
        is_active = Column(Boolean, default=True)
        created_at = Column(DateTime(timezone=True), server_default=func.now())
        updated_at = Column(DateTime(timezone=True), onupdate=func.now())
    
        # Relationships
        category = relationship("Category", back_populates="products")
        order_items = relationship("OrderItem", back_populates="product")
    
    class Category(Base):
        __tablename__ = "categories"
    
        id = Column(Integer, primary_key=True, index=True)
        name = Column(String(100), nullable=False, unique=True)
        description = Column(Text)
        parent_id = Column(Integer, ForeignKey("categories.id"))
        is_active = Column(Boolean, default=True)
        created_at = Column(DateTime(timezone=True), server_default=func.now())
    
        # Relationships
        products = relationship("Product", back_populates="category")
        parent = relationship("Category", remote_side=[id])
    Python

    Order Model (app/models/order.py):

    from sqlalchemy import Column, Integer, String, Float, DateTime, Boolean, ForeignKey, Enum
    from sqlalchemy.orm import relationship
    from sqlalchemy.sql import func
    from app.database import Base
    import enum
    
    class OrderStatus(enum.Enum):
        PENDING = "pending"
        CONFIRMED = "confirmed"
        PROCESSING = "processing"
        SHIPPED = "shipped"
        DELIVERED = "delivered"
        CANCELLED = "cancelled"
    
    class Order(Base):
        __tablename__ = "orders"
    
        id = Column(Integer, primary_key=True, index=True)
        user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
        order_number = Column(String(50), unique=True, index=True)
        status = Column(Enum(OrderStatus), default=OrderStatus.PENDING)
        total_amount = Column(Float, nullable=False)
        shipping_address = Column(String(500))
        billing_address = Column(String(500))
        payment_method = Column(String(50))
        payment_status = Column(String(50), default="pending")
        created_at = Column(DateTime(timezone=True), server_default=func.now())
        updated_at = Column(DateTime(timezone=True), onupdate=func.now())
    
        # Relationships
        user = relationship("User", back_populates="orders")
        order_items = relationship("OrderItem", back_populates="order", cascade="all, delete-orphan")
    
    class OrderItem(Base):
        __tablename__ = "order_items"
    
        id = Column(Integer, primary_key=True, index=True)
        order_id = Column(Integer, ForeignKey("orders.id"), nullable=False)
        product_id = Column(Integer, ForeignKey("products.id"), nullable=False)
        quantity = Column(Integer, nullable=False)
        unit_price = Column(Float, nullable=False)
        total_price = Column(Float, nullable=False)
    
        # Relationships
        order = relationship("Order", back_populates="order_items")
        product = relationship("Product", back_populates="order_items")
    Python

    Products Router (app/routers/products.py):

    from fastapi import APIRouter, Depends, HTTPException, status, Query
    from sqlalchemy.orm import Session
    from typing import List, Optional
    from app.database import get_db
    from app.models.product import Product, Category
    from app.auth.permissions import RequireAuthentication, RequireAdmin
    from app.schemas.product import ProductCreate, ProductUpdate, ProductResponse, CategoryResponse
    import uuid
    
    router = APIRouter()
    
    @router.get("/", response_model=List[ProductResponse])
    async def get_products(
        skip: int = Query(0, ge=0),
        limit: int = Query(100, ge=1, le=100),
        category_id: Optional[int] = None,
        search: Optional[str] = None,
        min_price: Optional[float] = None,
        max_price: Optional[float] = None,
        db: Session = Depends(get_db)
    ):
        """Get products with filtering and pagination"""
    
        query = db.query(Product).filter(Product.is_active == True)
    
        # Apply filters
        if category_id:
            query = query.filter(Product.category_id == category_id)
    
        if search:
            query = query.filter(Product.name.contains(search))
    
        if min_price is not None:
            query = query.filter(Product.price >= min_price)
    
        if max_price is not None:
            query = query.filter(Product.price <= max_price)
    
        # Pagination
        products = query.offset(skip).limit(limit).all()
    
        return products
    
    @router.get("/{product_id}", response_model=ProductResponse)
    async def get_product(product_id: int, db: Session = Depends(get_db)):
        """Get product by ID"""
    
        product = db.query(Product).filter(
            Product.id == product_id,
            Product.is_active == True
        ).first()
    
        if not product:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Product not found"
            )
    
        return product
    
    @router.post("/", response_model=ProductResponse, status_code=status.HTTP_201_CREATED)
    async def create_product(
        product: ProductCreate,
        db: Session = Depends(get_db),
        current_user = Depends(RequireAdmin)
    ):
        """Create new product (admin only)"""
    
        # Generate SKU if not provided
        if not product.sku:
            product.sku = f"SKU-{uuid.uuid4().hex[:8].upper()}"
    
        # Check if SKU already exists
        existing_product = db.query(Product).filter(Product.sku == product.sku).first()
        if existing_product:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Product with this SKU already exists"
            )
    
        db_product = Product(**product.dict())
        db.add(db_product)
        db.commit()
        db.refresh(db_product)
    
        return db_product
    
    @router.put("/{product_id}", response_model=ProductResponse)
    async def update_product(
        product_id: int,
        product_update: ProductUpdate,
        db: Session = Depends(get_db),
        current_user = Depends(RequireAdmin)
    ):
        """Update product (admin only)"""
    
        db_product = db.query(Product).filter(Product.id == product_id).first()
        if not db_product:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Product not found"
            )
    
        # Update fields
        update_data = product_update.dict(exclude_unset=True)
        for field, value in update_data.items():
            setattr(db_product, field, value)
    
        db.commit()
        db.refresh(db_product)
    
        return db_product
    
    @router.delete("/{product_id}")
    async def delete_product(
        product_id: int,
        db: Session = Depends(get_db),
        current_user = Depends(RequireAdmin)
    ):
        """Soft delete product (admin only)"""
    
        db_product = db.query(Product).filter(Product.id == product_id).first()
        if not db_product:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Product not found"
            )
    
        db_product.is_active = False
        db.commit()
    
        return {"message": "Product deleted successfully"}
    
    @router.get("/categories/", response_model=List[CategoryResponse])
    async def get_categories(db: Session = Depends(get_db)):
        """Get all product categories"""
    
        categories = db.query(Category).filter(Category.is_active == True).all()
        return categories
    
    @router.post("/categories/", response_model=CategoryResponse, status_code=status.HTTP_201_CREATED)
    async def create_category(
        category: CategoryCreate,
        db: Session = Depends(get_db),
        current_user = Depends(RequireAdmin)
    ):
        """Create new category (admin only)"""
    
        db_category = Category(**category.dict())
        db.add(db_category)
        db.commit()
        db.refresh(db_category)
    
        return db_category
    Python

    Orders Router (app/routers/orders.py):

    from fastapi import APIRouter, Depends, HTTPException, status, Query
    from sqlalchemy.orm import Session
    from typing import List, Optional
    from app.database import get_db
    from app.models.order import Order, OrderItem, OrderStatus
    from app.models.product import Product
    from app.auth.permissions import RequireAuthentication, RequireAdmin
    from app.schemas.order import OrderCreate, OrderResponse, OrderItemCreate
    import uuid
    from datetime import datetime
    
    router = APIRouter()
    
    @router.get("/", response_model=List[OrderResponse])
    async def get_orders(
        skip: int = Query(0, ge=0),
        limit: int = Query(50, ge=1, le=100),
        status: Optional[OrderStatus] = None,
        db: Session = Depends(get_db),
        current_user = Depends(RequireAuthentication)
    ):
        """Get user's orders"""
    
        query = db.query(Order).filter(Order.user_id == current_user.id)
    
        if status:
            query = query.filter(Order.status == status)
    
        orders = query.order_by(Order.created_at.desc()).offset(skip).limit(limit).all()
        return orders
    
    @router.get("/admin/all", response_model=List[OrderResponse])
    async def get_all_orders(
        skip: int = Query(0, ge=0),
        limit: int = Query(50, ge=1, le=100),
        status: Optional[OrderStatus] = None,
        user_id: Optional[int] = None,
        db: Session = Depends(get_db),
        current_user = Depends(RequireAdmin)
    ):
        """Get all orders (admin only)"""
    
        query = db.query(Order)
    
        if status:
            query = query.filter(Order.status == status)
    
        if user_id:
            query = query.filter(Order.user_id == user_id)
    
        orders = query.order_by(Order.created_at.desc()).offset(skip).limit(limit).all()
        return orders
    
    @router.get("/{order_id}", response_model=OrderResponse)
    async def get_order(
        order_id: int,
        db: Session = Depends(get_db),
        current_user = Depends(RequireAuthentication)
    ):
        """Get specific order"""
    
        order = db.query(Order).filter(Order.id == order_id).first()
    
        if not order:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Order not found"
            )
    
        # Users can only see their own orders unless admin
        if order.user_id != current_user.id and current_user.role != "admin":
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Access denied"
            )
    
        return order
    
    @router.post("/", response_model=OrderResponse, status_code=status.HTTP_201_CREATED)
    async def create_order(
        order_data: OrderCreate,
        db: Session = Depends(get_db),
        current_user = Depends(RequireAuthentication)
    ):
        """Create new order"""
    
        # Validate products and calculate total
        total_amount = 0
        order_items_data = []
    
        for item in order_data.items:
            product = db.query(Product).filter(
                Product.id == item.product_id,
                Product.is_active == True
            ).first()
    
            if not product:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail=f"Product {item.product_id} not found"
                )
    
            if product.stock_quantity < item.quantity:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail=f"Insufficient stock for product {product.name}"
                )
    
            item_total = product.price * item.quantity
            total_amount += item_total
    
            order_items_data.append({
                "product_id": product.id,
                "quantity": item.quantity,
                "unit_price": product.price,
                "total_price": item_total
            })
    
        # Create order
        order_number = f"ORD-{datetime.utcnow().strftime('%Y%m%d')}-{uuid.uuid4().hex[:8].upper()}"
    
        db_order = Order(
            user_id=current_user.id,
            order_number=order_number,
            total_amount=total_amount,
            shipping_address=order_data.shipping_address,
            billing_address=order_data.billing_address,
            payment_method=order_data.payment_method
        )
    
        db.add(db_order)
        db.flush()  # Get order ID
    
        # Create order items
        for item_data in order_items_data:
            db_order_item = OrderItem(
                order_id=db_order.id,
                **item_data
            )
            db.add(db_order_item)
    
        # Update product stock
        for item in order_data.items:
            product = db.query(Product).filter(Product.id == item.product_id).first()
            product.stock_quantity -= item.quantity
    
        db.commit()
        db.refresh(db_order)
    
        return db_order
    
    @router.put("/{order_id}/status", response_model=OrderResponse)
    async def update_order_status(
        order_id: int,
        new_status: OrderStatus,
        db: Session = Depends(get_db),
        current_user = Depends(RequireAdmin)
    ):
        """Update order status (admin only)"""
    
        order = db.query(Order).filter(Order.id == order_id).first()
    
        if not order:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Order not found"
            )
    
        order.status = new_status
        db.commit()
        db.refresh(order)
    
        return order
    
    @router.post("/{order_id}/cancel")
    async def cancel_order(
        order_id: int,
        db: Session = Depends(get_db),
        current_user = Depends(RequireAuthentication)
    ):
        """Cancel order"""
    
        order = db.query(Order).filter(Order.id == order_id).first()
    
        if not order:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Order not found"
            )
    
        # Users can only cancel their own orders
        if order.user_id != current_user.id and current_user.role != "admin":
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Access denied"
            )
    
        # Can only cancel pending or confirmed orders
        if order.status not in [OrderStatus.PENDING, OrderStatus.CONFIRMED]:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Order cannot be cancelled"
            )
    
        # Restore product stock
        for item in order.order_items:
            product = db.query(Product).filter(Product.id == item.product_id).first()
            if product:
                product.stock_quantity += item.quantity
    
        order.status = OrderStatus.CANCELLED
        db.commit()
    
        return {"message": "Order cancelled successfully"}
    Python

    Frontend Implementation

    Complete Dashboard (frontend/dashboard.html):

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>E-commerce Dashboard</title>
        <link rel="stylesheet" href="css/styles.css">
    </head>
    <body>
        <!-- Navigation -->
        <nav class="navbar">
            <div class="nav-container">
                <div class="nav-brand">
                    <h2>E-Commerce</h2>
                </div>
                <div class="nav-menu">
                    <a href="#products" class="nav-link" data-section="products">Products</a>
                    <a href="#orders" class="nav-link" data-section="orders">Orders</a>
                    <a href="#profile" class="nav-link" data-section="profile">Profile</a>
                    <div class="nav-user">
                        <span id="user-name"></span>
                        <button id="logout-btn" class="btn btn-outline">Logout</button>
                    </div>
                </div>
            </div>
        </nav>
    
        <!-- Main Content -->
        <main class="main-content">
            <!-- Products Section -->
            <section id="products-section" class="content-section active">
                <div class="section-header">
                    <h2>Products</h2>
                    <div class="section-controls">
                        <div class="search-box">
                            <input type="text" id="product-search" placeholder="Search products...">
                            <button id="search-btn" class="btn btn-primary">Search</button>
                        </div>
                        <div class="filter-box">
                            <select id="category-filter">
                                <option value="">All Categories</option>
                            </select>
                            <input type="number" id="min-price" placeholder="Min Price">
                            <input type="number" id="max-price" placeholder="Max Price">
                            <button id="filter-btn" class="btn btn-secondary">Filter</button>
                        </div>
                    </div>
                </div>
    
                <div id="products-grid" class="products-grid">
                    <!-- Products will be loaded here -->
                </div>
    
                <!-- Pagination -->
                <div id="products-pagination" class="pagination">
                    <!-- Pagination controls will be added here -->
                </div>
            </section>
    
            <!-- Orders Section -->
            <section id="orders-section" class="content-section">
                <div class="section-header">
                    <h2>My Orders</h2>
                    <div class="section-controls">
                        <select id="order-status-filter">
                            <option value="">All Orders</option>
                            <option value="pending">Pending</option>
                            <option value="confirmed">Confirmed</option>
                            <option value="processing">Processing</option>
                            <option value="shipped">Shipped</option>
                            <option value="delivered">Delivered</option>
                            <option value="cancelled">Cancelled</option>
                        </select>
                    </div>
                </div>
    
                <div id="orders-list" class="orders-list">
                    <!-- Orders will be loaded here -->
                </div>
            </section>
    
            <!-- Profile Section -->
            <section id="profile-section" class="content-section">
                <div class="section-header">
                    <h2>Profile</h2>
                </div>
    
                <div class="profile-content">
                    <form id="profile-form" class="form">
                        <div class="form-group">
                            <label for="profile-email">Email</label>
                            <input type="email" id="profile-email" readonly>
                        </div>
                        <div class="form-group">
                            <label for="profile-fullname">Full Name</label>
                            <input type="text" id="profile-fullname" required>
                        </div>
                        <div class="form-group">
                            <label for="profile-phone">Phone</label>
                            <input type="tel" id="profile-phone">
                        </div>
                        <div class="form-group">
                            <label for="profile-address">Address</label>
                            <textarea id="profile-address"></textarea>
                        </div>
                        <button type="submit" class="btn btn-primary">Update Profile</button>
                    </form>
    
                    <div class="profile-actions">
                        <h3>Account Actions</h3>
                        <button id="change-password-btn" class="btn btn-secondary">Change Password</button>
                        <button id="download-data-btn" class="btn btn-outline">Download My Data</button>
                        <button id="delete-account-btn" class="btn btn-danger">Delete Account</button>
                    </div>
                </div>
            </section>
        </main>
    
        <!-- Shopping Cart Modal -->
        <div id="cart-modal" class="modal">
            <div class="modal-content">
                <span class="close">×</span>
                <h2>Shopping Cart</h2>
                <div id="cart-items"></div>
                <div class="cart-total">
                    <strong>Total: $<span id="cart-total">0.00</span></strong>
                </div>
                <div class="cart-actions">
                    <button id="clear-cart-btn" class="btn btn-secondary">Clear Cart</button>
                    <button id="checkout-btn" class="btn btn-primary">Checkout</button>
                </div>
            </div>
        </div>
    
        <!-- Checkout Modal -->
        <div id="checkout-modal" class="modal">
            <div class="modal-content">
                <span class="close">×</span>
                <h2>Checkout</h2>
                <form id="checkout-form" class="form">
                    <div class="form-group">
                        <label for="shipping-address">Shipping Address</label>
                        <textarea id="shipping-address" required></textarea>
                    </div>
                    <div class="form-group">
                        <label for="billing-address">Billing Address</label>
                        <textarea id="billing-address" required></textarea>
                    </div>
                    <div class="form-group">
                        <label for="payment-method">Payment Method</label>
                        <select id="payment-method" required>
                            <option value="">Select Payment Method</option>
                            <option value="credit_card">Credit Card</option>
                            <option value="debit_card">Debit Card</option>
                            <option value="paypal">PayPal</option>
                            <option value="bank_transfer">Bank Transfer</option>
                        </select>
                    </div>
                    <div class="order-summary">
                        <h3>Order Summary</h3>
                        <div id="checkout-items"></div>
                        <div class="checkout-total">
                            <strong>Total: $<span id="checkout-total">0.00</span></strong>
                        </div>
                    </div>
                    <button type="submit" class="btn btn-primary">Place Order</button>
                </form>
            </div>
        </div>
    
        <!-- Cart Button -->
        <div class="cart-button">
            <button id="cart-btn" class="btn btn-primary">
                Cart (<span id="cart-count">0</span>)
            </button>
        </div>
    
        <script src="js/api.js"></script>
        <script src="js/auth.js"></script>
        <script src="js/products.js"></script>
        <script src="js/orders.js"></script>
        <script src="js/dashboard.js"></script>
    </body>
    </html>
    HTML

    Complete Products JavaScript (frontend/js/products.js):

    /**
     * Products Management
     */
    class ProductsManager {
        constructor(apiClient) {
            this.api = apiClient;
            this.products = [];
            this.categories = [];
            this.cart = JSON.parse(localStorage.getItem('cart')) || [];
            this.currentPage = 1;
            this.itemsPerPage = 12;
            this.totalProducts = 0;
    
            this.initializeEventListeners();
            this.loadCategories();
            this.loadProducts();
            this.updateCartDisplay();
        }
    
        initializeEventListeners() {
            // Search and filter
            document.getElementById('search-btn').addEventListener('click', () => this.searchProducts());
            document.getElementById('filter-btn').addEventListener('click', () => this.filterProducts());
            document.getElementById('product-search').addEventListener('keypress', (e) => {
                if (e.key === 'Enter') this.searchProducts();
            });
    
            // Cart
            document.getElementById('cart-btn').addEventListener('click', () => this.showCart());
            document.getElementById('clear-cart-btn').addEventListener('click', () => this.clearCart());
            document.getElementById('checkout-btn').addEventListener('click', () => this.showCheckout());
    
            // Checkout
            document.getElementById('checkout-form').addEventListener('submit', (e) => this.processCheckout(e));
    
            // Modal close buttons
            document.querySelectorAll('.modal .close').forEach(closeBtn => {
                closeBtn.addEventListener('click', (e) => {
                    e.target.closest('.modal').style.display = 'none';
                });
            });
    
            // Close modals when clicking outside
            window.addEventListener('click', (e) => {
                if (e.target.classList.contains('modal')) {
                    e.target.style.display = 'none';
                }
            });
        }
    
        async loadCategories() {
            try {
                const categories = await this.api.get('/products/categories/');
                this.categories = categories;
                this.renderCategoryFilter();
            } catch (error) {
                console.error('Failed to load categories:', error);
            }
        }
    
        renderCategoryFilter() {
            const categoryFilter = document.getElementById('category-filter');
            categoryFilter.innerHTML = '<option value="">All Categories</option>';
    
            this.categories.forEach(category => {
                const option = document.createElement('option');
                option.value = category.id;
                option.textContent = category.name;
                categoryFilter.appendChild(option);
            });
        }
    
        async loadProducts(page = 1) {
            try {
                const params = new URLSearchParams({
                    skip: (page - 1) * this.itemsPerPage,
                    limit: this.itemsPerPage
                });
    
                // Add filters
                const categoryFilter = document.getElementById('category-filter').value;
                const searchQuery = document.getElementById('product-search').value;
                const minPrice = document.getElementById('min-price').value;
                const maxPrice = document.getElementById('max-price').value;
    
                if (categoryFilter) params.append('category_id', categoryFilter);
                if (searchQuery) params.append('search', searchQuery);
                if (minPrice) params.append('min_price', minPrice);
                if (maxPrice) params.append('max_price', maxPrice);
    
                const products = await this.api.get(`/products/?${params}`);
                this.products = products;
                this.currentPage = page;
    
                this.renderProducts();
                this.renderPagination();
            } catch (error) {
                console.error('Failed to load products:', error);
                this.showError('Failed to load products');
            }
        }
    
        renderProducts() {
            const productsGrid = document.getElementById('products-grid');
    
            if (this.products.length === 0) {
                productsGrid.innerHTML = '<div class="no-products">No products found</div>';
                return;
            }
    
            productsGrid.innerHTML = this.products.map(product => `
                <div class="product-card" data-product-id="${product.id}">
                    <div class="product-image">
                        <img src="/api/products/${product.id}/image" alt="${product.name}" 
                             onerror="this.src='/assets/images/no-image.png'">
                    </div>
                    <div class="product-info">
                        <h3 class="product-name">${product.name}</h3>
                        <p class="product-description">${product.description || ''}</p>
                        <div class="product-price">$${product.price.toFixed(2)}</div>
                        <div class="product-stock">
                            ${product.stock_quantity > 0 
                                ? `In Stock (${product.stock_quantity})` 
                                : 'Out of Stock'
                            }
                        </div>
                    </div>
                    <div class="product-actions">
                        <div class="quantity-controls">
                            <button class="quantity-btn" onclick="this.nextElementSibling.stepDown()">-</button>
                            <input type="number" class="quantity-input" value="1" min="1" max="${product.stock_quantity}">
                            <button class="quantity-btn" onclick="this.previousElementSibling.stepUp()">+</button>
                        </div>
                        <button class="add-to-cart-btn btn btn-primary" 
                                ${product.stock_quantity === 0 ? 'disabled' : ''}
                                onclick="productsManager.addToCart(${product.id})">
                            Add to Cart
                        </button>
                    </div>
                </div>
            `).join('');
        }
    
        renderPagination() {
            const pagination = document.getElementById('products-pagination');
            const totalPages = Math.ceil(this.totalProducts / this.itemsPerPage);
    
            if (totalPages <= 1) {
                pagination.innerHTML = '';
                return;
            }
    
            let paginationHTML = '';
    
            // Previous button
            if (this.currentPage > 1) {
                paginationHTML += `<button class="pagination-btn" onclick="productsManager.loadProducts(${this.currentPage - 1})">Previous</button>`;
            }
    
            // Page numbers
            for (let i = 1; i <= totalPages; i++) {
                if (i === this.currentPage) {
                    paginationHTML += `<button class="pagination-btn active">${i}</button>`;
                } else {
                    paginationHTML += `<button class="pagination-btn" onclick="productsManager.loadProducts(${i})">${i}</button>`;
                }
            }
    
            // Next button
            if (this.currentPage < totalPages) {
                paginationHTML += `<button class="pagination-btn" onclick="productsManager.loadProducts(${this.currentPage + 1})">Next</button>`;
            }
    
            pagination.innerHTML = paginationHTML;
        }
    
        searchProducts() {
            this.loadProducts(1);
        }
    
        filterProducts() {
            this.loadProducts(1);
        }
    
        addToCart(productId) {
            const product = this.products.find(p => p.id === productId);
            if (!product) return;
    
            const productCard = document.querySelector(`[data-product-id="${productId}"]`);
            const quantity = parseInt(productCard.querySelector('.quantity-input').value);
    
            const existingItem = this.cart.find(item => item.product_id === productId);
    
            if (existingItem) {
                existingItem.quantity += quantity;
            } else {
                this.cart.push({
                    product_id: productId,
                    product: product,
                    quantity: quantity,
                    unit_price: product.price
                });
            }
    
            this.saveCart();
            this.updateCartDisplay();
            this.showSuccessMessage('Product added to cart');
        }
    
        removeFromCart(productId) {
            this.cart = this.cart.filter(item => item.product_id !== productId);
            this.saveCart();
            this.updateCartDisplay();
            this.renderCart();
        }
    
        updateCartQuantity(productId, quantity) {
            const item = this.cart.find(item => item.product_id === productId);
            if (item) {
                item.quantity = Math.max(1, quantity);
                this.saveCart();
                this.updateCartDisplay();
                this.renderCart();
            }
        }
    
        clearCart() {
            this.cart = [];
            this.saveCart();
            this.updateCartDisplay();
            this.renderCart();
        }
    
        saveCart() {
            localStorage.setItem('cart', JSON.stringify(this.cart));
        }
    
        updateCartDisplay() {
            const cartCount = this.cart.reduce((total, item) => total + item.quantity, 0);
            document.getElementById('cart-count').textContent = cartCount;
        }
    
        showCart() {
            this.renderCart();
            document.getElementById('cart-modal').style.display = 'block';
        }
    
        renderCart() {
            const cartItems = document.getElementById('cart-items');
            const cartTotal = document.getElementById('cart-total');
    
            if (this.cart.length === 0) {
                cartItems.innerHTML = '<div class="empty-cart">Your cart is empty</div>';
                cartTotal.textContent = '0.00';
                return;
            }
    
            const total = this.cart.reduce((sum, item) => sum + (item.unit_price * item.quantity), 0);
    
            cartItems.innerHTML = this.cart.map(item => `
                <div class="cart-item">
                    <div class="cart-item-info">
                        <h4>${item.product.name}</h4>
                        <p>$${item.unit_price.toFixed(2)} each</p>
                    </div>
                    <div class="cart-item-controls">
                        <div class="quantity-controls">
                            <button onclick="productsManager.updateCartQuantity(${item.product_id}, ${item.quantity - 1})">-</button>
                            <span>${item.quantity}</span>
                            <button onclick="productsManager.updateCartQuantity(${item.product_id}, ${item.quantity + 1})">+</button>
                        </div>
                        <div class="cart-item-total">$${(item.unit_price * item.quantity).toFixed(2)}</div>
                        <button class="remove-btn" onclick="productsManager.removeFromCart(${item.product_id})">Remove</button>
                    </div>
                </div>
            `).join('');
    
            cartTotal.textContent = total.toFixed(2);
        }
    
        showCheckout() {
            if (this.cart.length === 0) {
                this.showError('Your cart is empty');
                return;
            }
    
            this.renderCheckoutSummary();
            document.getElementById('cart-modal').style.display = 'none';
            document.getElementById('checkout-modal').style.display = 'block';
        }
    
        renderCheckoutSummary() {
            const checkoutItems = document.getElementById('checkout-items');
            const checkoutTotal = document.getElementById('checkout-total');
    
            const total = this.cart.reduce((sum, item) => sum + (item.unit_price * item.quantity), 0);
    
            checkoutItems.innerHTML = this.cart.map(item => `
                <div class="checkout-item">
                    <span>${item.product.name} x ${item.quantity}</span>
                    <span>$${(item.unit_price * item.quantity).toFixed(2)}</span>
                </div>
            `).join('');
    
            checkoutTotal.textContent = total.toFixed(2);
        }
    
        async processCheckout(event) {
            event.preventDefault();
    
            const formData = new FormData(event.target);
            const orderData = {
                items: this.cart.map(item => ({
                    product_id: item.product_id,
                    quantity: item.quantity
                })),
                shipping_address: formData.get('shipping-address'),
                billing_address: formData.get('billing-address'),
                payment_method: formData.get('payment-method')
            };
    
            try {
                const response = await this.api.post('/orders/', orderData);
    
                // Clear cart
                this.clearCart();
    
                // Close modal
                document.getElementById('checkout-modal').style.display = 'none';
    
                // Show success message
                this.showSuccessMessage(`Order placed successfully! Order #${response.order_number}`);
    
                // Refresh products to update stock
                this.loadProducts(this.currentPage);
    
            } catch (error) {
                console.error('Checkout failed:', error);
                this.showError('Failed to place order. Please try again.');
            }
        }
    
        showSuccessMessage(message) {
            // Create and show success notification
            const notification = document.createElement('div');
            notification.className = 'notification success';
            notification.textContent = message;
            document.body.appendChild(notification);
    
            setTimeout(() => {
                notification.remove();
            }, 3000);
        }
    
        showError(message) {
            // Create and show error notification
            const notification = document.createElement('div');
            notification.className = 'notification error';
            notification.textContent = message;
            document.body.appendChild(notification);
    
            setTimeout(() => {
                notification.remove();
            }, 3000);
        }
    }
    
    // Initialize when DOM is loaded
    document.addEventListener('DOMContentLoaded', () => {
        window.productsManager = new ProductsManager(api);
    });
    JavaScript

    This complete e-commerce example demonstrates:

    1. Full-Stack Integration: Complete JWT authentication with frontend/backend
    2. Production Security: CSRF protection, XSS prevention, rate limiting
    3. Real-World Features: Shopping cart, orders, user management, admin functions
    4. Scalable Architecture: Modular design with proper separation of concerns
    5. Error Handling: Comprehensive error handling and user feedback
    6. Performance: Pagination, caching, optimized queries

    The example shows how all the JWT concepts from this guide work together in a production application.


    17. Troubleshooting and FAQ

    This chapter addresses common issues, debugging techniques, and frequently asked questions about JWT implementation.

    Common JWT Issues and Solutions

    Token Verification Failures

    graph TD
        A[Token Verification Failed] --> B{Check Error Type}
        B --> C[Invalid Signature]
        B --> D[Token Expired]
        B --> E[Invalid Format]
        B --> F[Algorithm Mismatch]
    
        C --> C1[Verify secret key]
        C --> C2[Check key rotation]
    
        D --> D1[Check token lifetime]
        D --> D2[Implement refresh]
    
        E --> E1[Validate JWT structure]
        E --> E2[Check encoding]
    
        F --> F1[Verify algorithm settings]
        F --> F2[Check JWKS configuration]

    Debug Token Issues:

    # JWT Debugging Utility
    import jwt
    import json
    from datetime import datetime
    
    class JWTDebugger:
        def __init__(self):
            self.common_issues = {
                'ExpiredSignatureError': 'Token has expired',
                'InvalidSignatureError': 'Invalid signature - check secret key',
                'DecodeError': 'Invalid token format',
                'InvalidTokenError': 'Token structure is invalid',
                'InvalidKeyError': 'Invalid key provided',
                'InvalidAlgorithmError': 'Algorithm not allowed'
            }
    
        def debug_token(self, token: str, secret_key: str = None, verify: bool = False) -> dict:
            """Debug JWT token and provide diagnostic information"""
    
            result = {
                'valid': False,
                'issues': [],
                'header': None,
                'payload': None,
                'signature_valid': False
            }
    
            try:
                # Try to decode header
                result['header'] = jwt.get_unverified_header(token)
                print(f"Header: {json.dumps(result['header'], indent=2)}")
    
            except Exception as e:
                result['issues'].append(f"Header decode failed: {str(e)}")
                return result
    
            try:
                # Try to decode payload without verification
                result['payload'] = jwt.decode(token, options={"verify_signature": False})
                print(f"Payload: {json.dumps(result['payload'], indent=2)}")
    
                # Check expiration
                exp = result['payload'].get('exp')
                if exp:
                    exp_time = datetime.fromtimestamp(exp)
                    current_time = datetime.utcnow()
    
                    if exp_time < current_time:
                        result['issues'].append(f"Token expired at {exp_time}")
                    else:
                        print(f"Token expires at: {exp_time}")
    
            except Exception as e:
                result['issues'].append(f"Payload decode failed: {str(e)}")
    
            # Try signature verification if secret provided
            if secret_key and verify:
                try:
                    algorithm = result['header'].get('alg', 'HS256')
                    jwt.decode(token, secret_key, algorithms=[algorithm])
                    result['signature_valid'] = True
                    result['valid'] = True
                    print("✓ Signature is valid")
    
                except Exception as e:
                    error_type = type(e).__name__
                    error_msg = self.common_issues.get(error_type, str(e))
                    result['issues'].append(f"Signature verification failed: {error_msg}")
                    print(f"✗ {error_msg}")
    
            return result
    
        def validate_token_structure(self, token: str) -> bool:
            """Validate basic JWT structure"""
            parts = token.split('.')
    
            if len(parts) != 3:
                print(f"✗ Invalid JWT structure: expected 3 parts, got {len(parts)}")
                return False
    
            print("✓ JWT has correct structure (3 parts)")
    
            # Check if parts are base64 encoded
            try:
                for i, part in enumerate(parts[:2]):  # Don't decode signature
                    import base64
                    # Add padding if needed
                    padded = part + '=' * (4 - len(part) % 4)
                    base64.urlsafe_b64decode(padded)
                print("✓ All parts are properly base64 encoded")
                return True
    
            except Exception as e:
                print(f"✗ Base64 decode failed: {e}")
                return False
    
        def compare_tokens(self, token1: str, token2: str) -> dict:
            """Compare two tokens to identify differences"""
    
            comparison = {
                'headers_match': False,
                'payloads_match': False,
                'signatures_match': False,
                'differences': []
            }
    
            try:
                header1 = jwt.get_unverified_header(token1)
                header2 = jwt.get_unverified_header(token2)
    
                if header1 == header2:
                    comparison['headers_match'] = True
                else:
                    comparison['differences'].append("Headers differ")
                    print(f"Header 1: {header1}")
                    print(f"Header 2: {header2}")
    
            except Exception as e:
                comparison['differences'].append(f"Header comparison failed: {e}")
    
            try:
                payload1 = jwt.decode(token1, options={"verify_signature": False})
                payload2 = jwt.decode(token2, options={"verify_signature": False})
    
                if payload1 == payload2:
                    comparison['payloads_match'] = True
                else:
                    comparison['differences'].append("Payloads differ")
    
                    # Find specific differences
                    for key in set(payload1.keys()) | set(payload2.keys()):
                        if payload1.get(key) != payload2.get(key):
                            print(f"Payload difference in '{key}': {payload1.get(key)} vs {payload2.get(key)}")
    
            except Exception as e:
                comparison['differences'].append(f"Payload comparison failed: {e}")
    
            # Compare signatures
            sig1 = token1.split('.')[2]
            sig2 = token2.split('.')[2]
            comparison['signatures_match'] = sig1 == sig2
    
            return comparison
    
    # Usage example
    debugger = JWTDebugger()
    
    # Debug a problematic token
    token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
    secret = "your-secret-key"
    
    print("=== JWT Debug Report ===")
    result = debugger.debug_token(token, secret, verify=True)
    
    if result['issues']:
        print("\n=== Issues Found ===")
        for issue in result['issues']:
            print(f"• {issue}")
    Python

    Frontend Token Management Issues

    /**
     * JWT Frontend Debugging
     */
    class JWTDebugger {
        constructor() {
            this.debugMode = localStorage.getItem('jwt_debug') === 'true';
        }
    
        enableDebugMode() {
            localStorage.setItem('jwt_debug', 'true');
            this.debugMode = true;
            console.log('JWT Debug mode enabled');
        }
    
        disableDebugMode() {
            localStorage.setItem('jwt_debug', 'false');
            this.debugMode = false;
            console.log('JWT Debug mode disabled');
        }
    
        logTokenInfo(token) {
            if (!this.debugMode) return;
    
            try {
                // Decode token payload without verification
                const payload = this.decodeTokenPayload(token);
    
                console.group('JWT Token Info');
                console.log('Token:', token.substring(0, 50) + '...');
                console.log('Payload:', payload);
    
                if (payload.exp) {
                    const expDate = new Date(payload.exp * 1000);
                    const now = new Date();
                    const timeUntilExpiry = expDate - now;
    
                    console.log('Expires:', expDate.toISOString());
                    console.log('Time until expiry:', Math.round(timeUntilExpiry / 1000), 'seconds');
    
                    if (timeUntilExpiry < 0) {
                        console.warn('⚠️ Token is expired!');
                    } else if (timeUntilExpiry < 300000) { // 5 minutes
                        console.warn('⚠️ Token expires soon!');
                    }
                }
    
                console.groupEnd();
    
            } catch (error) {
                console.error('Failed to decode token:', error);
            }
        }
    
        decodeTokenPayload(token) {
            const parts = token.split('.');
            if (parts.length !== 3) {
                throw new Error('Invalid JWT format');
            }
    
            const payload = parts[1];
            // Add padding if needed
            const padded = payload + '='.repeat((4 - payload.length % 4) % 4);
    
            try {
                const decoded = atob(padded.replace(/-/g, '+').replace(/_/g, '/'));
                return JSON.parse(decoded);
            } catch (error) {
                throw new Error('Failed to decode token payload');
            }
        }
    
        checkTokenStorage() {
            if (!this.debugMode) return;
    
            console.group('Token Storage Check');
    
            const locations = [
                { name: 'localStorage.access_token', value: localStorage.getItem('access_token') },
                { name: 'localStorage.refresh_token', value: localStorage.getItem('refresh_token') },
                { name: 'sessionStorage.access_token', value: sessionStorage.getItem('access_token') },
                { name: 'document.cookie', value: document.cookie }
            ];
    
            locations.forEach(location => {
                if (location.value) {
                    console.log(`✓ Found in ${location.name}:`, location.value.substring(0, 50) + '...');
    
                    if (location.name.includes('token') && location.value.startsWith('eyJ')) {
                        this.logTokenInfo(location.value);
                    }
                } else {
                    console.log(`✗ Not found in ${location.name}`);
                }
            });
    
            console.groupEnd();
        }
    
        validateApiResponse(response, requestUrl) {
            if (!this.debugMode) return;
    
            console.group(`API Response: ${requestUrl}`);
            console.log('Status:', response.status);
            console.log('Headers:', Object.fromEntries(response.headers.entries()));
    
            if (response.status === 401) {
                console.warn('⚠️ Unauthorized - token may be invalid or expired');
                this.checkTokenStorage();
            }
    
            console.groupEnd();
        }
    
        simulateTokenExpiry() {
            // For testing purposes - create an expired token
            const header = { typ: 'JWT', alg: 'HS256' };
            const payload = {
                sub: 'test-user',
                exp: Math.floor(Date.now() / 1000) - 3600, // Expired 1 hour ago
                iat: Math.floor(Date.now() / 1000) - 7200   // Issued 2 hours ago
            };
    
            const encodedHeader = btoa(JSON.stringify(header)).replace(/=/g, '');
            const encodedPayload = btoa(JSON.stringify(payload)).replace(/=/g, '');
            const fakeToken = `${encodedHeader}.${encodedPayload}.fake-signature`;
    
            console.log('Simulated expired token:', fakeToken);
            return fakeToken;
        }
    }
    
    // Enhanced API client with debugging
    class DebugAwareAPIClient extends APIClient {
        constructor(baseUrl) {
            super(baseUrl);
            this.debugger = new JWTDebugger();
        }
    
        async request(endpoint, options = {}) {
            const token = this.getStoredToken();
    
            if (token) {
                this.debugger.logTokenInfo(token);
            }
    
            try {
                const response = await super.request(endpoint, options);
                this.debugger.validateApiResponse(response, endpoint);
                return response;
    
            } catch (error) {
                if (error.status === 401) {
                    console.warn('Authentication failed - checking token status');
                    this.debugger.checkTokenStorage();
                }
                throw error;
            }
        }
    }
    JavaScript

    Performance Optimization

    Token Size Optimization

    # Minimize JWT payload size
    class OptimizedJWTHandler:
        def __init__(self):
            # Use short claim names
            self.claim_mappings = {
                'user_id': 'uid',
                'username': 'usr',
                'role': 'r',
                'permissions': 'prm',
                'department': 'dept',
                'organization': 'org'
            }
    
            self.reverse_mappings = {v: k for k, v in self.claim_mappings.items()}
    
        def create_optimized_token(self, user_data: dict) -> str:
            """Create token with optimized payload"""
    
            # Map to shorter claim names
            optimized_payload = {}
            for key, value in user_data.items():
                short_key = self.claim_mappings.get(key, key)
                optimized_payload[short_key] = value
    
            # Use shorter algorithms and remove unnecessary claims
            optimized_payload.update({
                'exp': datetime.utcnow() + timedelta(minutes=15),  # Short expiry
                'iat': datetime.utcnow(),
                'iss': 'api',  # Short issuer
                'aud': 'app'   # Short audience
            })
    
            return jwt.encode(optimized_payload, self.secret_key, algorithm='HS256')
    
        def decode_optimized_token(self, token: str) -> dict:
            """Decode and expand claims"""
    
            payload = jwt.decode(token, self.secret_key, algorithms=['HS256'])
    
            # Map back to full claim names
            expanded_payload = {}
            for key, value in payload.items():
                full_key = self.reverse_mappings.get(key, key)
                expanded_payload[full_key] = value
    
            return expanded_payload
    JavaScript

    Caching Strategies

    # Redis-based token caching
    import aioredis
    import pickle
    
    class TokenCache:
        def __init__(self, redis_url: str):
            self.redis_url = redis_url
            self.redis_client = None
            self.default_ttl = 900  # 15 minutes
    
        async def initialize(self):
            self.redis_client = await aioredis.from_url(self.redis_url)
    
        async def cache_user_data(self, user_id: str, user_data: dict, ttl: int = None):
            """Cache user data to avoid database lookups"""
    
            key = f"user_data:{user_id}"
            ttl = ttl or self.default_ttl
    
            serialized_data = pickle.dumps(user_data)
            await self.redis_client.setex(key, ttl, serialized_data)
    
        async def get_cached_user_data(self, user_id: str) -> dict:
            """Get cached user data"""
    
            key = f"user_data:{user_id}"
            cached_data = await self.redis_client.get(key)
    
            if cached_data:
                return pickle.loads(cached_data)
    
            return None
    
        async def invalidate_user_cache(self, user_id: str):
            """Invalidate user cache when data changes"""
    
            key = f"user_data:{user_id}"
            await self.redis_client.delete(key)
    
    # Enhanced authentication with caching
    class CachedAuthHandler:
        def __init__(self, jwt_handler, token_cache, db_session):
            self.jwt_handler = jwt_handler
            self.cache = token_cache
            self.db = db_session
    
        async def authenticate_user(self, token: str) -> dict:
            """Authenticate user with caching"""
    
            # Verify token
            payload = await self.jwt_handler.verify_token(token)
            if not payload:
                return None
    
            user_id = payload.get('sub')
    
            # Try cache first
            user_data = await self.cache.get_cached_user_data(user_id)
    
            if not user_data:
                # Cache miss - query database
                user = self.db.query(User).filter(User.id == user_id).first()
                if not user:
                    return None
    
                user_data = {
                    'id': user.id,
                    'username': user.username,
                    'email': user.email,
                    'role': user.role,
                    'permissions': [p.name for p in user.permissions],
                    'is_active': user.is_active
                }
    
                # Cache for future requests
                await self.cache.cache_user_data(user_id, user_data)
    
            return user_data
    Python

    FAQ Section

    Q: Should I store JWTs in localStorage or cookies?

    A: It depends on your security requirements:

    localStorage:

    • ✅ Easy to implement
    • ✅ Works well with SPAs
    • ❌ Vulnerable to XSS attacks
    • ❌ Doesn’t send automatically

    HttpOnly Cookies:

    • ✅ Protected from XSS
    • ✅ Sent automatically
    • ❌ Vulnerable to CSRF (need protection)
    • ❌ More complex with CORS

    Recommendation: Use HttpOnly cookies with CSRF protection for best security.

    Q: How long should JWT tokens last?

    A: Token lifetime depends on your security requirements:

    • Access tokens: 15 minutes to 1 hour
    • Refresh tokens: 1 week to 1 month
    • Remember me tokens: Up to 1 year

    Shorter lifetimes = better security but more refresh requests.

    Q: Can I revoke JWTs?

    A: JWTs are stateless, but you can implement revocation:

    1. Blacklist tokens in database/cache
    2. Short token lifetimes with refresh mechanism
    3. Token versioning in user records
    4. Session tracking with database state

    Q: Should I include sensitive data in JWT payload?

    A: No! JWT payloads are only base64 encoded, not encrypted:

    Don’t include:

    • Passwords or password hashes
    • Credit card numbers
    • Social security numbers
    • Private API keys

    Safe to include:

    • User ID
    • Username
    • Role/permissions
    • Non-sensitive metadata

    Q: How do I handle JWT in microservices?

    A: Use shared verification:

    1. Shared secret for HMAC algorithms
    2. Public key for RS256/ES256 algorithms
    3. JWKS endpoint for key distribution
    4. API gateway for centralized auth
    5. Service mesh for automatic token forwarding

    Q: What’s the difference between JWT, JWS, and JWE?

    A:

    • JWT: JSON Web Token (the standard)
    • JWS: JSON Web Signature (signed tokens)
    • JWE: JSON Web Encryption (encrypted tokens)

    Most “JWT” implementations are actually JWS. Use JWE for sensitive data.

    Q: How do I debug “Invalid signature” errors?

    A: Common causes:

    1. Wrong secret key – verify environment variables
    2. Algorithm mismatch – check HS256 vs RS256
    3. Key rotation – ensure old keys still work
    4. Clock skew – synchronize server times
    5. Character encoding – check UTF-8 encoding

    Q: Can I use JWT for session management?

    A: Yes, but consider:

    Pros:

    • Stateless
    • Scalable
    • Cross-domain friendly

    Cons:

    • Cannot revoke easily
    • Larger than session IDs
    • No server-side session data

    Use hybrid approach: JWT for authentication, sessions for state.

    Q: How do I implement “Remember Me” with JWT?

    A: Use two-token system:

    1. Short-lived access token (15 minutes)
    2. Long-lived refresh token (30 days)
    3. Automatic refresh before expiry
    4. Refresh token rotation for security

    Q: What happens if my JWT secret is compromised?

    A: Immediate actions:

    1. Rotate the secret immediately
    2. Invalidate all existing tokens
    3. Force user re-authentication
    4. Audit access logs
    5. Review security practices

    Prevention: Use strong secrets, regular rotation, secure storage.

    Migration Strategies

    Moving from Session-based to JWT

    # Gradual migration strategy
    class HybridAuthHandler:
        def __init__(self, session_handler, jwt_handler):
            self.session_handler = session_handler
            self.jwt_handler = jwt_handler
    
        async def authenticate(self, request):
            """Try JWT first, fall back to sessions"""
    
            # Try JWT authentication
            auth_header = request.headers.get('Authorization')
            if auth_header and auth_header.startswith('Bearer '):
                token = auth_header.split(' ')[1]
                user = await self.jwt_handler.verify_token(token)
                if user:
                    return user
    
            # Fall back to session authentication
            session_id = request.cookies.get('session_id')
            if session_id:
                return await self.session_handler.get_user_from_session(session_id)
    
            return None
    
        async def migrate_user_to_jwt(self, user_id: str):
            """Migrate specific user to JWT"""
    
            # Mark user as JWT-enabled in database
            await self.mark_user_jwt_enabled(user_id)
    
            # Invalidate existing sessions
            await self.session_handler.invalidate_user_sessions(user_id)
    
            return True
    Python

    This comprehensive troubleshooting guide helps developers identify, debug, and resolve common JWT implementation issues while providing best practices for optimization and migration.


    18. Production Deployment and Monitoring

    This final chapter covers deploying JWT-based applications to production environments with proper monitoring, logging, and maintenance strategies.

    Production Deployment Checklist

    graph TB
        A[Production Deployment] --> B[Security Configuration]
        A --> C[Performance Optimization]
        A --> D[Monitoring Setup]
        A --> E[Backup Strategies]
    
        B --> B1[Environment Variables]
        B --> B2[HTTPS Configuration]
        B --> B3[Secret Management]
        B --> B4[Security Headers]
    
        C --> C1[Caching Strategy]
        C --> C2[Database Optimization]
        C --> C3[Load Balancing]
        C --> C4[CDN Configuration]
    
        D --> D1[Application Metrics]
        D --> D2[Security Monitoring]
        D --> D3[Error Tracking]
        D --> D4[Performance Monitoring]
    
        E --> E1[Database Backups]
        E --> E2[Configuration Backups]
        E --> E3[Disaster Recovery]
        E --> E4[Key Backup]

    Docker Production Setup

    Dockerfile for FastAPI Backend:

    FROM python:3.11-slim
    
    # Set environment variables
    ENV PYTHONDONTWRITEBYTECODE=1 \
        PYTHONUNBUFFERED=1 \
        PYTHONPATH="/app" \
        PIP_NO_CACHE_DIR=1 \
        PIP_DISABLE_PIP_VERSION_CHECK=1
    
    # Create non-root user
    RUN addgroup --system --gid 1001 appgroup && \
        adduser --system --uid 1001 --gid 1001 --no-create-home appuser
    
    # Install system dependencies
    RUN apt-get update && apt-get install -y \
        gcc \
        && rm -rf /var/lib/apt/lists/*
    
    # Set work directory
    WORKDIR /app
    
    # Copy requirements and install Python dependencies
    COPY requirements.txt .
    RUN pip install --no-cache-dir -r requirements.txt
    
    # Copy application code
    COPY . .
    
    # Change ownership to non-root user
    RUN chown -R appuser:appgroup /app
    
    # Switch to non-root user
    USER appuser
    
    # Health check
    HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
        CMD python -c "import requests; requests.get('http://localhost:8000/health')" || exit 1
    
    # Expose port
    EXPOSE 8000
    
    # Start application
    CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
    Dockerfile

    Production Docker Compose:

    version: '3.8'
    
    services:
      # FastAPI Backend
      api:
        build: 
          context: ./backend
          dockerfile: Dockerfile
        container_name: jwt_api
        restart: unless-stopped
        ports:
          - "8000:8000"
        environment:
          - DATABASE_URL=postgresql://user:password@postgres:5432/jwt_app
          - REDIS_URL=redis://redis:6379
          - SECRET_KEY=${SECRET_KEY}
          - ENVIRONMENT=production
          - LOG_LEVEL=INFO
        depends_on:
          postgres:
            condition: service_healthy
          redis:
            condition: service_healthy
        networks:
          - jwt_network
        volumes:
          - ./logs:/app/logs
        healthcheck:
          test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
          interval: 30s
          timeout: 10s
          retries: 3
    
      # PostgreSQL Database
      postgres:
        image: postgres:15-alpine
        container_name: jwt_postgres
        restart: unless-stopped
        environment:
          POSTGRES_DB: jwt_app
          POSTGRES_USER: user
          POSTGRES_PASSWORD: ${DB_PASSWORD}
          POSTGRES_INITDB_ARGS: "--auth-host=scram-sha-256"
        volumes:
          - postgres_data:/var/lib/postgresql/data
          - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
        networks:
          - jwt_network
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U user -d jwt_app"]
          interval: 30s
          timeout: 10s
          retries: 3
    
      # Redis Cache
      redis:
        image: redis:7-alpine
        container_name: jwt_redis
        restart: unless-stopped
        command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes
        volumes:
          - redis_data:/data
        networks:
          - jwt_network
        healthcheck:
          test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
          interval: 30s
          timeout: 10s
          retries: 3
    
      # Nginx Reverse Proxy
      nginx:
        image: nginx:alpine
        container_name: jwt_nginx
        restart: unless-stopped
        ports:
          - "80:80"
          - "443:443"
        volumes:
          - ./nginx/nginx.conf:/etc/nginx/nginx.conf
          - ./nginx/ssl:/etc/nginx/ssl
          - ./frontend:/usr/share/nginx/html
          - ./logs/nginx:/var/log/nginx
        depends_on:
          - api
        networks:
          - jwt_network
    
      # Monitoring with Prometheus
      prometheus:
        image: prom/prometheus:latest
        container_name: jwt_prometheus
        restart: unless-stopped
        ports:
          - "9090:9090"
        volumes:
          - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
          - prometheus_data:/prometheus
        command:
          - '--config.file=/etc/prometheus/prometheus.yml'
          - '--storage.tsdb.path=/prometheus'
          - '--web.console.libraries=/etc/prometheus/console_libraries'
          - '--web.console.templates=/etc/prometheus/consoles'
        networks:
          - jwt_network
    
      # Grafana Dashboard
      grafana:
        image: grafana/grafana:latest
        container_name: jwt_grafana
        restart: unless-stopped
        ports:
          - "3000:3000"
        environment:
          - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
        volumes:
          - grafana_data:/var/lib/grafana
          - ./monitoring/grafana:/etc/grafana/provisioning
        networks:
          - jwt_network
    
    volumes:
      postgres_data:
      redis_data:
      prometheus_data:
      grafana_data:
    
    networks:
      jwt_network:
        driver: bridge
    YAML

    Nginx Production Configuration

    nginx.conf:

    user nginx;
    worker_processes auto;
    error_log /var/log/nginx/error.log warn;
    pid /var/run/nginx.pid;
    
    events {
        worker_connections 1024;
        use epoll;
        multi_accept on;
    }
    
    http {
        include /etc/nginx/mime.types;
        default_type application/octet-stream;
    
        # Logging
        log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" "$http_x_forwarded_for" '
                        'rt=$request_time uct="$upstream_connect_time" '
                        'uht="$upstream_header_time" urt="$upstream_response_time"';
    
        access_log /var/log/nginx/access.log main;
    
        # Performance
        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;
        client_max_body_size 10M;
    
        # Compression
        gzip on;
        gzip_vary on;
        gzip_min_length 1024;
        gzip_proxied any;
        gzip_comp_level 6;
        gzip_types
            text/plain
            text/css
            text/xml
            text/javascript
            application/json
            application/javascript
            application/xml+rss
            application/atom+xml
            image/svg+xml;
    
        # Rate limiting
        limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
        limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
    
        # Security headers
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-XSS-Protection "1; mode=block" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header Referrer-Policy "no-referrer-when-downgrade" always;
        add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
    
        # Hide nginx version
        server_tokens off;
    
        # Upstream backend
        upstream api_backend {
            server api:8000;
            keepalive 32;
        }
    
        # HTTP redirect to HTTPS
        server {
            listen 80;
            server_name _;
            return 301 https://$host$request_uri;
        }
    
        # HTTPS server
        server {
            listen 443 ssl http2;
            server_name your-domain.com;
    
            # SSL Configuration
            ssl_certificate /etc/nginx/ssl/cert.pem;
            ssl_certificate_key /etc/nginx/ssl/key.pem;
            ssl_session_timeout 1d;
            ssl_session_cache shared:SSL:50m;
            ssl_session_tickets off;
    
            # Modern configuration
            ssl_protocols TLSv1.2 TLSv1.3;
            ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
            ssl_prefer_server_ciphers off;
    
            # HSTS
            add_header Strict-Transport-Security "max-age=63072000" always;
    
            # API routes
            location /api/ {
                limit_req zone=api burst=20 nodelay;
    
                proxy_pass http://api_backend;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_cache_bypass $http_upgrade;
    
                # Timeouts
                proxy_connect_timeout 5s;
                proxy_send_timeout 60s;
                proxy_read_timeout 60s;
            }
    
            # Auth endpoints with stricter rate limiting
            location /api/auth/ {
                limit_req zone=login burst=5 nodelay;
    
                proxy_pass http://api_backend;
                proxy_http_version 1.1;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
            }
    
            # Static files
            location / {
                root /usr/share/nginx/html;
                index index.html index.htm;
                try_files $uri $uri/ /index.html;
    
                # Cache static assets
                location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
                    expires 1y;
                    add_header Cache-Control "public, immutable";
                }
            }
    
            # Health check
            location /health {
                access_log off;
                return 200 "healthy\n";
                add_header Content-Type text/plain;
            }
        }
    }
    Nginx

    Application Monitoring

    Prometheus Configuration (monitoring/prometheus.yml):

    global:
      scrape_interval: 15s
      evaluation_interval: 15s
    
    rule_files:
      - "alert_rules.yml"
    
    alerting:
      alertmanagers:
        - static_configs:
            - targets:
              - alertmanager:9093
    
    scrape_configs:
      - job_name: 'jwt-api'
        static_configs:
          - targets: ['api:8000']
        metrics_path: '/metrics'
        scrape_interval: 10s
    
      - job_name: 'nginx'
        static_configs:
          - targets: ['nginx:9113']
    
      - job_name: 'postgres'
        static_configs:
          - targets: ['postgres-exporter:9187']
    
      - job_name: 'redis'
        static_configs:
          - targets: ['redis-exporter:9121']
    
      - job_name: 'node'
        static_configs:
          - targets: ['node-exporter:9100']
    YAML

    Application Metrics (app/monitoring/metrics.py):

    from prometheus_client import Counter, Histogram, Gauge, start_http_server
    from functools import wraps
    import time
    
    # Metrics
    REQUEST_COUNT = Counter(
        'http_requests_total',
        'Total HTTP requests',
        ['method', 'endpoint', 'status']
    )
    
    REQUEST_DURATION = Histogram(
        'http_request_duration_seconds',
        'HTTP request duration',
        ['method', 'endpoint']
    )
    
    ACTIVE_SESSIONS = Gauge(
        'active_jwt_sessions',
        'Number of active JWT sessions'
    )
    
    TOKEN_OPERATIONS = Counter(
        'jwt_token_operations_total',
        'JWT token operations',
        ['operation', 'status']
    )
    
    LOGIN_ATTEMPTS = Counter(
        'login_attempts_total',
        'Login attempts',
        ['status', 'ip']
    )
    
    FAILED_AUTH = Counter(
        'failed_authentication_total',
        'Failed authentication attempts',
        ['reason']
    )
    
    def track_request_metrics(func):
        """Decorator to track request metrics"""
        @wraps(func)
        async def wrapper(*args, **kwargs):
            start_time = time.time()
    
            try:
                result = await func(*args, **kwargs)
                status = getattr(result, 'status_code', 200)
                REQUEST_COUNT.labels(
                    method='POST',  # You'd get this from request
                    endpoint=func.__name__,
                    status=status
                ).inc()
    
                return result
    
            except Exception as e:
                REQUEST_COUNT.labels(
                    method='POST',
                    endpoint=func.__name__,
                    status=500
                ).inc()
                raise
    
            finally:
                REQUEST_DURATION.labels(
                    method='POST',
                    endpoint=func.__name__
                ).observe(time.time() - start_time)
    
        return wrapper
    
    class JWTMetrics:
        def __init__(self):
            self.active_tokens = set()
    
        def track_token_creation(self, jti: str, success: bool):
            """Track token creation"""
            status = 'success' if success else 'failed'
            TOKEN_OPERATIONS.labels(operation='create', status=status).inc()
    
            if success:
                self.active_tokens.add(jti)
                ACTIVE_SESSIONS.set(len(self.active_tokens))
    
        def track_token_verification(self, jti: str, success: bool):
            """Track token verification"""
            status = 'success' if success else 'failed'
            TOKEN_OPERATIONS.labels(operation='verify', status=status).inc()
    
        def track_token_revocation(self, jti: str):
            """Track token revocation"""
            TOKEN_OPERATIONS.labels(operation='revoke', status='success').inc()
            self.active_tokens.discard(jti)
            ACTIVE_SESSIONS.set(len(self.active_tokens))
    
        def track_login_attempt(self, success: bool, ip: str):
            """Track login attempts"""
            status = 'success' if success else 'failed'
            LOGIN_ATTEMPTS.labels(status=status, ip=ip).inc()
    
        def track_auth_failure(self, reason: str):
            """Track authentication failures"""
            FAILED_AUTH.labels(reason=reason).inc()
    
    # Global metrics instance
    jwt_metrics = JWTMetrics()
    
    # Start metrics server
    def start_metrics_server(port: int = 8001):
        start_http_server(port)
    Python

    Logging Configuration

    Structured Logging (app/logging_config.py):

    import logging
    import json
    from datetime import datetime
    from typing import Dict, Any
    import structlog
    
    class JSONFormatter(logging.Formatter):
        def format(self, record):
            log_entry = {
                'timestamp': datetime.utcnow().isoformat(),
                'level': record.levelname,
                'logger': record.name,
                'message': record.getMessage(),
                'module': record.module,
                'function': record.funcName,
                'line': record.lineno
            }
    
            # Add extra fields
            if hasattr(record, 'user_id'):
                log_entry['user_id'] = record.user_id
            if hasattr(record, 'request_id'):
                log_entry['request_id'] = record.request_id
            if hasattr(record, 'ip_address'):
                log_entry['ip_address'] = record.ip_address
            if hasattr(record, 'jwt_jti'):
                log_entry['jwt_jti'] = record.jwt_jti
    
            return json.dumps(log_entry)
    
    def setup_logging(log_level: str = "INFO"):
        """Setup structured logging"""
    
        # Configure structlog
        structlog.configure(
            processors=[
                structlog.stdlib.filter_by_level,
                structlog.stdlib.add_logger_name,
                structlog.stdlib.add_log_level,
                structlog.stdlib.PositionalArgumentsFormatter(),
                structlog.processors.TimeStamper(fmt="ISO"),
                structlog.processors.StackInfoRenderer(),
                structlog.processors.format_exc_info,
                structlog.processors.UnicodeDecoder(),
                structlog.processors.JSONRenderer()
            ],
            context_class=dict,
            logger_factory=structlog.stdlib.LoggerFactory(),
            wrapper_class=structlog.stdlib.BoundLogger,
            cache_logger_on_first_use=True,
        )
    
        # Configure standard logging
        logging.basicConfig(
            level=getattr(logging, log_level.upper()),
            format='%(message)s',
            handlers=[
                logging.StreamHandler(),
                logging.FileHandler('/app/logs/app.log')
            ]
        )
    
        # Set formatter
        formatter = JSONFormatter()
        for handler in logging.root.handlers:
            handler.setFormatter(formatter)
    
    # Security event logger
    class SecurityLogger:
        def __init__(self):
            self.logger = structlog.get_logger("security")
    
        def log_login_attempt(self, username: str, ip: str, success: bool, user_agent: str = None):
            self.logger.info(
                "login_attempt",
                username=username,
                ip_address=ip,
                success=success,
                user_agent=user_agent,
                event_type="authentication"
            )
    
        def log_token_operation(self, operation: str, jti: str, user_id: str, ip: str):
            self.logger.info(
                "token_operation",
                operation=operation,
                jwt_jti=jti,
                user_id=user_id,
                ip_address=ip,
                event_type="token"
            )
    
        def log_security_violation(self, violation_type: str, details: Dict[str, Any], ip: str):
            self.logger.warning(
                "security_violation",
                violation_type=violation_type,
                details=details,
                ip_address=ip,
                event_type="security"
            )
    
        def log_access_denied(self, user_id: str, resource: str, reason: str, ip: str):
            self.logger.warning(
                "access_denied",
                user_id=user_id,
                resource=resource,
                reason=reason,
                ip_address=ip,
                event_type="authorization"
            )
    
    # Global security logger
    security_logger = SecurityLogger()
    Python

    Environment Configuration

    .env.production:

    # Application
    ENVIRONMENT=production
    DEBUG=false
    LOG_LEVEL=INFO
    
    # Database
    DATABASE_URL=postgresql://user:password@postgres:5432/jwt_app
    DB_PASSWORD=your-secure-db-password
    
    # Redis
    REDIS_URL=redis://redis:6379
    REDIS_PASSWORD=your-secure-redis-password
    
    # JWT Configuration
    SECRET_KEY=your-super-secure-secret-key-min-32-chars
    ACCESS_TOKEN_EXPIRE_MINUTES=15
    REFRESH_TOKEN_EXPIRE_DAYS=30
    ALGORITHM=HS256
    
    # Security
    ALLOWED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com
    CORS_ALLOW_CREDENTIALS=true
    HTTPS_ONLY=true
    SECURE_COOKIES=true
    
    # Rate Limiting
    RATE_LIMIT_REQUESTS=100
    RATE_LIMIT_WINDOW=3600
    
    # Monitoring
    PROMETHEUS_ENABLED=true
    METRICS_PORT=8001
    
    # Email (for notifications)
    SMTP_HOST=smtp.gmail.com
    SMTP_PORT=587
    SMTP_USERNAME=your-email@gmail.com
    SMTP_PASSWORD=your-app-password
    
    # External Services
    OAUTH_GOOGLE_CLIENT_ID=your-google-client-id
    OAUTH_GOOGLE_CLIENT_SECRET=your-google-client-secret
    
    # Backup
    BACKUP_ENABLED=true
    BACKUP_S3_BUCKET=your-backup-bucket
    AWS_ACCESS_KEY_ID=your-aws-key
    AWS_SECRET_ACCESS_KEY=your-aws-secret
    INI

    Backup and Disaster Recovery

    Backup Script (scripts/backup.sh):

    #!/bin/bash
    
    # Configuration
    BACKUP_DIR="/backups"
    DATE=$(date +%Y%m%d_%H%M%S)
    RETENTION_DAYS=30
    
    # Database backup
    echo "Starting database backup..."
    docker exec jwt_postgres pg_dump -U user -d jwt_app | gzip > "$BACKUP_DIR/db_backup_$DATE.sql.gz"
    
    # Redis backup
    echo "Starting Redis backup..."
    docker exec jwt_redis redis-cli --rdb /data/backup.rdb
    docker cp jwt_redis:/data/backup.rdb "$BACKUP_DIR/redis_backup_$DATE.rdb"
    
    # Configuration backup
    echo "Backing up configuration..."
    tar -czf "$BACKUP_DIR/config_backup_$DATE.tar.gz" \
        .env.production \
        nginx/ \
        monitoring/ \
        docker-compose.yml
    
    # Upload to S3 (if configured)
    if [ "$BACKUP_S3_BUCKET" != "" ]; then
        echo "Uploading to S3..."
        aws s3 cp "$BACKUP_DIR/" "s3://$BACKUP_S3_BUCKET/backups/" --recursive --exclude "*" --include "*$DATE*"
    fi
    
    # Cleanup old backups
    echo "Cleaning up old backups..."
    find "$BACKUP_DIR" -name "*.gz" -mtime +$RETENTION_DAYS -delete
    find "$BACKUP_DIR" -name "*.rdb" -mtime +$RETENTION_DAYS -delete
    find "$BACKUP_DIR" -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete
    
    echo "Backup completed successfully!"
    Bash

    Crontab for automated backups:

    # Add to crontab (crontab -e)
    # Daily backup at 2 AM
    0 2 * * * /path/to/backup.sh >> /var/log/backup.log 2>&1
    
    # Weekly full backup on Sunday at 1 AM
    0 1 * * 0 /path/to/full_backup.sh >> /var/log/backup.log 2>&1
    Bash

    Health Checks and Monitoring

    Advanced Health Check (app/health.py):

    from fastapi import APIRouter, status
    from sqlalchemy.orm import Session
    from app.database import get_db
    from app.auth.token_blacklist import token_blacklist
    import aioredis
    import asyncio
    from datetime import datetime
    
    router = APIRouter()
    
    @router.get("/health")
    async def health_check():
        """Basic health check"""
        return {
            "status": "healthy",
            "timestamp": datetime.utcnow().isoformat(),
            "version": "1.0.0"
        }
    
    @router.get("/health/detailed")
    async def detailed_health_check(db: Session = Depends(get_db)):
        """Detailed health check with dependency status"""
    
        health_status = {
            "status": "healthy",
            "timestamp": datetime.utcnow().isoformat(),
            "version": "1.0.0",
            "checks": {}
        }
    
        # Database check
        try:
            db.execute("SELECT 1")
            health_status["checks"]["database"] = {"status": "healthy"}
        except Exception as e:
            health_status["checks"]["database"] = {"status": "unhealthy", "error": str(e)}
            health_status["status"] = "unhealthy"
    
        # Redis check
        try:
            redis_client = await aioredis.from_url("redis://redis:6379")
            await redis_client.ping()
            await redis_client.close()
            health_status["checks"]["redis"] = {"status": "healthy"}
        except Exception as e:
            health_status["checks"]["redis"] = {"status": "unhealthy", "error": str(e)}
            health_status["status"] = "unhealthy"
    
        # Token blacklist check
        try:
            stats = await token_blacklist.get_blacklist_stats()
            health_status["checks"]["token_blacklist"] = {
                "status": "healthy",
                "stats": stats
            }
        except Exception as e:
            health_status["checks"]["token_blacklist"] = {"status": "unhealthy", "error": str(e)}
    
        return health_status
    
    @router.get("/metrics")
    async def metrics_endpoint():
        """Expose metrics for Prometheus"""
        # This would be handled by prometheus_client
        pass
    Python

    This production deployment guide provides a complete setup for deploying JWT-based applications with proper security, monitoring, and maintenance procedures. The configuration ensures high availability, security, and observability in production environments.


    Conclusion

    This comprehensive guide has taken you through every aspect of JWT implementation, from basic concepts to production-ready applications. You now have the knowledge and tools to:

    • Understand JWT fundamentals and choose the right token types
    • Implement secure authentication with FastAPI and JavaScript
    • Handle advanced security scenarios including JWE and OAuth 2.0
    • Manage tokens effectively with rotation and revocation strategies
    • Deploy to production with proper monitoring and security measures

    Remember that security is an ongoing process. Keep your dependencies updated, monitor your applications, and stay informed about the latest security best practices.

    Key Takeaways

    1. Security First: Always prioritize security over convenience
    2. Token Lifecycle: Implement proper token creation, validation, and revocation
    3. Error Handling: Provide comprehensive error handling and logging
    4. Monitoring: Monitor authentication events and security metrics
    5. Testing: Thoroughly test all authentication flows and edge cases

    Next Steps

    • Implement the concepts in your own projects
    • Customize the examples to fit your specific requirements
    • Stay Updated with JWT and security best practices
    • Contribute to the community by sharing your experiences

    Happy coding with secure JWT implementations!


    19. Testing and Quality Assurance

    Comprehensive testing is essential for JWT authentication systems. This chapter covers testing strategies, frameworks, and best practices for ensuring your authentication system is robust and secure.

    Testing Strategy Overview

    graph TB
        A[Testing Strategy] --> B[Unit Tests]
        A --> C[Integration Tests]
        A --> D[End-to-End Tests]
        A --> E[Security Tests]
        A --> F[Performance Tests]
    
        B --> B1[JWT Handler Tests]
        B --> B2[Password Hashing Tests]
        B --> B3[Token Validation Tests]
        B --> B4[Utility Function Tests]
    
        C --> C1[API Endpoint Tests]
        C --> C2[Database Integration Tests]
        C --> C3[Authentication Flow Tests]
        C --> C4[Authorization Tests]
    
        D --> D1[User Journey Tests]
        D --> D2[Frontend Integration Tests]
        D --> D3[Cross-Browser Tests]
        D --> D4[Mobile Responsiveness Tests]
    
        E --> E1[Penetration Tests]
        E --> E2[Vulnerability Scans]
        E --> E3[Token Security Tests]
        E --> E4[OWASP Testing]
    
        F --> F1[Load Tests]
        F --> F2[Stress Tests]
        F --> F3[Token Performance Tests]
        F --> F4[Scalability Tests]

    Unit Testing with pytest

    JWT Handler Tests

    Create backend/tests/test_jwt_handler.py:

    import pytest
    from datetime import datetime, timedelta
    from app.auth.jwt_handler import JWTHandler
    from app.auth.jwe_handler import JWEHandler
    from freezegun import freeze_time
    import jwt
    
    class TestJWTHandler:
    
        @pytest.fixture
        def jwt_handler(self):
            """Create JWT handler instance for testing"""
            return JWTHandler()
    
        @pytest.fixture
        def sample_payload(self):
            """Sample token payload for testing"""
            return {
                "sub": "user123",
                "email": "test@example.com",
                "role": "user",
                "permissions": ["read", "write"]
            }
    
        def test_create_access_token(self, jwt_handler, sample_payload):
            """Test access token creation"""
            token = jwt_handler.create_access_token(sample_payload)
    
            assert token is not None
            assert isinstance(token, str)
            assert len(token.split('.')) == 3  # JWT has 3 parts
    
            # Verify token can be decoded
            decoded = jwt_handler.verify_token(token)
            assert decoded is not None
            assert decoded["sub"] == sample_payload["sub"]
            assert decoded["email"] == sample_payload["email"]
            assert "exp" in decoded
            assert "iat" in decoded
    
        def test_create_refresh_token(self, jwt_handler, sample_payload):
            """Test refresh token creation"""
            token = jwt_handler.create_refresh_token(sample_payload)
    
            assert token is not None
            decoded = jwt_handler.verify_token(token)
            assert decoded is not None
            assert decoded["type"] == "refresh"
    
            # Refresh token should have longer expiry
            exp_time = datetime.fromtimestamp(decoded["exp"])
            iat_time = datetime.fromtimestamp(decoded["iat"])
            duration = exp_time - iat_time
            assert duration.days >= 7  # At least 7 days
    
        def test_verify_valid_token(self, jwt_handler, sample_payload):
            """Test verification of valid token"""
            token = jwt_handler.create_access_token(sample_payload)
            decoded = jwt_handler.verify_token(token)
    
            assert decoded is not None
            assert decoded["sub"] == sample_payload["sub"]
            assert decoded["email"] == sample_payload["email"]
    
        def test_verify_expired_token(self, jwt_handler, sample_payload):
            """Test verification of expired token"""
            # Create token that expires immediately
            with freeze_time("2023-01-01 12:00:00"):
                token = jwt_handler.create_access_token(sample_payload)
    
            # Try to verify it after expiration
            with freeze_time("2023-01-01 13:00:00"):
                decoded = jwt_handler.verify_token(token)
                assert decoded is None
    
        def test_verify_invalid_token(self, jwt_handler):
            """Test verification of invalid token"""
            invalid_tokens = [
                "invalid.token.here",
                "not.a.jwt",
                "",
                None,
                "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.invalid.signature"
            ]
    
            for invalid_token in invalid_tokens:
                decoded = jwt_handler.verify_token(invalid_token)
                assert decoded is None
    
        def test_password_hashing(self, jwt_handler):
            """Test password hashing and verification"""
            password = "test_password_123"
    
            # Hash password
            hashed = jwt_handler.hash_password(password)
            assert hashed is not None
            assert hashed != password
            assert len(hashed) > 20  # Bcrypt hashes are long
    
            # Verify correct password
            assert jwt_handler.verify_password(password, hashed) is True
    
            # Verify incorrect password
            assert jwt_handler.verify_password("wrong_password", hashed) is False
    
        def test_refresh_access_token(self, jwt_handler, sample_payload):
            """Test access token refresh"""
            # Create refresh token
            refresh_token = jwt_handler.create_refresh_token(sample_payload)
    
            # Refresh access token
            new_access_token = jwt_handler.refresh_access_token(refresh_token)
            assert new_access_token is not None
    
            # Verify new token
            decoded = jwt_handler.verify_token(new_access_token)
            assert decoded is not None
            assert decoded["sub"] == sample_payload["sub"]
            assert decoded["type"] == "access"
    
        def test_refresh_with_invalid_token(self, jwt_handler):
            """Test refresh with invalid refresh token"""
            invalid_tokens = [
                "invalid_token",
                None,
                jwt_handler.create_access_token({"sub": "test"})  # Access token, not refresh
            ]
    
            for invalid_token in invalid_tokens:
                result = jwt_handler.refresh_access_token(invalid_token)
                assert result is None
    
        def test_token_claims_validation(self, jwt_handler, sample_payload):
            """Test validation of token claims"""
            token = jwt_handler.create_access_token(sample_payload)
            decoded = jwt_handler.verify_token(token)
    
            # Check required claims
            assert "sub" in decoded
            assert "exp" in decoded
            assert "iat" in decoded
            assert "type" in decoded
    
            # Check expiration is in the future
            exp_time = datetime.fromtimestamp(decoded["exp"])
            assert exp_time > datetime.utcnow()
    
            # Check issued at is in the past
            iat_time = datetime.fromtimestamp(decoded["iat"])
            assert iat_time <= datetime.utcnow()
    
    class TestJWEHandler:
    
        @pytest.fixture
        def jwe_handler(self):
            """Create JWE handler instance for testing"""
            return JWEHandler()
    
        @pytest.fixture
        def sample_data(self):
            """Sample data for encryption testing"""
            return {
                "user_id": "123",
                "sensitive_data": "confidential information",
                "timestamp": "2023-01-01T00:00:00Z"
            }
    
        def test_encrypt_decrypt_data(self, jwe_handler, sample_data):
            """Test data encryption and decryption"""
            # Encrypt data
            encrypted = jwe_handler.encrypt_data(sample_data)
            assert encrypted is not None
            assert encrypted != sample_data
    
            # Decrypt data
            decrypted = jwe_handler.decrypt_data(encrypted)
            assert decrypted == sample_data
    
        def test_create_verify_jwe_token(self, jwe_handler, sample_data):
            """Test JWE token creation and verification"""
            # Create JWE token
            token = jwe_handler.create_jwe_token(sample_data)
            assert token is not None
            assert isinstance(token, str)
            assert len(token.split('.')) == 5  # JWE has 5 parts
    
            # Verify JWE token
            decrypted = jwe_handler.verify_jwe_token(token)
            assert decrypted is not None
            assert decrypted["user_id"] == sample_data["user_id"]
    
        def test_invalid_jwe_token(self, jwe_handler):
            """Test handling of invalid JWE tokens"""
            invalid_tokens = [
                "invalid.jwe.token.here.invalid",
                "not.a.jwe",
                "",
                None
            ]
    
            for invalid_token in invalid_tokens:
                result = jwe_handler.verify_jwe_token(invalid_token)
                assert result is None
    Python

    Authentication API Tests

    Create backend/tests/test_auth_api.py:

    import pytest
    from fastapi.testclient import TestClient
    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker
    from app.main import app
    from app.database.database import get_db, Base
    import tempfile
    import os
    
    # Create temporary database for testing
    SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
    engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
    TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
    
    Base.metadata.create_all(bind=engine)
    
    def override_get_db():
        try:
            db = TestingSessionLocal()
            yield db
        finally:
            db.close()
    
    app.dependency_overrides[get_db] = override_get_db
    
    class TestAuthenticationAPI:
    
        @pytest.fixture(scope="class")
        def client(self):
            """Create test client"""
            return TestClient(app)
    
        @pytest.fixture(autouse=True)
        def setup_database(self):
            """Setup clean database for each test"""
            Base.metadata.drop_all(bind=engine)
            Base.metadata.create_all(bind=engine)
            yield
            Base.metadata.drop_all(bind=engine)
    
        def test_register_user_success(self, client):
            """Test successful user registration"""
            user_data = {
                "email": "test@example.com",
                "password": "password123",
                "full_name": "Test User"
            }
    
            response = client.post("/auth/register", json=user_data)
    
            assert response.status_code == 201
            data = response.json()
            assert data["email"] == user_data["email"]
            assert data["full_name"] == user_data["full_name"]
            assert "id" in data
            assert "password" not in data  # Password should not be returned
    
        def test_register_user_duplicate_email(self, client):
            """Test registration with duplicate email"""
            user_data = {
                "email": "duplicate@example.com",
                "password": "password123",
                "full_name": "First User"
            }
    
            # First registration should succeed
            response1 = client.post("/auth/register", json=user_data)
            assert response1.status_code == 201
    
            # Second registration with same email should fail
            response2 = client.post("/auth/register", json=user_data)
            assert response2.status_code == 400
            assert "already registered" in response2.json()["detail"].lower()
    
        def test_register_user_invalid_email(self, client):
            """Test registration with invalid email"""
            user_data = {
                "email": "invalid-email",
                "password": "password123",
                "full_name": "Test User"
            }
    
            response = client.post("/auth/register", json=user_data)
            assert response.status_code == 422  # Validation error
    
        def test_register_user_weak_password(self, client):
            """Test registration with weak password"""
            user_data = {
                "email": "test@example.com",
                "password": "123",  # Too short
                "full_name": "Test User"
            }
    
            # Note: This test assumes password validation is implemented
            # You might need to add password strength validation
            response = client.post("/auth/register", json=user_data)
            # Should either succeed (if no validation) or fail with appropriate error
            assert response.status_code in [201, 400, 422]
    
        def test_login_success(self, client):
            """Test successful login"""
            # First register a user
            user_data = {
                "email": "login@example.com",
                "password": "password123",
                "full_name": "Login User"
            }
            client.post("/auth/register", json=user_data)
    
            # Then login
            login_data = {
                "username": user_data["email"],
                "password": user_data["password"]
            }
    
            response = client.post("/auth/login", data=login_data)
    
            assert response.status_code == 200
            data = response.json()
            assert "access_token" in data
            assert "refresh_token" in data
            assert data["token_type"] == "bearer"
    
            # Verify token is valid by accessing protected endpoint
            headers = {"Authorization": f"Bearer {data['access_token']}"}
            profile_response = client.get("/users/profile", headers=headers)
            assert profile_response.status_code == 200
    
        def test_login_invalid_credentials(self, client):
            """Test login with invalid credentials"""
            # Register a user first
            user_data = {
                "email": "test@example.com",
                "password": "correct_password",
                "full_name": "Test User"
            }
            client.post("/auth/register", json=user_data)
    
            # Try to login with wrong password
            login_data = {
                "username": user_data["email"],
                "password": "wrong_password"
            }
    
            response = client.post("/auth/login", data=login_data)
            assert response.status_code == 401
            assert "invalid" in response.json()["detail"].lower()
    
        def test_login_nonexistent_user(self, client):
            """Test login with nonexistent user"""
            login_data = {
                "username": "nonexistent@example.com",
                "password": "password123"
            }
    
            response = client.post("/auth/login", data=login_data)
            assert response.status_code == 401
    
        def test_protected_route_with_valid_token(self, client):
            """Test accessing protected route with valid token"""
            # Register and login to get token
            user_data = {
                "email": "protected@example.com",
                "password": "password123",
                "full_name": "Protected User"
            }
            client.post("/auth/register", json=user_data)
    
            login_response = client.post("/auth/login", data={
                "username": user_data["email"],
                "password": user_data["password"]
            })
            token = login_response.json()["access_token"]
    
            # Access protected route
            headers = {"Authorization": f"Bearer {token}"}
            response = client.get("/users/profile", headers=headers)
    
            assert response.status_code == 200
            data = response.json()
            assert data["email"] == user_data["email"]
            assert data["full_name"] == user_data["full_name"]
    
        def test_protected_route_without_token(self, client):
            """Test accessing protected route without token"""
            response = client.get("/users/profile")
            assert response.status_code == 403  # Forbidden
    
        def test_protected_route_with_invalid_token(self, client):
            """Test accessing protected route with invalid token"""
            headers = {"Authorization": "Bearer invalid_token_here"}
            response = client.get("/users/profile", headers=headers)
            assert response.status_code == 401  # Unauthorized
    
        def test_refresh_token_success(self, client):
            """Test successful token refresh"""
            # Register and login to get tokens
            user_data = {
                "email": "refresh@example.com",
                "password": "password123",
                "full_name": "Refresh User"
            }
            client.post("/auth/register", json=user_data)
    
            login_response = client.post("/auth/login", data={
                "username": user_data["email"],
                "password": user_data["password"]
            })
            refresh_token = login_response.json()["refresh_token"]
    
            # Refresh access token
            refresh_data = {"refresh_token": refresh_token}
            response = client.post("/auth/refresh", json=refresh_data)
    
            assert response.status_code == 200
            data = response.json()
            assert "access_token" in data
            assert data["token_type"] == "bearer"
    
            # Verify new token works
            headers = {"Authorization": f"Bearer {data['access_token']}"}
            profile_response = client.get("/users/profile", headers=headers)
            assert profile_response.status_code == 200
    
        def test_refresh_token_invalid(self, client):
            """Test token refresh with invalid refresh token"""
            refresh_data = {"refresh_token": "invalid_refresh_token"}
            response = client.post("/auth/refresh", json=refresh_data)
            assert response.status_code == 401
    
        def test_logout_success(self, client):
            """Test successful logout"""
            # Register and login
            user_data = {
                "email": "logout@example.com",
                "password": "password123",
                "full_name": "Logout User"
            }
            client.post("/auth/register", json=user_data)
    
            login_response = client.post("/auth/login", data={
                "username": user_data["email"],
                "password": user_data["password"]
            })
            token = login_response.json()["access_token"]
    
            # Logout
            headers = {"Authorization": f"Bearer {token}"}
            response = client.post("/auth/logout", headers=headers)
            assert response.status_code == 200
    Python

    Frontend Testing with Jest

    Setup Jest Testing Environment

    Create frontend/package.json:

    {
      "name": "jwt-frontend-tests",
      "version": "1.0.0",
      "description": "Frontend tests for JWT authentication",
      "scripts": {
        "test": "jest",
        "test:watch": "jest --watch",
        "test:coverage": "jest --coverage"
      },
      "devDependencies": {
        "jest": "^29.0.0",
        "jest-environment-jsdom": "^29.0.0",
        "@testing-library/jest-dom": "^5.16.0",
        "fetch-mock": "^9.11.0"
      },
      "jest": {
        "testEnvironment": "jsdom",
        "setupFilesAfterEnv": ["<rootDir>/tests/setup.js"],
        "collectCoverageFrom": [
          "js/**/*.js",
          "!js/main.js"
        ]
      }
    }
    JSON

    Create frontend/tests/setup.js:

    // Jest setup file
    import '@testing-library/jest-dom';
    import fetchMock from 'fetch-mock';
    
    // Mock localStorage
    const localStorageMock = {
      getItem: jest.fn(),
      setItem: jest.fn(),
      removeItem: jest.fn(),
      clear: jest.fn(),
    };
    global.localStorage = localStorageMock;
    
    // Mock sessionStorage
    const sessionStorageMock = {
      getItem: jest.fn(),
      setItem: jest.fn(),
      removeItem: jest.fn(),
      clear: jest.fn(),
    };
    global.sessionStorage = sessionStorageMock;
    
    // Setup fetch mock
    global.fetch = fetchMock.sandbox();
    
    // Reset mocks before each test
    beforeEach(() => {
      fetchMock.reset();
      localStorage.clear();
      sessionStorage.clear();
    });
    JavaScript

    API Client Tests

    Create frontend/tests/api.test.js:

    import fetchMock from 'fetch-mock';
    
    // Mock the API client (you'd import the actual implementation)
    class ApiClient {
      constructor(baseUrl = 'http://localhost:8000') {
        this.baseUrl = baseUrl;
        this.token = null;
      }
    
      getStoredToken() {
        return localStorage.getItem('access_token');
      }
    
      storeToken(token) {
        localStorage.setItem('access_token', token);
        this.token = token;
      }
    
      removeToken() {
        localStorage.removeItem('access_token');
        localStorage.removeItem('refresh_token');
        this.token = null;
      }
    
      async request(endpoint, options = {}) {
        const url = `${this.baseUrl}${endpoint}`;
        const response = await fetch(url, options);
    
        if (!response.ok) {
          const error = await response.json();
          throw new Error(error.detail || 'Request failed');
        }
    
        return response.json();
      }
    
      async login(email, password) {
        const formData = new FormData();
        formData.append('username', email);
        formData.append('password', password);
    
        const response = await fetch(`${this.baseUrl}/auth/login`, {
          method: 'POST',
          body: formData,
        });
    
        if (!response.ok) {
          const error = await response.json();
          throw new Error(error.detail || 'Login failed');
        }
    
        const data = await response.json();
        this.storeToken(data.access_token);
        localStorage.setItem('refresh_token', data.refresh_token);
        return data;
      }
    }
    
    describe('ApiClient', () => {
      let api;
    
      beforeEach(() => {
        api = new ApiClient();
        fetchMock.reset();
      });
    
      describe('Token Management', () => {
        test('should store token in localStorage', () => {
          const token = 'test.jwt.token';
          api.storeToken(token);
    
          expect(localStorage.setItem).toHaveBeenCalledWith('access_token', token);
          expect(api.token).toBe(token);
        });
    
        test('should retrieve token from localStorage', () => {
          const token = 'stored.jwt.token';
          localStorage.getItem.mockReturnValue(token);
    
          const retrieved = api.getStoredToken();
          expect(retrieved).toBe(token);
          expect(localStorage.getItem).toHaveBeenCalledWith('access_token');
        });
    
        test('should remove tokens from localStorage', () => {
          api.removeToken();
    
          expect(localStorage.removeItem).toHaveBeenCalledWith('access_token');
          expect(localStorage.removeItem).toHaveBeenCalledWith('refresh_token');
          expect(api.token).toBeNull();
        });
      });
    
      describe('Login', () => {
        test('should login successfully', async () => {
          const mockResponse = {
            access_token: 'mock.access.token',
            refresh_token: 'mock.refresh.token',
            token_type: 'bearer'
          };
    
          fetchMock.post('http://localhost:8000/auth/login', {
            status: 200,
            body: mockResponse
          });
    
          const result = await api.login('test@example.com', 'password123');
    
          expect(result).toEqual(mockResponse);
          expect(localStorage.setItem).toHaveBeenCalledWith('access_token', mockResponse.access_token);
          expect(localStorage.setItem).toHaveBeenCalledWith('refresh_token', mockResponse.refresh_token);
        });
    
        test('should handle login failure', async () => {
          fetchMock.post('http://localhost:8000/auth/login', {
            status: 401,
            body: { detail: 'Invalid credentials' }
          });
    
          await expect(api.login('test@example.com', 'wrongpassword'))
            .rejects.toThrow('Invalid credentials');
        });
      });
    
      describe('Request Method', () => {
        test('should make successful request', async () => {
          const mockData = { message: 'Success' };
          fetchMock.get('http://localhost:8000/test', {
            status: 200,
            body: mockData
          });
    
          const result = await api.request('/test');
          expect(result).toEqual(mockData);
        });
    
        test('should handle request errors', async () => {
          fetchMock.get('http://localhost:8000/error', {
            status: 400,
            body: { detail: 'Bad request' }
          });
    
          await expect(api.request('/error'))
            .rejects.toThrow('Bad request');
        });
      });
    });
    JavaScript

    End-to-End Testing with Playwright

    Setup Playwright

    Create e2e/package.json:

    {
      "name": "jwt-e2e-tests",
      "version": "1.0.0",
      "scripts": {
        "test": "playwright test",
        "test:headed": "playwright test --headed",
        "test:ui": "playwright test --ui"
      },
      "devDependencies": {
        "@playwright/test": "^1.40.0"
      }
    }
    JSON

    Create e2e/playwright.config.js:

    import { defineConfig, devices } from '@playwright/test';
    
    export default defineConfig({
      testDir: './tests',
      fullyParallel: true,
      forbidOnly: !!process.env.CI,
      retries: process.env.CI ? 2 : 0,
      workers: process.env.CI ? 1 : undefined,
      reporter: 'html',
      use: {
        baseURL: 'http://localhost:5500', // Your frontend URL
        trace: 'on-first-retry',
        screenshot: 'only-on-failure',
      },
    
      projects: [
        {
          name: 'chromium',
          use: { ...devices['Desktop Chrome'] },
        },
        {
          name: 'firefox',
          use: { ...devices['Desktop Firefox'] },
        },
        {
          name: 'webkit',
          use: { ...devices['Desktop Safari'] },
        },
        {
          name: 'Mobile Chrome',
          use: { ...devices['Pixel 5'] },
        },
      ],
    
      webServer: [
        {
          command: 'cd ../backend && uvicorn app.main:app --reload --port 8000',
          port: 8000,
          reuseExistingServer: !process.env.CI,
        },
        {
          command: 'cd ../frontend && python -m http.server 5500',
          port: 5500,
          reuseExistingServer: !process.env.CI,
        },
      ],
    });
    JavaScript

    E2E Authentication Tests

    Create e2e/tests/auth.spec.js:

    import { test, expect } from '@playwright/test';
    
    test.describe('JWT Authentication Flow', () => {
      test.beforeEach(async ({ page }) => {
        // Start with clean state
        await page.context().clearCookies();
        await page.evaluate(() => {
          localStorage.clear();
          sessionStorage.clear();
        });
    
        await page.goto('/');
      });
    
      test('should display login form by default', async ({ page }) => {
        await expect(page.locator('#login-section')).toBeVisible();
        await expect(page.locator('#register-section')).toBeHidden();
        await expect(page.locator('#dashboard-section')).toBeHidden();
      });
    
      test('should register new user successfully', async ({ page }) => {
        // Click register navigation
        await page.click('button:has-text("Register")');
        await expect(page.locator('#register-section')).toBeVisible();
    
        // Fill registration form
        await page.fill('#register-name', 'Test User');
        await page.fill('#register-email', 'test@example.com');
        await page.fill('#register-password', 'password123');
    
        // Submit form
        await page.click('#register-form button[type="submit"]');
    
        // Should show success message and redirect to login
        await expect(page.locator('#status')).toContainText('Registration successful');
        await expect(page.locator('#login-section')).toBeVisible();
      });
    
      test('should login successfully and show dashboard', async ({ page }) => {
        // First register a user (you might want to use API for setup)
        await page.click('button:has-text("Register")');
        await page.fill('#register-name', 'Login User');
        await page.fill('#register-email', 'login@example.com');
        await page.fill('#register-password', 'password123');
        await page.click('#register-form button[type="submit"]');
    
        // Wait for redirect to login
        await expect(page.locator('#login-section')).toBeVisible();
    
        // Login
        await page.fill('#login-email', 'login@example.com');
        await page.fill('#login-password', 'password123');
        await page.click('#login-form button[type="submit"]');
    
        // Should show dashboard
        await expect(page.locator('#dashboard-section')).toBeVisible();
        await expect(page.locator('#user-info')).toContainText('login@example.com');
      });
    
      test('should handle login failure', async ({ page }) => {
        await page.fill('#login-email', 'nonexistent@example.com');
        await page.fill('#login-password', 'wrongpassword');
        await page.click('#login-form button[type="submit"]');
    
        // Should show error message
        await expect(page.locator('#status')).toContainText('Login failed');
        await expect(page.locator('#dashboard-section')).toBeHidden();
      });
    
      test('should logout successfully', async ({ page }) => {
        // Login first (setup)
        await page.click('button:has-text("Register")');
        await page.fill('#register-name', 'Logout User');
        await page.fill('#register-email', 'logout@example.com');
        await page.fill('#register-password', 'password123');
        await page.click('#register-form button[type="submit"]');
    
        await page.fill('#login-email', 'logout@example.com');
        await page.fill('#login-password', 'password123');
        await page.click('#login-form button[type="submit"]');
    
        await expect(page.locator('#dashboard-section')).toBeVisible();
    
        // Logout
        await page.click('#logout-btn');
    
        // Should return to login
        await expect(page.locator('#login-section')).toBeVisible();
        await expect(page.locator('#dashboard-section')).toBeHidden();
      });
    
      test('should persist authentication across page refresh', async ({ page }) => {
        // Login first
        await page.click('button:has-text("Register")');
        await page.fill('#register-name', 'Persist User');
        await page.fill('#register-email', 'persist@example.com');
        await page.fill('#register-password', 'password123');
        await page.click('#register-form button[type="submit"]');
    
        await page.fill('#login-email', 'persist@example.com');
        await page.fill('#login-password', 'password123');
        await page.click('#login-form button[type="submit"]');
    
        await expect(page.locator('#dashboard-section')).toBeVisible();
    
        // Refresh page
        await page.reload();
    
        // Should still be logged in
        await expect(page.locator('#dashboard-section')).toBeVisible();
      });
    
      test('should handle token expiration gracefully', async ({ page }) => {
        // This test would require mocking or using very short token expiry
        // For demonstration, we'll simulate by clearing the token
    
        // Login first
        await page.click('button:has-text("Register")');
        await page.fill('#register-name', 'Expire User');
        await page.fill('#register-email', 'expire@example.com');
        await page.fill('#register-password', 'password123');
        await page.click('#register-form button[type="submit"]');
    
        await page.fill('#login-email', 'expire@example.com');
        await page.fill('#login-password', 'password123');
        await page.click('#login-form button[type="submit"]');
    
        await expect(page.locator('#dashboard-section')).toBeVisible();
    
        // Simulate token expiration by clearing localStorage
        await page.evaluate(() => {
          localStorage.removeItem('access_token');
        });
    
        // Try to access protected data
        await page.click('button:has-text("Load Protected Data")');
    
        // Should redirect to login
        await expect(page.locator('#login-section')).toBeVisible();
      });
    });
    
    test.describe('Responsive Design', () => {
      test('should work on mobile devices', async ({ page }) => {
        await page.setViewportSize({ width: 375, height: 667 }); // iPhone SE
    
        await expect(page.locator('.container')).toBeVisible();
        await expect(page.locator('#login-form')).toBeVisible();
    
        // Test form interaction on mobile
        await page.fill('#login-email', 'mobile@example.com');
        await page.fill('#login-password', 'password123');
    
        // Forms should be usable on mobile
        const emailInput = page.locator('#login-email');
        await expect(emailInput).toHaveValue('mobile@example.com');
      });
    });
    JavaScript

    Security Testing

    JWT Security Tests

    Create backend/tests/test_security.py:

    import pytest
    import jwt
    from datetime import datetime, timedelta
    from app.auth.jwt_handler import JWTHandler
    from app.security.security_middleware import SecurityMiddleware
    from fastapi.testclient import TestClient
    from app.main import app
    
    client = TestClient(app)
    
    class TestJWTSecurity:
    
        @pytest.fixture
        def jwt_handler(self):
            return JWTHandler()
    
        def test_reject_none_algorithm(self, jwt_handler):
            """Test rejection of 'none' algorithm tokens"""
            # Create a token with 'none' algorithm
            payload = {"sub": "user123", "exp": datetime.utcnow() + timedelta(hours=1)}
            none_token = jwt.encode(payload, "", algorithm="none")
    
            # Should reject the token
            decoded = jwt_handler.verify_token(none_token)
            assert decoded is None
    
        def test_algorithm_confusion_attack(self, jwt_handler):
            """Test protection against algorithm confusion attacks"""
            from cryptography.hazmat.primitives import serialization
            from cryptography.hazmat.primitives.asymmetric import rsa
    
            # Generate RSA key pair
            private_key = rsa.generate_private_key(
                public_exponent=65537,
                key_size=2048
            )
            public_key = private_key.public_key()
    
            # Create RS256 token
            payload = {"sub": "user123", "exp": datetime.utcnow() + timedelta(hours=1)}
            rs256_token = jwt.encode(payload, private_key, algorithm="RS256")
    
            # Try to verify with HS256 using public key as secret
            public_pem = public_key.public_bytes(
                encoding=serialization.Encoding.PEM,
                format=serialization.PublicFormat.SubjectPublicKeyInfo
            )
    
            # This should fail - algorithm confusion should be prevented
            try:
                decoded = jwt.decode(rs256_token, public_pem, algorithms=["HS256"])
                assert False, "Algorithm confusion attack succeeded"
            except jwt.InvalidSignatureError:
                pass  # Expected - attack prevented
    
        def test_token_replay_attack(self, jwt_handler):
            """Test protection against token replay attacks"""
            payload = {"sub": "user123"}
            token1 = jwt_handler.create_access_token(payload)
            token2 = jwt_handler.create_access_token(payload)
    
            # Tokens should be different (due to timestamp/nonce)
            assert token1 != token2
    
            # Both should be valid
            assert jwt_handler.verify_token(token1) is not None
            assert jwt_handler.verify_token(token2) is not None
    
        def test_weak_secret_rejection(self):
            """Test rejection of weak secrets"""
            weak_secrets = ["", "123", "password", "a" * 10]
    
            for weak_secret in weak_secrets:
                # In production, you should validate secret strength
                # This is a demonstration of what should be checked
                assert len(weak_secret) < 32  # Minimum recommended length
    
        def test_token_tampering_detection(self, jwt_handler):
            """Test detection of token tampering"""
            payload = {"sub": "user123", "role": "user"}
            token = jwt_handler.create_access_token(payload)
    
            # Tamper with token by changing role
            parts = token.split('.')
            header = parts[0]
            payload_part = parts[1]
            signature = parts[2]
    
            # Decode and modify payload
            import base64
            import json
    
            # Add padding if needed
            payload_part += '=' * (4 - len(payload_part) % 4)
            decoded_payload = json.loads(base64.urlsafe_b64decode(payload_part))
    
            # Tamper with role
            decoded_payload["role"] = "admin"
    
            # Re-encode
            tampered_payload = base64.urlsafe_b64encode(
                json.dumps(decoded_payload).encode()
            ).decode().rstrip('=')
    
            # Create tampered token
            tampered_token = f"{header}.{tampered_payload}.{signature}"
    
            # Should be rejected
            decoded = jwt_handler.verify_token(tampered_token)
            assert decoded is None
    
    class TestSecurityMiddleware:
    
        def test_rate_limiting(self):
            """Test rate limiting functionality"""
            # This would require testing the middleware with multiple requests
            # Simplified test
            for i in range(10):
                response = client.get("/")
                assert response.status_code == 200
    
            # After many requests, should be rate limited
            # (This depends on your rate limiting configuration)
    
        def test_csrf_protection(self):
            """Test CSRF protection"""
            # Attempt POST without CSRF token
            response = client.post("/auth/logout")
            # Should require proper authentication
            assert response.status_code in [401, 403]
    
        def test_xss_prevention(self):
            """Test XSS prevention in inputs"""
            xss_payloads = [
                "<script>alert('xss')</script>",
                "javascript:alert('xss')",
                "<img src=x onerror=alert('xss')>",
                "';alert('xss');//"
            ]
    
            for payload in xss_payloads:
                # Try to register with XSS payload
                response = client.post("/auth/register", json={
                    "email": f"test{payload}@example.com",
                    "password": "password123",
                    "full_name": payload
                })
    
                # Should either reject or sanitize
                if response.status_code == 201:
                    data = response.json()
                    # Full name should be sanitized
                    assert "<script>" not in data["full_name"]
                    assert "javascript:" not in data["full_name"]
    
    class TestPasswordSecurity:
    
        def test_password_hashing_strength(self):
            """Test password hashing strength"""
            jwt_handler = JWTHandler()
            password = "test_password_123"
    
            hashed = jwt_handler.hash_password(password)
    
            # Should use bcrypt
            assert hashed.startswith("$2b$")
    
            # Should be different each time
            hashed2 = jwt_handler.hash_password(password)
            assert hashed != hashed2
    
            # Both should verify correctly
            assert jwt_handler.verify_password(password, hashed)
            assert jwt_handler.verify_password(password, hashed2)
    
        def test_password_timing_attack_resistance(self):
            """Test resistance to timing attacks"""
            import time
            jwt_handler = JWTHandler()
    
            # Hash a password
            real_password = "correct_password"
            hashed = jwt_handler.hash_password(real_password)
    
            # Time verification with correct password
            start = time.time()
            jwt_handler.verify_password(real_password, hashed)
            correct_time = time.time() - start
    
            # Time verification with incorrect password
            start = time.time()
            jwt_handler.verify_password("wrong_password", hashed)
            incorrect_time = time.time() - start
    
            # Times should be similar (within reasonable bounds)
            # bcrypt naturally provides timing attack resistance
            assert abs(correct_time - incorrect_time) < 0.1  # 100ms tolerance
    Python

    Performance Testing

    Load Testing with Locust

    Create tests/performance/locustfile.py:

    from locust import HttpUser, task, between
    import random
    import string
    
    class JWTAuthUser(HttpUser):
        wait_time = between(1, 3)
    
        def on_start(self):
            """Setup user data"""
            self.email = f"user_{random.randint(1000, 9999)}@example.com"
            self.password = "password123"
            self.token = None
    
            # Register user
            self.register()
    
            # Login to get token
            self.login()
    
        def register(self):
            """Register a new user"""
            user_data = {
                "email": self.email,
                "password": self.password,
                "full_name": f"User {random.randint(1000, 9999)}"
            }
    
            response = self.client.post("/auth/register", json=user_data)
            if response.status_code != 201:
                print(f"Registration failed: {response.text}")
    
        def login(self):
            """Login and store token"""
            login_data = {
                "username": self.email,
                "password": self.password
            }
    
            response = self.client.post("/auth/login", data=login_data)
            if response.status_code == 200:
                data = response.json()
                self.token = data["access_token"]
            else:
                print(f"Login failed: {response.text}")
    
        @task(3)
        def access_protected_data(self):
            """Access protected endpoints"""
            if self.token:
                headers = {"Authorization": f"Bearer {self.token}"}
                self.client.get("/users/profile", headers=headers)
    
        @task(1)
        def refresh_token(self):
            """Refresh access token"""
            if self.token:
                # In a real scenario, you'd use the refresh token
                # For this test, we'll just re-login
                self.login()
    
        @task(1)
        def get_protected_data(self):
            """Get protected business data"""
            if self.token:
                headers = {"Authorization": f"Bearer {self.token}"}
                self.client.get("/users/protected-data", headers=headers)
    
    class TokenCreationUser(HttpUser):
        """Focused on testing token creation performance"""
        wait_time = between(0.1, 0.5)
    
        def on_start(self):
            self.email = f"perf_{random.randint(10000, 99999)}@example.com"
            self.password = "password123"
    
            # Register once
            user_data = {
                "email": self.email,
                "password": self.password,
                "full_name": f"Perf User {random.randint(1000, 9999)}"
            }
            self.client.post("/auth/register", json=user_data)
    
        @task
        def login_stress_test(self):
            """Stress test login endpoint"""
            login_data = {
                "username": self.email,
                "password": self.password
            }
            self.client.post("/auth/login", data=login_data)
    Python

    Testing Configuration and CI/CD

    GitHub Actions Workflow

    Create .github/workflows/test.yml:

    name: JWT Authentication Tests
    
    on:
      push:
        branches: [ main, develop ]
      pull_request:
        branches: [ main ]
    
    jobs:
      backend-tests:
        runs-on: ubuntu-latest
    
        services:
          postgres:
            image: postgres:13
            env:
              POSTGRES_PASSWORD: postgres
              POSTGRES_DB: test_db
            options: >-
              --health-cmd pg_isready
              --health-interval 10s
              --health-timeout 5s
              --health-retries 5
    
        steps:
        - uses: actions/checkout@v3
    
        - name: Set up Python
          uses: actions/setup-python@v4
          with:
            python-version: '3.11'
    
        - name: Install dependencies
          run: |
            cd backend
            python -m pip install --upgrade pip
            pip install -r requirements.txt
            pip install pytest pytest-cov pytest-asyncio
    
        - name: Run unit tests
          run: |
            cd backend
            pytest tests/ -v --cov=app --cov-report=xml
    
        - name: Upload coverage to Codecov
          uses: codecov/codecov-action@v3
          with:
            file: ./backend/coverage.xml
    
      frontend-tests:
        runs-on: ubuntu-latest
    
        steps:
        - uses: actions/checkout@v3
    
        - name: Set up Node.js
          uses: actions/setup-node@v3
          with:
            node-version: '18'
    
        - name: Install dependencies
          run: |
            cd frontend
            npm ci
    
        - name: Run tests
          run: |
            cd frontend
            npm test -- --coverage --watchAll=false
    
      e2e-tests:
        runs-on: ubuntu-latest
    
        steps:
        - uses: actions/checkout@v3
    
        - name: Set up Python
          uses: actions/setup-python@v4
          with:
            python-version: '3.11'
    
        - name: Set up Node.js
          uses: actions/setup-node@v3
          with:
            node-version: '18'
    
        - name: Install Python dependencies
          run: |
            cd backend
            python -m pip install --upgrade pip
            pip install -r requirements.txt
    
        - name: Install Playwright
          run: |
            cd e2e
            npm ci
            npx playwright install
    
        - name: Run E2E tests
          run: |
            cd e2e
            npm test
    
      security-tests:
        runs-on: ubuntu-latest
    
        steps:
        - uses: actions/checkout@v3
    
        - name: Run OWASP ZAP Scan
          uses: zaproxy/action-baseline@v0.7.0
          with:
            target: 'http://localhost:8000'
    
        - name: Run Bandit Security Scan
          run: |
            cd backend
            pip install bandit
            bandit -r app/ -f json -o bandit-report.json
    
        - name: Upload security reports
          uses: actions/upload-artifact@v3
          with:
            name: security-reports
            path: |
              backend/bandit-report.json
              report_html.html
    YAML

    This comprehensive testing guide covers:

    1. Unit Testing: JWT handlers, password hashing, token validation
    2. Integration Testing: API endpoints, database interactions, authentication flows
    3. Frontend Testing: JavaScript components, API client, user interactions
    4. End-to-End Testing: Complete user journeys, cross-browser compatibility
    5. Security Testing: Token security, algorithm confusion, XSS/CSRF protection
    6. Performance Testing: Load testing, stress testing, token creation performance
    7. CI/CD Integration: Automated testing workflows and security scans

    The testing strategy ensures your JWT authentication system is robust, secure, and performant in production environments.


    20. JWT in Microservices Architecture

    This chapter covers implementing JWT authentication in microservices environments, including service-to-service authentication, API gateway patterns, and distributed security strategies.

    Microservices Authentication Architecture

    graph TB
        A[Client] --> B[API Gateway]
        B --> C[Auth Service]
        B --> D[User Service]
        B --> E[Order Service]
        B --> F[Payment Service]
        B --> G[Notification Service]
    
        C --> H[Identity Provider]
        C --> I[Token Store/Cache]
    
        D -.->|Service-to-Service| E
        E -.->|Service-to-Service| F
        F -.->|Service-to-Service| G
    
        subgraph "Security Layer"
            J[JWT Validation]
            K[Rate Limiting]
            L[Authorization]
        end
    
        B --> J
        J --> K
        K --> L

    API Gateway with JWT Authentication

    Gateway Implementation with FastAPI

    Create api-gateway/app/main.py:

    from fastapi import FastAPI, Depends, HTTPException, status, Request
    from fastapi.middleware.cors import CORSMiddleware
    from fastapi.responses import JSONResponse
    import httpx
    import asyncio
    from typing import Dict, Optional
    import time
    import logging
    from functools import wraps
    
    from app.auth.jwt_middleware import JWTMiddleware
    from app.auth.service_registry import ServiceRegistry
    from app.config import settings
    from app.middleware.rate_limiter import RateLimiter
    from app.middleware.circuit_breaker import CircuitBreaker
    
    # Initialize FastAPI app
    app = FastAPI(
        title="API Gateway",
        description="Centralized API Gateway with JWT authentication",
        version="1.0.0"
    )
    
    # Add CORS middleware
    app.add_middleware(
        CORSMiddleware,
        allow_origins=settings.ALLOWED_ORIGINS,
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )
    
    # Add custom middleware
    app.add_middleware(JWTMiddleware)
    
    # Initialize components
    service_registry = ServiceRegistry()
    rate_limiter = RateLimiter()
    circuit_breaker = CircuitBreaker()
    
    # Setup logging
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger(__name__)
    
    class APIGateway:
        def __init__(self):
            self.http_client = httpx.AsyncClient(timeout=30.0)
            self.service_registry = service_registry
    
        async def forward_request(
            self, 
            service_name: str, 
            path: str, 
            method: str, 
            headers: Dict[str, str],
            params: Dict = None,
            data: bytes = None
        ) -> Dict:
            """Forward request to microservice"""
    
            # Get service URL from registry
            service_url = await self.service_registry.get_service_url(service_name)
            if not service_url:
                raise HTTPException(
                    status_code=503,
                    detail=f"Service {service_name} unavailable"
                )
    
            # Build request URL
            url = f"{service_url}{path}"
    
            # Prepare headers (remove host-specific headers)
            filtered_headers = {
                k: v for k, v in headers.items() 
                if k.lower() not in ['host', 'content-length']
            }
    
            # Add service authentication
            filtered_headers.update(await self.get_service_auth_headers(service_name))
    
            try:
                # Forward request with circuit breaker
                response = await circuit_breaker.call(
                    service_name,
                    self._make_request,
                    method=method,
                    url=url,
                    headers=filtered_headers,
                    params=params,
                    content=data
                )
    
                return {
                    "status_code": response.status_code,
                    "headers": dict(response.headers),
                    "content": response.content,
                    "json": response.json() if self._is_json_response(response) else None
                }
    
            except httpx.RequestError as e:
                logger.error(f"Request to {service_name} failed: {e}")
                raise HTTPException(
                    status_code=503,
                    detail=f"Service {service_name} request failed"
                )
    
        async def _make_request(self, **kwargs):
            """Make HTTP request to service"""
            return await self.http_client.request(**kwargs)
    
        def _is_json_response(self, response) -> bool:
            """Check if response is JSON"""
            content_type = response.headers.get('content-type', '')
            return 'application/json' in content_type
    
        async def get_service_auth_headers(self, service_name: str) -> Dict[str, str]:
            """Get authentication headers for service-to-service communication"""
            # Generate service token or get from cache
            service_token = await self.service_registry.get_service_token(service_name)
            return {
                "X-Service-Token": service_token,
                "X-Gateway-ID": settings.GATEWAY_ID
            }
    
    # Global gateway instance
    gateway = APIGateway()
    
    # Route handlers
    @app.api_route("/auth/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH"])
    async def auth_service(request: Request, path: str):
        """Route to authentication service"""
    
        # Extract request data
        headers = dict(request.headers)
        params = dict(request.query_params)
        body = await request.body()
    
        # Forward to auth service
        response = await gateway.forward_request(
            service_name="auth-service",
            path=f"/{path}",
            method=request.method,
            headers=headers,
            params=params,
            data=body
        )
    
        return JSONResponse(
            status_code=response["status_code"],
            content=response["json"] or response["content"],
            headers=response["headers"]
        )
    
    @app.api_route("/users/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH"])
    async def user_service(request: Request, path: str, current_user: Dict = Depends(JWTMiddleware.get_current_user)):
        """Route to user service (requires authentication)"""
    
        # Add user context to headers
        headers = dict(request.headers)
        headers["X-User-ID"] = str(current_user["sub"])
        headers["X-User-Role"] = current_user.get("role", "user")
    
        params = dict(request.query_params)
        body = await request.body()
    
        # Apply rate limiting
        await rate_limiter.check_limit(
            key=f"user:{current_user['sub']}",
            limit=100,  # 100 requests per hour
            window=3600
        )
    
        response = await gateway.forward_request(
            service_name="user-service",
            path=f"/{path}",
            method=request.method,
            headers=headers,
            params=params,
            data=body
        )
    
        return JSONResponse(
            status_code=response["status_code"],
            content=response["json"] or response["content"]
        )
    
    @app.api_route("/orders/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH"])
    async def order_service(request: Request, path: str, current_user: Dict = Depends(JWTMiddleware.get_current_user)):
        """Route to order service (requires authentication)"""
    
        headers = dict(request.headers)
        headers["X-User-ID"] = str(current_user["sub"])
        headers["X-User-Role"] = current_user.get("role", "user")
    
        params = dict(request.query_params)
        body = await request.body()
    
        response = await gateway.forward_request(
            service_name="order-service",
            path=f"/{path}",
            method=request.method,
            headers=headers,
            params=params,
            data=body
        )
    
        return JSONResponse(
            status_code=response["status_code"],
            content=response["json"] or response["content"]
        )
    
    @app.get("/health")
    async def health_check():
        """Gateway health check"""
        service_health = await service_registry.check_all_services()
    
        return {
            "status": "healthy" if all(service_health.values()) else "degraded",
            "services": service_health,
            "timestamp": time.time()
        }
    
    @app.on_event("startup")
    async def startup_event():
        """Initialize gateway on startup"""
        await service_registry.initialize()
        logger.info("API Gateway started successfully")
    
    @app.on_event("shutdown")
    async def shutdown_event():
        """Cleanup on shutdown"""
        await gateway.http_client.aclose()
        logger.info("API Gateway shutdown completed")
    Python

    JWT Middleware for Gateway

    Create api-gateway/app/auth/jwt_middleware.py:

    from fastapi import HTTPException, status, Request
    from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
    import jwt
    from typing import Dict, Optional
    import time
    import redis
    import json
    
    from app.config import settings
    
    class JWTMiddleware:
        def __init__(self):
            self.secret_key = settings.JWT_SECRET_KEY
            self.algorithm = settings.JWT_ALGORITHM
            self.redis_client = redis.Redis.from_url(settings.REDIS_URL) if settings.REDIS_URL else None
    
        async def __call__(self, request: Request, call_next):
            """Middleware to validate JWT tokens"""
    
            # Skip authentication for certain paths
            if self._should_skip_auth(request.url.path):
                return await call_next(request)
    
            # Extract token
            token = self._extract_token(request)
    
            if not token:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Authentication required"
                )
    
            # Validate token
            try:
                payload = await self._validate_token(token)
    
                # Add user context to request
                request.state.current_user = payload
    
                # Continue processing
                response = await call_next(request)
    
                # Add token refresh header if needed
                if self._token_needs_refresh(payload):
                    response.headers["X-Token-Refresh-Needed"] = "true"
    
                return response
    
            except HTTPException:
                raise
            except Exception as e:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Invalid token"
                )
    
        def _should_skip_auth(self, path: str) -> bool:
            """Check if path should skip authentication"""
            skip_paths = [
                "/auth/login",
                "/auth/register", 
                "/auth/refresh",
                "/health",
                "/docs",
                "/openapi.json"
            ]
    
            return any(path.startswith(skip_path) for skip_path in skip_paths)
    
        def _extract_token(self, request: Request) -> Optional[str]:
            """Extract JWT token from request"""
    
            # Try Authorization header first
            auth_header = request.headers.get("Authorization")
            if auth_header and auth_header.startswith("Bearer "):
                return auth_header.split(" ")[1]
    
            # Try cookie
            token_cookie = request.cookies.get("access_token")
            if token_cookie:
                return token_cookie
    
            # Try query parameter (not recommended for production)
            token_param = request.query_params.get("token")
            if token_param:
                return token_param
    
            return None
    
        async def _validate_token(self, token: str) -> Dict:
            """Validate JWT token"""
    
            # Check token blacklist (if using Redis)
            if self.redis_client and await self._is_token_blacklisted(token):
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Token has been revoked"
                )
    
            # Decode and validate token
            try:
                payload = jwt.decode(
                    token,
                    self.secret_key,
                    algorithms=[self.algorithm]
                )
    
                # Validate required claims
                if not payload.get("sub"):
                    raise HTTPException(
                        status_code=status.HTTP_401_UNAUTHORIZED,
                        detail="Invalid token: missing subject"
                    )
    
                # Check expiration
                if payload.get("exp", 0) < time.time():
                    raise HTTPException(
                        status_code=status.HTTP_401_UNAUTHORIZED,
                        detail="Token has expired"
                    )
    
                return payload
    
            except jwt.ExpiredSignatureError:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Token has expired"
                )
            except jwt.JWTError:
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Invalid token"
                )
    
        async def _is_token_blacklisted(self, token: str) -> bool:
            """Check if token is blacklisted"""
            try:
                # Extract JTI from token (if present)
                payload = jwt.decode(token, options={"verify_signature": False})
                jti = payload.get("jti")
    
                if jti:
                    return await self.redis_client.exists(f"blacklist:{jti}")
    
                return False
            except:
                return False
    
        def _token_needs_refresh(self, payload: Dict) -> bool:
            """Check if token needs refresh"""
            exp = payload.get("exp", 0)
            current_time = time.time()
    
            # Refresh if token expires in less than 5 minutes
            return (exp - current_time) < 300
    
        @staticmethod
        async def get_current_user(request: Request) -> Dict:
            """Dependency to get current user from request state"""
            if not hasattr(request.state, 'current_user'):
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Authentication required"
                )
    
            return request.state.current_user
    Python

    Service-to-Service Authentication

    Service Registry Implementation

    Create api-gateway/app/auth/service_registry.py:

    import asyncio
    import httpx
    import jwt
    import time
    from typing import Dict, List, Optional
    from datetime import datetime, timedelta
    import logging
    
    from app.config import settings
    
    logger = logging.getLogger(__name__)
    
    class ServiceRegistry:
        def __init__(self):
            self.services: Dict[str, Dict] = {}
            self.service_tokens: Dict[str, str] = {}
            self.http_client = httpx.AsyncClient()
    
        async def initialize(self):
            """Initialize service registry"""
    
            # Register known services
            await self.register_service(
                name="auth-service",
                url=settings.AUTH_SERVICE_URL,
                health_endpoint="/health"
            )
    
            await self.register_service(
                name="user-service", 
                url=settings.USER_SERVICE_URL,
                health_endpoint="/health"
            )
    
            await self.register_service(
                name="order-service",
                url=settings.ORDER_SERVICE_URL,
                health_endpoint="/health"
            )
    
            # Start health checking
            asyncio.create_task(self._health_check_loop())
    
            logger.info("Service registry initialized")
    
        async def register_service(
            self, 
            name: str, 
            url: str, 
            health_endpoint: str = "/health"
        ):
            """Register a service"""
    
            self.services[name] = {
                "url": url,
                "health_endpoint": health_endpoint,
                "healthy": True,
                "last_check": time.time(),
                "failures": 0
            }
    
            # Generate service token
            await self._generate_service_token(name)
    
            logger.info(f"Registered service: {name} at {url}")
    
        async def get_service_url(self, service_name: str) -> Optional[str]:
            """Get URL for healthy service"""
    
            service = self.services.get(service_name)
            if not service or not service["healthy"]:
                return None
    
            return service["url"]
    
        async def get_service_token(self, service_name: str) -> Optional[str]:
            """Get authentication token for service"""
    
            token = self.service_tokens.get(service_name)
    
            # Check if token needs refresh
            if not token or self._token_needs_refresh(token):
                await self._generate_service_token(service_name)
                token = self.service_tokens.get(service_name)
    
            return token
    
        async def _generate_service_token(self, service_name: str):
            """Generate service-to-service authentication token"""
    
            payload = {
                "sub": f"service:{service_name}",
                "iss": "api-gateway",
                "aud": service_name,
                "iat": time.time(),
                "exp": time.time() + 3600,  # 1 hour expiry
                "scope": "service-to-service",
                "service_name": service_name
            }
    
            token = jwt.encode(
                payload,
                settings.SERVICE_SECRET_KEY,
                algorithm="HS256"
            )
    
            self.service_tokens[service_name] = token
            logger.debug(f"Generated service token for {service_name}")
    
        def _token_needs_refresh(self, token: str) -> bool:
            """Check if service token needs refresh"""
            try:
                payload = jwt.decode(token, options={"verify_signature": False})
                exp = payload.get("exp", 0)
    
                # Refresh if expires in less than 10 minutes
                return (exp - time.time()) < 600
            except:
                return True
    
        async def check_service_health(self, service_name: str) -> bool:
            """Check health of specific service"""
    
            service = self.services.get(service_name)
            if not service:
                return False
    
            try:
                url = f"{service['url']}{service['health_endpoint']}"
                response = await self.http_client.get(url, timeout=5.0)
    
                healthy = response.status_code == 200
    
                # Update service status
                service["healthy"] = healthy
                service["last_check"] = time.time()
    
                if healthy:
                    service["failures"] = 0
                else:
                    service["failures"] += 1
    
                    # Remove service if too many failures
                    if service["failures"] >= 3:
                        logger.warning(f"Service {service_name} marked as unhealthy after {service['failures']} failures")
    
                return healthy
    
            except Exception as e:
                logger.error(f"Health check failed for {service_name}: {e}")
                service["healthy"] = False
                service["failures"] += 1
                return False
    
        async def check_all_services(self) -> Dict[str, bool]:
            """Check health of all services"""
    
            health_status = {}
    
            for service_name in self.services:
                health_status[service_name] = await self.check_service_health(service_name)
    
            return health_status
    
        async def _health_check_loop(self):
            """Periodic health checking"""
    
            while True:
                try:
                    await self.check_all_services()
                    await asyncio.sleep(30)  # Check every 30 seconds
                except Exception as e:
                    logger.error(f"Health check loop error: {e}")
                    await asyncio.sleep(60)  # Wait longer on error
    Python

    Microservice Implementation

    User Service with Service Authentication

    Create user-service/app/main.py:

    from fastapi import FastAPI, Depends, HTTPException, status, Request
    from fastapi.middleware.cors import CORSMiddleware
    import jwt
    from typing import Dict, Optional
    import time
    
    from app.auth.service_auth import ServiceAuthMiddleware, require_service_auth, get_user_context
    from app.models.user import User
    from app.database import get_db
    from app.config import settings
    
    app = FastAPI(
        title="User Service",
        description="Microservice for user management",
        version="1.0.0"
    )
    
    # Add service authentication middleware
    app.add_middleware(ServiceAuthMiddleware)
    
    # Add CORS
    app.add_middleware(
        CORSMiddleware,
        allow_origins=settings.ALLOWED_ORIGINS,
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )
    
    @app.get("/health")
    async def health_check():
        """Service health check"""
        return {
            "status": "healthy",
            "service": "user-service",
            "timestamp": time.time()
        }
    
    @app.get("/users/profile")
    async def get_user_profile(
        user_context: Dict = Depends(get_user_context),
        service_auth: Dict = Depends(require_service_auth),
        db = Depends(get_db)
    ):
        """Get user profile (requires service authentication)"""
    
        user_id = user_context.get("user_id")
        if not user_id:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="User context required"
            )
    
        # Query user from database
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="User not found"
            )
    
        return {
            "id": user.id,
            "email": user.email,
            "full_name": user.full_name,
            "is_active": user.is_active,
            "created_at": user.created_at.isoformat()
        }
    
    @app.get("/users/{user_id}")
    async def get_user_by_id(
        user_id: int,
        service_auth: Dict = Depends(require_service_auth),
        user_context: Dict = Depends(get_user_context),
        db = Depends(get_db)
    ):
        """Get user by ID (admin or service access only)"""
    
        # Check if requesting user is admin or if it's a service request
        requesting_user_role = user_context.get("role", "user")
        is_service_request = service_auth.get("scope") == "service-to-service"
    
        if not is_service_request and requesting_user_role != "admin":
            # Users can only access their own profile
            requesting_user_id = user_context.get("user_id")
            if str(requesting_user_id) != str(user_id):
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="Access denied"
                )
    
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="User not found"
            )
    
        return {
            "id": user.id,
            "email": user.email,
            "full_name": user.full_name,
            "is_active": user.is_active
        }
    
    @app.post("/users/{user_id}/orders")
    async def create_user_order(
        user_id: int,
        order_data: dict,
        service_auth: Dict = Depends(require_service_auth),
        db = Depends(get_db)
    ):
        """Create order for user (service-to-service only)"""
    
        # This endpoint is only accessible by other services
        if service_auth.get("scope") != "service-to-service":
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Service access required"
            )
    
        # Verify user exists
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="User not found"
            )
    
        # Process order creation logic here
        # This would typically involve business logic specific to your application
    
        return {
            "message": "Order created successfully",
            "user_id": user_id,
            "order_data": order_data
        }
    
    @app.get("/internal/users/batch")
    async def get_users_batch(
        user_ids: str,  # Comma-separated IDs
        service_auth: Dict = Depends(require_service_auth),
        db = Depends(get_db)
    ):
        """Get multiple users by IDs (internal service use only)"""
    
        # Only allow service-to-service calls
        if service_auth.get("scope") != "service-to-service":
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Internal endpoint - service access required"
            )
    
        # Parse user IDs
        try:
            ids = [int(id.strip()) for id in user_ids.split(",")]
        except ValueError:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Invalid user IDs format"
            )
    
        # Query users
        users = db.query(User).filter(User.id.in_(ids)).all()
    
        return {
            "users": [
                {
                    "id": user.id,
                    "email": user.email,
                    "full_name": user.full_name,
                    "is_active": user.is_active
                }
                for user in users
            ]
        }
    Python

    Service Authentication Middleware

    Create user-service/app/auth/service_auth.py:

    from fastapi import HTTPException, status, Request, Depends
    import jwt
    from typing import Dict, Optional
    import time
    
    from app.config import settings
    
    class ServiceAuthMiddleware:
        def __init__(self):
            self.service_secret = settings.SERVICE_SECRET_KEY
    
        async def __call__(self, request: Request, call_next):
            """Middleware to validate service-to-service authentication"""
    
            # Skip for health checks
            if request.url.path == "/health":
                return await call_next(request)
    
            # Extract service token
            service_token = request.headers.get("X-Service-Token")
    
            if service_token:
                # Validate service token
                try:
                    service_payload = jwt.decode(
                        service_token,
                        self.service_secret,
                        algorithms=["HS256"]
                    )
    
                    # Store service context
                    request.state.service_auth = service_payload
    
                except jwt.JWTError:
                    raise HTTPException(
                        status_code=status.HTTP_401_UNAUTHORIZED,
                        detail="Invalid service token"
                    )
    
            # Extract user context from gateway headers
            user_id = request.headers.get("X-User-ID")
            user_role = request.headers.get("X-User-Role")
    
            if user_id:
                request.state.user_context = {
                    "user_id": int(user_id),
                    "role": user_role or "user"
                }
    
            return await call_next(request)
    
    async def require_service_auth(request: Request) -> Dict:
        """Dependency to require service authentication"""
    
        if not hasattr(request.state, 'service_auth'):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Service authentication required"
            )
    
        return request.state.service_auth
    
    async def get_user_context(request: Request) -> Dict:
        """Dependency to get user context from gateway"""
    
        if not hasattr(request.state, 'user_context'):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="User context not provided by gateway"
            )
    
        return request.state.user_context
    
    def optional_user_context(request: Request) -> Optional[Dict]:
        """Optional user context dependency"""
    
        if hasattr(request.state, 'user_context'):
            return request.state.user_context
    
        return None
    Python

    Inter-Service Communication

    Service Communication Helper

    Create shared/service_client.py:

    import httpx
    import jwt
    import time
    from typing import Dict, Optional, Any
    import asyncio
    from functools import wraps
    
    class ServiceClient:
        def __init__(self, service_name: str, base_url: str, secret_key: str):
            self.service_name = service_name
            self.base_url = base_url
            self.secret_key = secret_key
            self.http_client = httpx.AsyncClient(timeout=30.0)
            self._service_token = None
            self._token_expires = 0
    
        async def __aenter__(self):
            return self
    
        async def __aexit__(self, exc_type, exc_val, exc_tb):
            await self.http_client.aclose()
    
        def _generate_service_token(self) -> str:
            """Generate service authentication token"""
    
            now = time.time()
            payload = {
                "sub": f"service:{self.service_name}",
                "iat": now,
                "exp": now + 3600,  # 1 hour
                "scope": "service-to-service"
            }
    
            token = jwt.encode(payload, self.secret_key, algorithm="HS256")
            self._service_token = token
            self._token_expires = now + 3600
    
            return token
    
        def _get_service_token(self) -> str:
            """Get valid service token"""
    
            if not self._service_token or time.time() >= (self._token_expires - 300):
                return self._generate_service_token()
    
            return self._service_token
    
        async def request(
            self, 
            method: str, 
            endpoint: str, 
            data: Any = None,
            params: Dict = None,
            headers: Dict = None,
            **kwargs
        ) -> httpx.Response:
            """Make authenticated request to service"""
    
            url = f"{self.base_url}{endpoint}"
    
            # Prepare headers
            request_headers = {
                "X-Service-Token": self._get_service_token(),
                "X-Service-Name": self.service_name,
                "Content-Type": "application/json"
            }
    
            if headers:
                request_headers.update(headers)
    
            # Prepare request data
            request_kwargs = {
                "method": method,
                "url": url,
                "headers": request_headers,
                "params": params,
                **kwargs
            }
    
            if data is not None:
                if isinstance(data, dict):
                    request_kwargs["json"] = data
                else:
                    request_kwargs["content"] = data
    
            # Make request with retry logic
            return await self._request_with_retry(**request_kwargs)
    
        async def _request_with_retry(self, max_retries: int = 3, **kwargs) -> httpx.Response:
            """Make request with retry logic"""
    
            last_exception = None
    
            for attempt in range(max_retries):
                try:
                    response = await self.http_client.request(**kwargs)
    
                    if response.status_code < 500:  # Don't retry client errors
                        return response
    
                    # Server error - retry after delay
                    if attempt < max_retries - 1:
                        await asyncio.sleep(2 ** attempt)  # Exponential backoff
    
                except httpx.RequestError as e:
                    last_exception = e
                    if attempt < max_retries - 1:
                        await asyncio.sleep(2 ** attempt)
    
            # All retries failed
            if last_exception:
                raise last_exception
    
            return response
    
        # Convenience methods
        async def get(self, endpoint: str, **kwargs) -> httpx.Response:
            return await self.request("GET", endpoint, **kwargs)
    
        async def post(self, endpoint: str, data: Any = None, **kwargs) -> httpx.Response:
            return await self.request("POST", endpoint, data=data, **kwargs)
    
        async def put(self, endpoint: str, data: Any = None, **kwargs) -> httpx.Response:
            return await self.request("PUT", endpoint, data=data, **kwargs)
    
        async def delete(self, endpoint: str, **kwargs) -> httpx.Response:
            return await self.request("DELETE", endpoint, **kwargs)
    
    # Service client factory
    class ServiceClientFactory:
        def __init__(self, secret_key: str):
            self.secret_key = secret_key
            self.clients: Dict[str, ServiceClient] = {}
    
        def get_client(self, service_name: str, base_url: str) -> ServiceClient:
            """Get or create service client"""
    
            key = f"{service_name}:{base_url}"
    
            if key not in self.clients:
                self.clients[key] = ServiceClient(
                    service_name=service_name,
                    base_url=base_url,
                    secret_key=self.secret_key
                )
    
            return self.clients[key]
    
        async def close_all(self):
            """Close all client connections"""
            for client in self.clients.values():
                await client.http_client.aclose()
    
    # Usage example in order service
    class OrderService:
        def __init__(self, service_factory: ServiceClientFactory):
            self.user_client = service_factory.get_client(
                "order-service",
                "http://user-service:8000"
            )
            self.payment_client = service_factory.get_client(
                "order-service", 
                "http://payment-service:8000"
            )
    
        async def create_order(self, user_id: int, order_data: Dict) -> Dict:
            """Create order with user validation and payment processing"""
    
            # 1. Validate user exists
            user_response = await self.user_client.get(f"/users/{user_id}")
            if user_response.status_code != 200:
                raise HTTPException(
                    status_code=400,
                    detail="Invalid user"
                )
    
            user_data = user_response.json()
    
            # 2. Process payment
            payment_data = {
                "user_id": user_id,
                "amount": order_data["total_amount"],
                "payment_method": order_data["payment_method"]
            }
    
            payment_response = await self.payment_client.post(
                "/payments/process",
                data=payment_data
            )
    
            if payment_response.status_code != 200:
                raise HTTPException(
                    status_code=400,
                    detail="Payment processing failed"
                )
    
            # 3. Create order record
            order = {
                "user_id": user_id,
                "user_email": user_data["email"],
                "items": order_data["items"],
                "total_amount": order_data["total_amount"],
                "payment_id": payment_response.json()["payment_id"],
                "status": "confirmed"
            }
    
            # Save to database and return
            return order
    Python

    Distributed Tracing and Monitoring

    Request Tracing Middleware

    Create shared/tracing_middleware.py:

    import time
    import uuid
    from fastapi import Request
    import logging
    from typing import Dict, Optional
    
    logger = logging.getLogger(__name__)
    
    class TracingMiddleware:
        def __init__(self):
            self.service_name = None
    
        def __call__(self, service_name: str):
            self.service_name = service_name
            return self._middleware
    
        async def _middleware(self, request: Request, call_next):
            """Distributed tracing middleware"""
    
            # Generate or extract trace ID
            trace_id = request.headers.get("X-Trace-ID") or str(uuid.uuid4())
            span_id = str(uuid.uuid4())
            parent_span_id = request.headers.get("X-Span-ID")
    
            # Start timing
            start_time = time.time()
    
            # Add tracing headers to request state
            request.state.trace_id = trace_id
            request.state.span_id = span_id
            request.state.parent_span_id = parent_span_id
    
            # Log request start
            logger.info(
                "Request started",
                extra={
                    "trace_id": trace_id,
                    "span_id": span_id,
                    "parent_span_id": parent_span_id,
                    "service": self.service_name,
                    "method": request.method,
                    "path": request.url.path,
                    "user_agent": request.headers.get("User-Agent")
                }
            )
    
            try:
                # Process request
                response = await call_next(request)
    
                # Calculate duration
                duration = time.time() - start_time
    
                # Log successful response
                logger.info(
                    "Request completed",
                    extra={
                        "trace_id": trace_id,
                        "span_id": span_id,
                        "service": self.service_name,
                        "status_code": response.status_code,
                        "duration_ms": round(duration * 1000, 2)
                    }
                )
    
                # Add tracing headers to response
                response.headers["X-Trace-ID"] = trace_id
                response.headers["X-Span-ID"] = span_id
    
                return response
    
            except Exception as e:
                # Calculate duration
                duration = time.time() - start_time
    
                # Log error
                logger.error(
                    "Request failed",
                    extra={
                        "trace_id": trace_id,
                        "span_id": span_id,
                        "service": self.service_name,
                        "error": str(e),
                        "duration_ms": round(duration * 1000, 2)
                    }
                )
    
                raise
    
    # Usage in services
    def setup_tracing(app, service_name: str):
        """Setup tracing for a service"""
        tracing_middleware = TracingMiddleware()
        app.add_middleware(tracing_middleware(service_name))
    Python

    This microservices architecture provides:

    1. Centralized Authentication: API Gateway handles JWT validation
    2. Service-to-Service Security: Dedicated tokens for inter-service communication
    3. Service Discovery: Dynamic service registration and health checking
    4. Circuit Breaking: Fault tolerance for service communication
    5. Distributed Tracing: Request tracking across service boundaries
    6. Rate Limiting: Per-user and per-service rate limits
    7. Secure Communication: Authenticated and authorized service calls

    The architecture ensures secure, scalable, and maintainable microservices with proper JWT authentication throughout the system.


    21. Performance Optimization

    This chapter covers advanced performance optimization techniques for JWT systems, including token size optimization, caching strategies, performance monitoring, and scalability best practices.

    JWT Token Size Optimization

    Understanding Token Size Impact

    graph TB
        A[JWT Token Size] --> B[Network Overhead]
        A --> C[Storage Requirements]
        A --> D[Processing Time]
        A --> E[Mobile Performance]
    
        B --> F[Request Latency]
        C --> G[Cookie Limits]
        D --> H[CPU Usage]
        E --> I[Battery Impact]
    
        subgraph "Optimization Strategies"
            J[Claim Minimization]
            K[Compact Encoding]
            L[Reference Tokens]
            M[Token Compression]
        end
    
        F --> J
        G --> K
        H --> L
        I --> M

    Token Size Analysis Tool

    Create performance/token_analyzer.py:

    import jwt
    import json
    import base64
    import gzip
    from typing import Dict, Any, List, Tuple
    from datetime import datetime, timedelta
    import time
    
    class JWTPerformanceAnalyzer:
        def __init__(self):
            self.secret_key = "your-secret-key"
            self.algorithm = "HS256"
    
        def analyze_token_size(self, payload: Dict[str, Any]) -> Dict[str, Any]:
            """Analyze JWT token size and performance characteristics"""
    
            # Generate token
            token = jwt.encode(payload, self.secret_key, algorithm=self.algorithm)
    
            # Basic measurements
            token_bytes = len(token.encode('utf-8'))
    
            # Decode token parts
            header, payload_data, signature = token.split('.')
    
            # Analyze each part
            header_size = len(header.encode('utf-8'))
            payload_size = len(payload_data.encode('utf-8'))
            signature_size = len(signature.encode('utf-8'))
    
            # Decode payload for analysis
            decoded_payload = json.loads(
                base64.urlsafe_b64decode(payload_data + '==').decode('utf-8')
            )
    
            # Calculate compression potential
            payload_json = json.dumps(decoded_payload, separators=(',', ':'))
            compressed = gzip.compress(payload_json.encode('utf-8'))
            compression_ratio = len(compressed) / len(payload_json)
    
            # Network impact analysis
            http_overhead = self._calculate_http_overhead(token_bytes)
    
            return {
                "token_size": {
                    "total_bytes": token_bytes,
                    "header_bytes": header_size,
                    "payload_bytes": payload_size,
                    "signature_bytes": signature_size,
                    "base64_overhead": self._calculate_base64_overhead(payload_json)
                },
                "payload_analysis": {
                    "claim_count": len(decoded_payload),
                    "json_size": len(payload_json),
                    "compressed_size": len(compressed),
                    "compression_ratio": compression_ratio,
                    "claims_breakdown": self._analyze_claims(decoded_payload)
                },
                "network_impact": {
                    "http_overhead_bytes": http_overhead,
                    "total_request_size": token_bytes + http_overhead,
                    "cookie_limit_usage": (token_bytes / 4096) * 100,  # 4KB cookie limit
                    "mobile_3g_transfer_time_ms": self._estimate_transfer_time(token_bytes, "3g"),
                    "mobile_4g_transfer_time_ms": self._estimate_transfer_time(token_bytes, "4g")
                },
                "recommendations": self._generate_recommendations(decoded_payload, token_bytes)
            }
    
        def _calculate_base64_overhead(self, original_size: int) -> float:
            """Calculate Base64 encoding overhead"""
            return (original_size * 4 / 3) - original_size
    
        def _calculate_http_overhead(self, token_size: int) -> int:
            """Estimate HTTP header overhead"""
            # Authorization: Bearer <token>
            return len("Authorization: Bearer ") + 20  # Additional headers
    
        def _estimate_transfer_time(self, bytes_size: int, network_type: str) -> float:
            """Estimate transfer time based on network conditions"""
    
            speeds = {
                "3g": 0.5 * 1024 * 1024 / 8,  # 0.5 Mbps in bytes/sec
                "4g": 10 * 1024 * 1024 / 8,   # 10 Mbps in bytes/sec
                "wifi": 50 * 1024 * 1024 / 8  # 50 Mbps in bytes/sec
            }
    
            speed = speeds.get(network_type, speeds["4g"])
            return (bytes_size / speed) * 1000  # Convert to milliseconds
    
        def _analyze_claims(self, payload: Dict[str, Any]) -> Dict[str, int]:
            """Analyze individual claim sizes"""
    
            claims_size = {}
            for key, value in payload.items():
                claim_json = json.dumps({key: value}, separators=(',', ':'))
                claims_size[key] = len(claim_json.encode('utf-8'))
    
            return claims_size
    
        def _generate_recommendations(self, payload: Dict[str, Any], token_size: int) -> List[str]:
            """Generate optimization recommendations"""
    
            recommendations = []
    
            # Size-based recommendations
            if token_size > 2048:
                recommendations.append("Token size exceeds 2KB - consider using reference tokens")
    
            if token_size > 1024:
                recommendations.append("Large token size - review claim necessity")
    
            # Claim-based recommendations
            claim_count = len(payload)
            if claim_count > 10:
                recommendations.append(f"High claim count ({claim_count}) - consider claim consolidation")
    
            # Check for common optimization opportunities
            if 'permissions' in payload and isinstance(payload['permissions'], list):
                if len(payload['permissions']) > 5:
                    recommendations.append("Consider using role-based permissions instead of listing all permissions")
    
            if 'groups' in payload and isinstance(payload['groups'], list):
                if len(payload['groups']) > 3:
                    recommendations.append("Large groups list - consider using group IDs instead of names")
    
            # String length checks
            for key, value in payload.items():
                if isinstance(value, str) and len(value) > 100:
                    recommendations.append(f"Long string value in '{key}' claim - consider using references")
    
            return recommendations
    
        def benchmark_algorithms(self, payload: Dict[str, Any]) -> Dict[str, Dict]:
            """Benchmark different JWT algorithms"""
    
            algorithms = ['HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512']
            results = {}
    
            for alg in algorithms:
                try:
                    # Skip RSA algorithms for this simple test
                    if alg.startswith('RS'):
                        continue
    
                    start_time = time.perf_counter()
    
                    # Encode
                    token = jwt.encode(payload, self.secret_key, algorithm=alg)
                    encode_time = time.perf_counter() - start_time
    
                    # Decode
                    start_time = time.perf_counter()
                    jwt.decode(token, self.secret_key, algorithms=[alg])
                    decode_time = time.perf_counter() - start_time
    
                    results[alg] = {
                        "token_size": len(token.encode('utf-8')),
                        "encode_time_ms": encode_time * 1000,
                        "decode_time_ms": decode_time * 1000,
                        "total_time_ms": (encode_time + decode_time) * 1000
                    }
    
                except Exception as e:
                    results[alg] = {"error": str(e)}
    
            return results
    
        def optimize_payload(self, payload: Dict[str, Any]) -> Tuple[Dict[str, Any], Dict[str, str]]:
            """Optimize payload for minimum size"""
    
            optimized = payload.copy()
            optimizations = {}
    
            # Shorten claim names
            claim_mappings = {
                'user_id': 'uid',
                'username': 'usr',
                'email': 'eml',
                'full_name': 'fn',
                'permissions': 'perms',
                'groups': 'grps',
                'issued_at': 'iat',
                'expires_at': 'exp',
                'not_before': 'nbf'
            }
    
            for old_key, new_key in claim_mappings.items():
                if old_key in optimized:
                    optimized[new_key] = optimized.pop(old_key)
                    optimizations[f"Claim name shortening"] = f"'{old_key}' -> '{new_key}'"
    
            # Optimize arrays
            if 'perms' in optimized and isinstance(optimized['perms'], list):
                # Convert permission names to IDs or codes
                perm_map = {
                    'read_users': 'ru',
                    'write_users': 'wu',
                    'delete_users': 'du',
                    'read_orders': 'ro',
                    'write_orders': 'wo',
                    'admin_access': 'aa'
                }
    
                optimized_perms = []
                for perm in optimized['perms']:
                    if perm in perm_map:
                        optimized_perms.append(perm_map[perm])
                    else:
                        optimized_perms.append(perm)
    
                optimized['perms'] = optimized_perms
                optimizations["Permission optimization"] = "Converted to short codes"
    
            # Remove unnecessary claims
            current_time = int(time.time())
            if 'iat' in optimized and abs(optimized['iat'] - current_time) < 60:
                # Remove iat if it's very recent (can be inferred)
                del optimized['iat']
                optimizations["Remove redundant iat"] = "Recent timestamp removed"
    
            return optimized, optimizations
    
    # Usage example
    def analyze_jwt_performance():
        """Example usage of JWT performance analyzer"""
    
        analyzer = JWTPerformanceAnalyzer()
    
        # Sample payload
        sample_payload = {
            "sub": "1234567890",
            "name": "John Doe",
            "email": "john.doe@example.com",
            "iat": int(time.time()),
            "exp": int(time.time()) + 3600,
            "permissions": ["read_users", "write_users", "read_orders"],
            "groups": ["admin", "managers", "developers"],
            "department": "Engineering",
            "location": "San Francisco, CA"
        }
    
        # Analyze original token
        print("=== Original Token Analysis ===")
        original_analysis = analyzer.analyze_token_size(sample_payload)
        print(f"Token size: {original_analysis['token_size']['total_bytes']} bytes")
        print(f"Claim count: {original_analysis['payload_analysis']['claim_count']}")
        print(f"Compression ratio: {original_analysis['payload_analysis']['compression_ratio']:.2f}")
    
        # Optimize payload
        optimized_payload, optimizations = analyzer.optimize_payload(sample_payload)
    
        print("\n=== Optimized Token Analysis ===")
        optimized_analysis = analyzer.analyze_token_size(optimized_payload)
        print(f"Token size: {optimized_analysis['token_size']['total_bytes']} bytes")
        print(f"Size reduction: {original_analysis['token_size']['total_bytes'] - optimized_analysis['token_size']['total_bytes']} bytes")
    
        print("\n=== Optimizations Applied ===")
        for optimization, description in optimizations.items():
            print(f"- {optimization}: {description}")
    
        print("\n=== Recommendations ===")
        for recommendation in original_analysis['recommendations']:
            print(f"- {recommendation}")
    
        # Benchmark algorithms
        print("\n=== Algorithm Benchmark ===")
        benchmark = analyzer.benchmark_algorithms(sample_payload)
        for alg, metrics in benchmark.items():
            if 'error' not in metrics:
                print(f"{alg}: {metrics['token_size']} bytes, "
                      f"{metrics['total_time_ms']:.2f}ms total")
    
    if __name__ == "__main__":
        analyze_jwt_performance()
    Python

    Caching Strategies

    Multi-Level JWT Caching

    Create performance/jwt_cache.py:

    import asyncio
    import redis
    import json
    import time
    import hashlib
    from typing import Dict, Any, Optional, Union
    from functools import wraps
    import jwt
    
    class JWTCacheManager:
        def __init__(self, redis_url: str, default_ttl: int = 3600):
            self.redis_client = redis.from_url(redis_url)
            self.default_ttl = default_ttl
            self.local_cache = {}
            self.cache_stats = {
                "hits": 0,
                "misses": 0,
                "local_hits": 0,
                "redis_hits": 0
            }
    
        def _generate_cache_key(self, token: str) -> str:
            """Generate cache key for token"""
            # Use hash to avoid storing sensitive data in cache keys
            return f"jwt:decoded:{hashlib.sha256(token.encode()).hexdigest()[:16]}"
    
        async def get_decoded_token(self, token: str) -> Optional[Dict[str, Any]]:
            """Get decoded token from cache (multi-level)"""
    
            cache_key = self._generate_cache_key(token)
    
            # Level 1: Local memory cache
            if cache_key in self.local_cache:
                entry = self.local_cache[cache_key]
                if entry['expires'] > time.time():
                    self.cache_stats["hits"] += 1
                    self.cache_stats["local_hits"] += 1
                    return entry['data']
                else:
                    # Expired, remove from local cache
                    del self.local_cache[cache_key]
    
            # Level 2: Redis cache
            try:
                cached_data = self.redis_client.get(cache_key)
                if cached_data:
                    decoded_data = json.loads(cached_data)
    
                    # Store in local cache for faster access
                    self.local_cache[cache_key] = {
                        'data': decoded_data,
                        'expires': time.time() + 300  # 5 minutes in local cache
                    }
    
                    self.cache_stats["hits"] += 1
                    self.cache_stats["redis_hits"] += 1
                    return decoded_data
            except Exception as e:
                print(f"Redis cache error: {e}")
    
            self.cache_stats["misses"] += 1
            return None
    
        async def set_decoded_token(self, token: str, decoded_data: Dict[str, Any], ttl: Optional[int] = None):
            """Store decoded token in cache"""
    
            cache_key = self._generate_cache_key(token)
            ttl = ttl or self.default_ttl
    
            # Store in Redis
            try:
                self.redis_client.setex(
                    cache_key,
                    ttl,
                    json.dumps(decoded_data)
                )
            except Exception as e:
                print(f"Redis cache set error: {e}")
    
            # Store in local cache
            self.local_cache[cache_key] = {
                'data': decoded_data,
                'expires': time.time() + min(ttl, 300)  # Max 5 minutes local
            }
    
        async def invalidate_token(self, token: str):
            """Invalidate cached token"""
    
            cache_key = self._generate_cache_key(token)
    
            # Remove from local cache
            if cache_key in self.local_cache:
                del self.local_cache[cache_key]
    
            # Remove from Redis
            try:
                self.redis_client.delete(cache_key)
            except Exception as e:
                print(f"Redis cache delete error: {e}")
    
        def get_cache_stats(self) -> Dict[str, Any]:
            """Get cache performance statistics"""
    
            total_requests = self.cache_stats["hits"] + self.cache_stats["misses"]
            hit_rate = (self.cache_stats["hits"] / total_requests * 100) if total_requests > 0 else 0
    
            return {
                "total_requests": total_requests,
                "cache_hits": self.cache_stats["hits"],
                "cache_misses": self.cache_stats["misses"],
                "hit_rate_percent": round(hit_rate, 2),
                "local_cache_hits": self.cache_stats["local_hits"],
                "redis_cache_hits": self.cache_stats["redis_hits"],
                "local_cache_size": len(self.local_cache)
            }
    
        def cleanup_local_cache(self):
            """Clean up expired entries from local cache"""
    
            current_time = time.time()
            expired_keys = [
                key for key, entry in self.local_cache.items()
                if entry['expires'] <= current_time
            ]
    
            for key in expired_keys:
                del self.local_cache[key]
    
            return len(expired_keys)
    
    class CachedJWTHandler:
        def __init__(self, secret_key: str, algorithm: str = "HS256", cache_manager: JWTCacheManager = None):
            self.secret_key = secret_key
            self.algorithm = algorithm
            self.cache_manager = cache_manager
    
        async def decode_token(self, token: str, verify: bool = True) -> Dict[str, Any]:
            """Decode JWT token with caching"""
    
            if not self.cache_manager:
                # No caching, decode directly
                return jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
    
            # Try cache first
            cached_result = await self.cache_manager.get_decoded_token(token)
            if cached_result:
                return cached_result
    
            # Cache miss, decode token
            try:
                decoded_data = jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
    
                # Calculate TTL based on token expiry
                exp = decoded_data.get('exp')
                if exp:
                    ttl = max(int(exp - time.time()), 60)  # Minimum 1 minute
                else:
                    ttl = 3600  # Default 1 hour
    
                # Store in cache
                await self.cache_manager.set_decoded_token(token, decoded_data, ttl)
    
                return decoded_data
    
            except jwt.ExpiredSignatureError:
                # Don't cache expired tokens
                raise
            except jwt.JWTError:
                # Don't cache invalid tokens
                raise
    
        async def invalidate_user_tokens(self, user_id: str):
            """Invalidate all cached tokens for a user (simplified approach)"""
            # In a real implementation, you'd need to track user tokens
            # This is a placeholder for the concept
            pass
    
    # Performance monitoring decorator
    def monitor_jwt_performance(func):
        """Decorator to monitor JWT operation performance"""
    
        @wraps(func)
        async def wrapper(*args, **kwargs):
            start_time = time.perf_counter()
    
            try:
                result = await func(*args, **kwargs)
                duration = time.perf_counter() - start_time
    
                # Log successful operation
                print(f"JWT operation {func.__name__} completed in {duration*1000:.2f}ms")
    
                return result
    
            except Exception as e:
                duration = time.perf_counter() - start_time
                print(f"JWT operation {func.__name__} failed in {duration*1000:.2f}ms: {e}")
                raise
    
        return wrapper
    
    # Usage example
    async def example_cached_jwt_usage():
        """Example of using cached JWT handler"""
    
        # Initialize cache manager
        cache_manager = JWTCacheManager("redis://localhost:6379", default_ttl=3600)
    
        # Initialize JWT handler with caching
        jwt_handler = CachedJWTHandler("your-secret-key", cache_manager=cache_manager)
    
        # Sample token
        sample_payload = {
            "sub": "1234567890",
            "name": "John Doe",
            "iat": int(time.time()),
            "exp": int(time.time()) + 3600
        }
    
        token = jwt.encode(sample_payload, "your-secret-key", algorithm="HS256")
    
        # First decode (cache miss)
        print("First decode (cache miss):")
        start_time = time.perf_counter()
        decoded1 = await jwt_handler.decode_token(token)
        print(f"Time: {(time.perf_counter() - start_time)*1000:.2f}ms")
    
        # Second decode (cache hit)
        print("Second decode (cache hit):")
        start_time = time.perf_counter()
        decoded2 = await jwt_handler.decode_token(token)
        print(f"Time: {(time.perf_counter() - start_time)*1000:.2f}ms")
    
        # Display cache stats
        stats = cache_manager.get_cache_stats()
        print(f"Cache stats: {stats}")
    
    if __name__ == "__main__":
        asyncio.run(example_cached_jwt_usage())
    Python

    Database Query Optimization

    Optimized User Repository

    Create performance/optimized_repository.py:

    from sqlalchemy.orm import Session, joinedload, selectinload
    from sqlalchemy import text, and_, or_
    from typing import List, Optional, Dict, Any
    import redis
    import json
    import time
    from functools import lru_cache
    
    from app.models.user import User, Role, Permission
    from app.database import get_db
    
    class OptimizedUserRepository:
        def __init__(self, db: Session, redis_client: redis.Redis = None):
            self.db = db
            self.redis_client = redis_client
            self.cache_ttl = 300  # 5 minutes
    
        async def get_user_with_permissions(self, user_id: int) -> Optional[Dict[str, Any]]:
            """Get user with permissions optimized for JWT token generation"""
    
            # Try cache first
            if self.redis_client:
                cache_key = f"user:permissions:{user_id}"
                cached_data = self.redis_client.get(cache_key)
                if cached_data:
                    return json.loads(cached_data)
    
            # Optimized query with eager loading
            user = (
                self.db.query(User)
                .options(
                    joinedload(User.roles).joinedload(Role.permissions),
                    selectinload(User.permissions)
                )
                .filter(User.id == user_id)
                .first()
            )
    
            if not user:
                return None
    
            # Build permissions set efficiently
            permissions = set()
    
            # Direct user permissions
            for perm in user.permissions:
                permissions.add(perm.name)
    
            # Role-based permissions
            for role in user.roles:
                for perm in role.permissions:
                    permissions.add(perm.name)
    
            # Build result
            result = {
                "id": user.id,
                "email": user.email,
                "username": user.username,
                "full_name": user.full_name,
                "is_active": user.is_active,
                "roles": [role.name for role in user.roles],
                "permissions": list(permissions),
                "last_login": user.last_login.isoformat() if user.last_login else None
            }
    
            # Cache result
            if self.redis_client:
                self.redis_client.setex(
                    cache_key,
                    self.cache_ttl,
                    json.dumps(result, default=str)
                )
    
            return result
    
        async def batch_get_users(self, user_ids: List[int]) -> Dict[int, Dict[str, Any]]:
            """Efficiently get multiple users"""
    
            # Check cache for each user
            cached_users = {}
            uncached_ids = []
    
            if self.redis_client:
                for user_id in user_ids:
                    cache_key = f"user:basic:{user_id}"
                    cached_data = self.redis_client.get(cache_key)
                    if cached_data:
                        cached_users[user_id] = json.loads(cached_data)
                    else:
                        uncached_ids.append(user_id)
            else:
                uncached_ids = user_ids
    
            # Query uncached users in batch
            if uncached_ids:
                users = (
                    self.db.query(User)
                    .filter(User.id.in_(uncached_ids))
                    .all()
                )
    
                for user in users:
                    user_data = {
                        "id": user.id,
                        "email": user.email,
                        "username": user.username,
                        "full_name": user.full_name,
                        "is_active": user.is_active
                    }
    
                    cached_users[user.id] = user_data
    
                    # Cache individual user
                    if self.redis_client:
                        cache_key = f"user:basic:{user.id}"
                        self.redis_client.setex(
                            cache_key,
                            self.cache_ttl,
                            json.dumps(user_data)
                        )
    
            return cached_users
    
        async def get_user_roles_compressed(self, user_id: int) -> Optional[str]:
            """Get user roles in compressed format for JWT"""
    
            cache_key = f"user:roles:compressed:{user_id}"
    
            if self.redis_client:
                cached_data = self.redis_client.get(cache_key)
                if cached_data:
                    return cached_data.decode('utf-8')
    
            # Query roles efficiently
            roles = (
                self.db.query(Role.name)
                .join(User.roles)
                .filter(User.id == user_id)
                .all()
            )
    
            if not roles:
                return None
    
            # Create compressed role representation
            role_codes = {
                'admin': 'a',
                'manager': 'm',
                'user': 'u',
                'moderator': 'mod',
                'support': 's'
            }
    
            compressed_roles = ','.join([
                role_codes.get(role.name, role.name[:3])
                for role in roles
            ])
    
            # Cache result
            if self.redis_client:
                self.redis_client.setex(cache_key, self.cache_ttl, compressed_roles)
    
            return compressed_roles
    
        def invalidate_user_cache(self, user_id: int):
            """Invalidate all cache entries for a user"""
    
            if not self.redis_client:
                return
    
            cache_patterns = [
                f"user:permissions:{user_id}",
                f"user:basic:{user_id}",
                f"user:roles:compressed:{user_id}"
            ]
    
            for pattern in cache_patterns:
                self.redis_client.delete(pattern)
    
        @lru_cache(maxsize=1000)
        def get_permission_mapping(self) -> Dict[str, str]:
            """Get cached permission name to code mapping"""
    
            permissions = self.db.query(Permission).all()
    
            mapping = {}
            for perm in permissions:
                # Create short codes for common permissions
                if perm.name.startswith('read_'):
                    code = 'r' + perm.name[5:7]
                elif perm.name.startswith('write_'):
                    code = 'w' + perm.name[6:8]
                elif perm.name.startswith('delete_'):
                    code = 'd' + perm.name[7:9]
                else:
                    code = perm.name[:4]
    
                mapping[perm.name] = code
    
            return mapping
    
    # Database connection pooling optimization
    class OptimizedDatabase:
        def __init__(self, database_url: str):
            from sqlalchemy import create_engine
            from sqlalchemy.pool import QueuePool
    
            self.engine = create_engine(
                database_url,
                poolclass=QueuePool,
                pool_size=20,
                max_overflow=30,
                pool_pre_ping=True,
                pool_recycle=3600,
                echo=False  # Set to True for debugging
            )
    
        def get_connection_stats(self) -> Dict[str, Any]:
            """Get database connection pool statistics"""
    
            pool = self.engine.pool
    
            return {
                "pool_size": pool.size(),
                "checked_in": pool.checkedin(),
                "checked_out": pool.checkedout(),
                "overflow": pool.overflow(),
                "invalid": pool.invalid()
            }
    
    # Query optimization examples
    class OptimizedQueries:
    
        @staticmethod
        def get_user_login_optimized(db: Session, email: str) -> Optional[User]:
            """Optimized user login query"""
    
            # Use index hint and specific columns
            return (
                db.query(User)
                .filter(
                    and_(
                        User.email == email,
                        User.is_active == True
                    )
                )
                .options(
                    joinedload(User.roles).load_only(Role.name),
                    selectinload(User.permissions).load_only(Permission.name)
                )
                .first()
            )
    
        @staticmethod
        def bulk_update_last_login(db: Session, user_ids: List[int], timestamp: int):
            """Bulk update last login timestamps"""
    
            # Use bulk update for better performance
            db.execute(
                text(
                    "UPDATE users SET last_login = :timestamp WHERE id = ANY(:user_ids)"
                ),
                {"timestamp": timestamp, "user_ids": user_ids}
            )
            db.commit()
    
        @staticmethod
        def get_active_users_count(db: Session) -> int:
            """Get count of active users efficiently"""
    
            # Use count query instead of loading all records
            return (
                db.query(User.id)
                .filter(User.is_active == True)
                .count()
            )
    
    # Performance monitoring
    class DatabasePerformanceMonitor:
        def __init__(self):
            self.query_stats = {}
    
        def monitor_query(self, query_name: str):
            """Decorator to monitor query performance"""
    
            def decorator(func):
                @wraps(func)
                def wrapper(*args, **kwargs):
                    start_time = time.perf_counter()
    
                    try:
                        result = func(*args, **kwargs)
                        duration = time.perf_counter() - start_time
    
                        # Track performance
                        if query_name not in self.query_stats:
                            self.query_stats[query_name] = {
                                "count": 0,
                                "total_time": 0,
                                "avg_time": 0,
                                "max_time": 0
                            }
    
                        stats = self.query_stats[query_name]
                        stats["count"] += 1
                        stats["total_time"] += duration
                        stats["avg_time"] = stats["total_time"] / stats["count"]
                        stats["max_time"] = max(stats["max_time"], duration)
    
                        return result
    
                    except Exception as e:
                        print(f"Query {query_name} failed: {e}")
                        raise
    
                return wrapper
            return decorator
    
        def get_performance_report(self) -> Dict[str, Any]:
            """Get performance report"""
    
            return {
                "queries": self.query_stats,
                "total_queries": sum(stats["count"] for stats in self.query_stats.values()),
                "slowest_queries": sorted(
                    self.query_stats.items(),
                    key=lambda x: x[1]["max_time"],
                    reverse=True
                )[:5]
            }
    Python

    Frontend Performance Optimization

    Optimized Token Management

    Create frontend/performance/optimized-auth.js:

    class OptimizedAuthManager {
        constructor(config = {}) {
            this.baseURL = config.baseURL || '/api';
            this.tokenKey = config.tokenKey || 'access_token';
            this.refreshKey = config.refreshKey || 'refresh_token';
    
            // Performance optimization settings
            this.requestQueue = [];
            this.isRefreshing = false;
            this.refreshPromise = null;
    
            // Token validation cache
            this.validationCache = new Map();
            this.cacheTimeout = 30000; // 30 seconds
    
            // Request deduplication
            this.pendingRequests = new Map();
    
            // Performance metrics
            this.metrics = {
                requestCount: 0,
                cacheHits: 0,
                tokenRefreshCount: 0,
                averageResponseTime: 0,
                totalResponseTime: 0
            };
    
            this.initializePerformanceMonitoring();
        }
    
        initializePerformanceMonitoring() {
            // Monitor page visibility for token management
            document.addEventListener('visibilitychange', () => {
                if (document.visibilityState === 'visible') {
                    this.validateTokenOnFocus();
                }
            });
    
            // Preload critical resources
            this.preloadAuthResources();
        }
    
        async preloadAuthResources() {
            // Preload authentication endpoints for faster response
            const criticalEndpoints = ['/auth/validate', '/auth/refresh'];
    
            for (const endpoint of criticalEndpoints) {
                try {
                    // DNS prefetch and preconnect
                    const link = document.createElement('link');
                    link.rel = 'preconnect';
                    link.href = this.baseURL;
                    document.head.appendChild(link);
                } catch (error) {
                    console.warn('Preload failed:', error);
                }
            }
        }
    
        // Optimized token validation with caching
        async validateToken(token = null) {
            const tokenToValidate = token || this.getToken();
    
            if (!tokenToValidate) {
                return { valid: false, reason: 'no_token' };
            }
    
            // Check validation cache first
            const cacheKey = this.hashToken(tokenToValidate);
            const cached = this.validationCache.get(cacheKey);
    
            if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
                this.metrics.cacheHits++;
                return cached.result;
            }
    
            // Validate token
            try {
                const payload = this.parseJWTPayload(tokenToValidate);
                const now = Math.floor(Date.now() / 1000);
    
                const result = {
                    valid: payload.exp > now,
                    expiresAt: payload.exp,
                    needsRefresh: payload.exp - now < 300, // 5 minutes
                    payload: payload
                };
    
                // Cache result
                this.validationCache.set(cacheKey, {
                    result: result,
                    timestamp: Date.now()
                });
    
                return result;
    
            } catch (error) {
                const result = { valid: false, reason: 'invalid_token', error: error.message };
    
                // Cache negative result for shorter time
                this.validationCache.set(cacheKey, {
                    result: result,
                    timestamp: Date.now() - (this.cacheTimeout / 2)
                });
    
                return result;
            }
        }
    
        // Optimized API request with deduplication
        async makeAuthenticatedRequest(url, options = {}) {
            const requestKey = `${options.method || 'GET'}:${url}:${JSON.stringify(options.body || {})}`;
    
            // Check for duplicate requests
            if (this.pendingRequests.has(requestKey)) {
                return this.pendingRequests.get(requestKey);
            }
    
            const requestPromise = this._executeRequest(url, options);
            this.pendingRequests.set(requestKey, requestPromise);
    
            try {
                const result = await requestPromise;
                return result;
            } finally {
                // Clean up pending request
                setTimeout(() => {
                    this.pendingRequests.delete(requestKey);
                }, 1000); // Allow 1 second for potential duplicates
            }
        }
    
        async _executeRequest(url, options = {}) {
            const startTime = performance.now();
            this.metrics.requestCount++;
    
            let token = this.getToken();
    
            // Validate token before request
            const validation = await this.validateToken(token);
    
            if (!validation.valid) {
                // Try to refresh token
                const refreshed = await this.refreshTokenIfNeeded();
                if (!refreshed) {
                    throw new Error('Authentication required');
                }
                token = this.getToken();
            } else if (validation.needsRefresh) {
                // Refresh in background
                this.refreshTokenInBackground();
            }
    
            // Make request
            const requestOptions = {
                ...options,
                headers: {
                    'Authorization': `Bearer ${token}`,
                    'Content-Type': 'application/json',
                    ...options.headers
                }
            };
    
            try {
                const response = await fetch(`${this.baseURL}${url}`, requestOptions);
    
                // Update metrics
                const responseTime = performance.now() - startTime;
                this.updateMetrics(responseTime);
    
                if (response.status === 401) {
                    // Token might be invalid, try refresh
                    const refreshed = await this.refreshTokenIfNeeded();
                    if (refreshed) {
                        // Retry request with new token
                        requestOptions.headers['Authorization'] = `Bearer ${this.getToken()}`;
                        return fetch(`${this.baseURL}${url}`, requestOptions);
                    }
                }
    
                return response;
    
            } catch (error) {
                console.error('Request failed:', error);
                throw error;
            }
        }
    
        async refreshTokenIfNeeded() {
            if (this.isRefreshing) {
                // Wait for ongoing refresh
                return this.refreshPromise;
            }
    
            this.isRefreshing = true;
            this.refreshPromise = this._performTokenRefresh();
    
            try {
                const result = await this.refreshPromise;
                return result;
            } finally {
                this.isRefreshing = false;
                this.refreshPromise = null;
            }
        }
    
        async _performTokenRefresh() {
            const refreshToken = this.getRefreshToken();
    
            if (!refreshToken) {
                this.clearTokens();
                return false;
            }
    
            try {
                const response = await fetch(`${this.baseURL}/auth/refresh`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({
                        refresh_token: refreshToken
                    })
                });
    
                if (response.ok) {
                    const data = await response.json();
                    this.setToken(data.access_token);
    
                    if (data.refresh_token) {
                        this.setRefreshToken(data.refresh_token);
                    }
    
                    this.metrics.tokenRefreshCount++;
    
                    // Clear validation cache
                    this.validationCache.clear();
    
                    return true;
                } else {
                    this.clearTokens();
                    return false;
                }
    
            } catch (error) {
                console.error('Token refresh failed:', error);
                this.clearTokens();
                return false;
            }
        }
    
        refreshTokenInBackground() {
            // Non-blocking token refresh
            setTimeout(() => {
                this.refreshTokenIfNeeded();
            }, 0);
        }
    
        validateTokenOnFocus() {
            // Validate token when page regains focus
            setTimeout(async () => {
                const validation = await this.validateToken();
                if (!validation.valid) {
                    await this.refreshTokenIfNeeded();
                }
            }, 0);
        }
    
        // Utility methods
        parseJWTPayload(token) {
            const parts = token.split('.');
            if (parts.length !== 3) {
                throw new Error('Invalid JWT format');
            }
    
            const payload = parts[1];
            const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));
            return JSON.parse(decoded);
        }
    
        hashToken(token) {
            // Simple hash for caching (in production, use proper hashing)
            let hash = 0;
            for (let i = 0; i < token.length; i++) {
                const char = token.charCodeAt(i);
                hash = ((hash << 5) - hash) + char;
                hash = hash & hash; // Convert to 32-bit integer
            }
            return hash.toString();
        }
    
        updateMetrics(responseTime) {
            this.metrics.totalResponseTime += responseTime;
            this.metrics.averageResponseTime = this.metrics.totalResponseTime / this.metrics.requestCount;
        }
    
        getPerformanceMetrics() {
            return {
                ...this.metrics,
                cacheHitRate: (this.metrics.cacheHits / this.metrics.requestCount * 100).toFixed(2) + '%',
                validationCacheSize: this.validationCache.size,
                pendingRequestsCount: this.pendingRequests.size
            };
        }
    
        // Token storage methods
        getToken() {
            return localStorage.getItem(this.tokenKey);
        }
    
        setToken(token) {
            localStorage.setItem(this.tokenKey, token);
        }
    
        getRefreshToken() {
            return localStorage.getItem(this.refreshKey);
        }
    
        setRefreshToken(token) {
            localStorage.setItem(this.refreshKey, token);
        }
    
        clearTokens() {
            localStorage.removeItem(this.tokenKey);
            localStorage.removeItem(this.refreshKey);
            this.validationCache.clear();
        }
    
        // Cleanup method
        cleanup() {
            this.validationCache.clear();
            this.pendingRequests.clear();
        }
    }
    
    // Performance monitoring utilities
    class AuthPerformanceMonitor {
        constructor() {
            this.metrics = {
                authOperations: [],
                networkLatency: [],
                cachePerformance: []
            };
        }
    
        recordAuthOperation(operation, duration, success) {
            this.metrics.authOperations.push({
                operation,
                duration,
                success,
                timestamp: Date.now()
            });
    
            // Keep only last 100 operations
            if (this.metrics.authOperations.length > 100) {
                this.metrics.authOperations.shift();
            }
        }
    
        recordNetworkLatency(endpoint, latency) {
            this.metrics.networkLatency.push({
                endpoint,
                latency,
                timestamp: Date.now()
            });
    
            // Keep only last 50 measurements
            if (this.metrics.networkLatency.length > 50) {
                this.metrics.networkLatency.shift();
            }
        }
    
        getPerformanceReport() {
            const authOps = this.metrics.authOperations;
            const networkOps = this.metrics.networkLatency;
    
            return {
                authOperations: {
                    total: authOps.length,
                    successful: authOps.filter(op => op.success).length,
                    averageDuration: authOps.reduce((sum, op) => sum + op.duration, 0) / authOps.length,
                    successRate: (authOps.filter(op => op.success).length / authOps.length * 100).toFixed(2) + '%'
                },
                networkPerformance: {
                    totalRequests: networkOps.length,
                    averageLatency: networkOps.reduce((sum, op) => sum + op.latency, 0) / networkOps.length,
                    maxLatency: Math.max(...networkOps.map(op => op.latency)),
                    minLatency: Math.min(...networkOps.map(op => op.latency))
                }
            };
        }
    }
    
    // Export for use
    export { OptimizedAuthManager, AuthPerformanceMonitor };
    JavaScript

    This performance optimization chapter provides comprehensive strategies for optimizing JWT systems including token size reduction, multi-level caching, database optimization, and frontend performance enhancements.


    22. Migration Strategies

    This chapter covers practical strategies for migrating from existing authentication systems to JWT-based authentication, including session-based systems, legacy integrations, and smooth transition approaches.

    Migration Planning Framework

    Migrating from Session-Based Authentication

    Dual Authentication System

    Create migration/dual_auth_system.py:

    from fastapi import FastAPI, Depends, HTTPException, status, Request, Response
    from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
    import jwt
    from typing import Dict, Any, Optional, Union
    import time
    import redis
    from datetime import datetime, timedelta
    
    from app.models.user import User
    from app.database import get_db
    from app.config import settings
    
    class DualAuthenticationManager:
        """Manages both session-based and JWT authentication during migration"""
    
        def __init__(self, redis_client: redis.Redis, jwt_secret: str):
            self.redis_client = redis_client
            self.jwt_secret = jwt_secret
            self.session_timeout = 3600  # 1 hour
            self.migration_mode = True  # Enable dual authentication
    
        async def authenticate_request(self, request: Request) -> Dict[str, Any]:
            """Authenticate request using either session or JWT"""
    
            # Try JWT authentication first (preferred)
            jwt_user = await self._authenticate_jwt(request)
            if jwt_user:
                return {
                    "user": jwt_user,
                    "auth_type": "jwt",
                    "should_migrate": False
                }
    
            # Fall back to session authentication
            session_user = await self._authenticate_session(request)
            if session_user:
                return {
                    "user": session_user,
                    "auth_type": "session",
                    "should_migrate": True  # Mark for JWT migration
                }
    
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Authentication required"
            )
    
        async def _authenticate_jwt(self, request: Request) -> Optional[Dict[str, Any]]:
            """Authenticate using JWT token"""
    
            # Extract JWT from Authorization header
            auth_header = request.headers.get("Authorization")
            if not auth_header or not auth_header.startswith("Bearer "):
                return None
    
            token = auth_header.split(" ")[1]
    
            try:
                payload = jwt.decode(
                    token,
                    self.jwt_secret,
                    algorithms=["HS256"]
                )
    
                return {
                    "user_id": payload["sub"],
                    "email": payload.get("email"),
                    "roles": payload.get("roles", []),
                    "permissions": payload.get("permissions", [])
                }
    
            except jwt.JWTError:
                return None
    
        async def _authenticate_session(self, request: Request) -> Optional[Dict[str, Any]]:
            """Authenticate using legacy session"""
    
            # Extract session ID from cookie
            session_id = request.cookies.get("session_id")
            if not session_id:
                return None
    
            # Check session in Redis/database
            session_key = f"session:{session_id}"
            session_data = self.redis_client.get(session_key)
    
            if not session_data:
                return None
    
            try:
                import json
                session_info = json.loads(session_data)
    
                # Validate session expiry
                if session_info.get("expires_at", 0) < time.time():
                    self.redis_client.delete(session_key)
                    return None
    
                return {
                    "user_id": session_info["user_id"],
                    "email": session_info.get("email"),
                    "roles": session_info.get("roles", []),
                    "permissions": session_info.get("permissions", [])
                }
    
            except (json.JSONDecodeError, KeyError):
                return None
    
        async def migrate_session_to_jwt(self, user_data: Dict[str, Any], response: Response) -> str:
            """Convert session authentication to JWT token"""
    
            # Create JWT payload
            payload = {
                "sub": str(user_data["user_id"]),
                "email": user_data["email"],
                "roles": user_data.get("roles", []),
                "permissions": user_data.get("permissions", []),
                "iat": int(time.time()),
                "exp": int(time.time()) + 3600,  # 1 hour
                "migration": True  # Mark as migrated token
            }
    
            # Generate JWT token
            token = jwt.encode(payload, self.jwt_secret, algorithm="HS256")
    
            # Set JWT token as httpOnly cookie for smooth transition
            response.set_cookie(
                key="jwt_token",
                value=token,
                httponly=True,
                secure=True,
                samesite="strict",
                max_age=3600
            )
    
            # Optionally remove old session
            session_id = user_data.get("session_id")
            if session_id:
                self.redis_client.delete(f"session:{session_id}")
    
            return token
    
        def create_migration_response(self, response: Response, token: str):
            """Add migration headers and cookies to response"""
    
            # Add migration indicator header
            response.headers["X-Auth-Migrated"] = "true"
            response.headers["X-Auth-Type"] = "jwt"
    
            # Set new JWT cookie
            response.set_cookie(
                key="access_token",
                value=token,
                httponly=True,
                secure=True,
                samesite="strict",
                max_age=3600
            )
    
    class SessionToJWTConverter:
        """Utility to convert existing sessions to JWT format"""
    
        def __init__(self, db_session, redis_client, jwt_secret):
            self.db = db_session
            self.redis_client = redis_client
            self.jwt_secret = jwt_secret
    
        async def convert_all_active_sessions(self) -> Dict[str, Any]:
            """Convert all active sessions to JWT tokens"""
    
            conversion_stats = {
                "total_sessions": 0,
                "converted": 0,
                "failed": 0,
                "expired": 0
            }
    
            # Get all active sessions from Redis
            session_keys = self.redis_client.keys("session:*")
            conversion_stats["total_sessions"] = len(session_keys)
    
            for session_key in session_keys:
                try:
                    session_data = self.redis_client.get(session_key)
                    if not session_data:
                        continue
    
                    import json
                    session_info = json.loads(session_data)
    
                    # Check if session is expired
                    if session_info.get("expires_at", 0) < time.time():
                        conversion_stats["expired"] += 1
                        self.redis_client.delete(session_key)
                        continue
    
                    # Get full user data
                    user = self.db.query(User).filter(
                        User.id == session_info["user_id"]
                    ).first()
    
                    if not user:
                        conversion_stats["failed"] += 1
                        continue
    
                    # Create JWT token
                    jwt_token = await self._create_jwt_for_user(user)
    
                    # Store JWT token mapping
                    jwt_key = f"jwt_migration:{user.id}"
                    self.redis_client.setex(
                        jwt_key,
                        3600,  # 1 hour
                        jwt_token
                    )
    
                    conversion_stats["converted"] += 1
    
                except Exception as e:
                    print(f"Failed to convert session {session_key}: {e}")
                    conversion_stats["failed"] += 1
    
            return conversion_stats
    
        async def _create_jwt_for_user(self, user: User) -> str:
            """Create JWT token for user"""
    
            # Get user roles and permissions
            roles = [role.name for role in user.roles]
            permissions = []
    
            for role in user.roles:
                permissions.extend([perm.name for perm in role.permissions])
    
            # Add direct user permissions
            permissions.extend([perm.name for perm in user.permissions])
    
            # Remove duplicates
            permissions = list(set(permissions))
    
            payload = {
                "sub": str(user.id),
                "email": user.email,
                "username": user.username,
                "full_name": user.full_name,
                "roles": roles,
                "permissions": permissions,
                "iat": int(time.time()),
                "exp": int(time.time()) + 3600,
                "migrated": True
            }
    
            return jwt.encode(payload, self.jwt_secret, algorithm="HS256")
    
    # FastAPI middleware for dual authentication
    class MigrationAuthMiddleware:
        def __init__(self, app: FastAPI, auth_manager: DualAuthenticationManager):
            self.app = app
            self.auth_manager = auth_manager
    
        async def __call__(self, request: Request, call_next):
            """Middleware to handle dual authentication"""
    
            # Skip authentication for certain paths
            skip_paths = ["/auth/login", "/auth/register", "/health", "/docs"]
            if any(request.url.path.startswith(path) for path in skip_paths):
                return await call_next(request)
    
            try:
                # Authenticate request
                auth_result = await self.auth_manager.authenticate_request(request)
    
                # Store authentication info in request state
                request.state.current_user = auth_result["user"]
                request.state.auth_type = auth_result["auth_type"]
                request.state.should_migrate = auth_result["should_migrate"]
    
                # Process request
                response = await call_next(request)
    
                # Handle migration if needed
                if auth_result["should_migrate"] and auth_result["auth_type"] == "session":
                    token = await self.auth_manager.migrate_session_to_jwt(
                        auth_result["user"],
                        response
                    )
                    self.auth_manager.create_migration_response(response, token)
    
                return response
    
            except HTTPException:
                # Authentication failed
                raise
            except Exception as e:
                print(f"Authentication middleware error: {e}")
                raise HTTPException(
                    status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    detail="Authentication error"
                )
    
    # FastAPI app with dual authentication
    def create_migration_app():
        """Create FastAPI app with migration support"""
    
        app = FastAPI(title="Migration API", version="1.0.0")
    
        # Initialize components
        redis_client = redis.from_url(settings.REDIS_URL)
        auth_manager = DualAuthenticationManager(redis_client, settings.JWT_SECRET_KEY)
    
        # Add migration middleware
        app.add_middleware(MigrationAuthMiddleware, auth_manager=auth_manager)
    
        @app.post("/auth/migrate")
        async def migrate_authentication(request: Request, response: Response):
            """Endpoint to explicitly migrate from session to JWT"""
    
            if not hasattr(request.state, 'current_user'):
                raise HTTPException(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    detail="Authentication required"
                )
    
            if request.state.auth_type == "jwt":
                return {"message": "Already using JWT authentication"}
    
            # Migrate to JWT
            token = await auth_manager.migrate_session_to_jwt(
                request.state.current_user,
                response
            )
    
            return {
                "message": "Successfully migrated to JWT authentication",
                "token_type": "Bearer",
                "access_token": token
            }
    
        @app.get("/auth/status")
        async def authentication_status(request: Request):
            """Get current authentication status"""
    
            if not hasattr(request.state, 'current_user'):
                return {"authenticated": False}
    
            return {
                "authenticated": True,
                "auth_type": request.state.auth_type,
                "user_id": request.state.current_user["user_id"],
                "should_migrate": request.state.should_migrate
            }
    
        return app
    Python

    Legacy Database Integration

    Database Migration Utilities

    Create migration/database_migration.py:

    from sqlalchemy import create_engine, text, Column, Integer, String, DateTime, Boolean
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.orm import sessionmaker
    from alembic import command
    from alembic.config import Config
    from typing import Dict, List, Any
    import hashlib
    import secrets
    from datetime import datetime
    
    Base = declarative_base()
    
    class LegacyUser(Base):
        """Legacy user table structure"""
        __tablename__ = "legacy_users"
    
        id = Column(Integer, primary_key=True)
        username = Column(String(255), unique=True)
        email = Column(String(255), unique=True)
        password_hash = Column(String(255))  # Different hashing algorithm
        salt = Column(String(255))
        full_name = Column(String(255))
        created_at = Column(DateTime)
        last_login = Column(DateTime)
        is_active = Column(Boolean, default=True)
        user_role = Column(String(50))  # Single role field
    
    class DatabaseMigrationManager:
        def __init__(self, legacy_db_url: str, new_db_url: str):
            self.legacy_engine = create_engine(legacy_db_url)
            self.new_engine = create_engine(new_db_url)
    
            # Create sessions
            LegacySession = sessionmaker(bind=self.legacy_engine)
            NewSession = sessionmaker(bind=self.new_engine)
    
            self.legacy_session = LegacySession()
            self.new_session = NewSession()
    
            self.migration_stats = {
                "users_migrated": 0,
                "users_failed": 0,
                "roles_created": 0,
                "permissions_mapped": 0
            }
    
        async def migrate_users(self, batch_size: int = 1000) -> Dict[str, Any]:
            """Migrate users from legacy database"""
    
            # Get total count
            total_users = self.legacy_session.query(LegacyUser).count()
            print(f"Migrating {total_users} users...")
    
            # Process in batches
            offset = 0
            while offset < total_users:
                batch = (
                    self.legacy_session.query(LegacyUser)
                    .offset(offset)
                    .limit(batch_size)
                    .all()
                )
    
                await self._migrate_user_batch(batch)
                offset += batch_size
    
                print(f"Processed {min(offset, total_users)}/{total_users} users")
    
            return self.migration_stats
    
        async def _migrate_user_batch(self, users: List[LegacyUser]):
            """Migrate a batch of users"""
    
            from app.models.user import User, Role
            from app.auth.password_handler import get_password_hash
    
            for legacy_user in users:
                try:
                    # Check if user already exists
                    existing_user = (
                        self.new_session.query(User)
                        .filter(User.email == legacy_user.email)
                        .first()
                    )
    
                    if existing_user:
                        print(f"User {legacy_user.email} already exists, skipping")
                        continue
    
                    # Convert legacy password hash to new format
                    new_password_hash = await self._convert_password_hash(
                        legacy_user.password_hash,
                        legacy_user.salt
                    )
    
                    # Create new user
                    new_user = User(
                        username=legacy_user.username,
                        email=legacy_user.email,
                        hashed_password=new_password_hash,
                        full_name=legacy_user.full_name,
                        is_active=legacy_user.is_active,
                        created_at=legacy_user.created_at or datetime.utcnow(),
                        last_login=legacy_user.last_login
                    )
    
                    # Migrate user role
                    if legacy_user.user_role:
                        role = await self._get_or_create_role(legacy_user.user_role)
                        new_user.roles.append(role)
    
                    self.new_session.add(new_user)
                    self.new_session.commit()
    
                    self.migration_stats["users_migrated"] += 1
    
                except Exception as e:
                    print(f"Failed to migrate user {legacy_user.email}: {e}")
                    self.new_session.rollback()
                    self.migration_stats["users_failed"] += 1
    
        async def _convert_password_hash(self, legacy_hash: str, salt: str) -> str:
            """Convert legacy password hash to new format"""
    
            # Option 1: Mark for password reset (recommended)
            # Generate a temporary hash that will force password reset
            return get_password_hash(secrets.token_urlsafe(32))
    
            # Option 2: Convert hash if possible (depends on legacy algorithm)
            # This example assumes MD5 -> bcrypt conversion
            # In practice, you might need to invalidate passwords and force reset
    
        async def _get_or_create_role(self, role_name: str) -> 'Role':
            """Get existing role or create new one"""
    
            from app.models.user import Role
    
            role = self.new_session.query(Role).filter(Role.name == role_name).first()
    
            if not role:
                role = Role(
                    name=role_name,
                    description=f"Migrated role: {role_name}"
                )
                self.new_session.add(role)
                self.new_session.commit()
    
                self.migration_stats["roles_created"] += 1
    
            return role
    
        async def migrate_custom_tables(self):
            """Migrate custom authentication-related tables"""
    
            # Example: Migrate user groups
            await self._migrate_user_groups()
    
            # Example: Migrate user preferences
            await self._migrate_user_preferences()
    
            # Example: Migrate audit logs
            await self._migrate_audit_logs()
    
        async def _migrate_user_groups(self):
            """Migrate user groups to roles/permissions"""
    
            # Query legacy groups
            legacy_groups = self.legacy_session.execute(
                text("SELECT * FROM user_groups")
            ).fetchall()
    
            from app.models.user import Role, Permission
    
            for group in legacy_groups:
                # Create role for group
                role = Role(
                    name=group.group_name,
                    description=group.description or f"Migrated from group: {group.group_name}"
                )
    
                self.new_session.add(role)
                self.new_session.commit()
    
                # Migrate group permissions
                group_permissions = self.legacy_session.execute(
                    text("SELECT permission_name FROM group_permissions WHERE group_id = :group_id"),
                    {"group_id": group.id}
                ).fetchall()
    
                for perm in group_permissions:
                    permission = (
                        self.new_session.query(Permission)
                        .filter(Permission.name == perm.permission_name)
                        .first()
                    )
    
                    if not permission:
                        permission = Permission(
                            name=perm.permission_name,
                            description=f"Migrated permission: {perm.permission_name}"
                        )
                        self.new_session.add(permission)
                        self.new_session.commit()
    
                    role.permissions.append(permission)
    
                self.new_session.commit()
    
        async def _migrate_user_preferences(self):
            """Migrate user preferences/settings"""
    
            # This would be custom based on your legacy system
            preferences = self.legacy_session.execute(
                text("SELECT user_id, preference_key, preference_value FROM user_preferences")
            ).fetchall()
    
            # Store in new format (could be JSON column or separate table)
            user_prefs = {}
            for pref in preferences:
                if pref.user_id not in user_prefs:
                    user_prefs[pref.user_id] = {}
                user_prefs[pref.user_id][pref.preference_key] = pref.preference_value
    
            # Update users with preferences
            from app.models.user import User
            import json
    
            for user_id, prefs in user_prefs.items():
                user = self.new_session.query(User).filter(User.id == user_id).first()
                if user:
                    # Assuming you have a preferences JSON column
                    user.preferences = json.dumps(prefs)
                    self.new_session.commit()
    
        async def _migrate_audit_logs(self):
            """Migrate authentication audit logs"""
    
            # Migrate login history
            login_logs = self.legacy_session.execute(
                text("""
                    SELECT user_id, login_time, ip_address, user_agent, success 
                    FROM login_audit 
                    WHERE login_time > NOW() - INTERVAL 90 DAY
                """)
            ).fetchall()
    
            # Insert into new audit system
            for log in login_logs:
                self.new_session.execute(
                    text("""
                        INSERT INTO auth_audit_logs (user_id, event_type, ip_address, user_agent, success, created_at)
                        VALUES (:user_id, 'login', :ip_address, :user_agent, :success, :created_at)
                    """),
                    {
                        "user_id": log.user_id,
                        "ip_address": log.ip_address,
                        "user_agent": log.user_agent,
                        "success": log.success,
                        "created_at": log.login_time
                    }
                )
    
            self.new_session.commit()
    
        def create_migration_report(self) -> Dict[str, Any]:
            """Generate migration report"""
    
            return {
                "migration_stats": self.migration_stats,
                "timestamp": datetime.utcnow().isoformat(),
                "legacy_db_info": {
                    "total_users": self.legacy_session.query(LegacyUser).count(),
                    "active_users": self.legacy_session.query(LegacyUser).filter(LegacyUser.is_active == True).count()
                },
                "new_db_info": {
                    "migrated_users": self.migration_stats["users_migrated"],
                    "failed_migrations": self.migration_stats["users_failed"]
                }
            }
    
        def cleanup(self):
            """Clean up database connections"""
            self.legacy_session.close()
            self.new_session.close()
    
    # Migration CLI tool
    class MigrationCLI:
        def __init__(self, config_file: str):
            self.config = self._load_config(config_file)
            self.migration_manager = DatabaseMigrationManager(
                self.config["legacy_db_url"],
                self.config["new_db_url"]
            )
    
        def _load_config(self, config_file: str) -> Dict[str, Any]:
            """Load migration configuration"""
            import json
            with open(config_file, 'r') as f:
                return json.load(f)
    
        async def run_migration(self):
            """Run complete migration process"""
    
            print("Starting database migration...")
    
            try:
                # Step 1: Migrate users
                print("Migrating users...")
                await self.migration_manager.migrate_users()
    
                # Step 2: Migrate custom tables
                print("Migrating custom tables...")
                await self.migration_manager.migrate_custom_tables()
    
                # Step 3: Generate report
                print("Generating migration report...")
                report = self.migration_manager.create_migration_report()
    
                # Save report
                with open("migration_report.json", "w") as f:
                    import json
                    json.dump(report, f, indent=2)
    
                print("Migration completed successfully!")
                print(f"Users migrated: {report['migration_stats']['users_migrated']}")
                print(f"Failed migrations: {report['migration_stats']['users_failed']}")
    
            except Exception as e:
                print(f"Migration failed: {e}")
                raise
            finally:
                self.migration_manager.cleanup()
    
    # Usage example
    async def main():
        """Example migration execution"""
    
        migration_cli = MigrationCLI("migration_config.json")
        await migration_cli.run_migration()
    
    if __name__ == "__main__":
        import asyncio
        asyncio.run(main())
    Python

    Gradual Migration Strategy

    Feature Flag System for Authentication

    Create migration/feature_flags.py:

    import redis
    import json
    from typing import Dict, Any, Optional, List
    from enum import Enum
    from dataclasses import dataclass
    
    class AuthFeatureFlag(Enum):
        JWT_AUTHENTICATION = "jwt_auth"
        SESSION_FALLBACK = "session_fallback"
        MIGRATION_MODE = "migration_mode"
        JWT_REFRESH = "jwt_refresh"
        ENHANCED_SECURITY = "enhanced_security"
        ROLE_BASED_AUTH = "rbac_auth"
    
    @dataclass
    class FeatureFlagConfig:
        enabled: bool
        rollout_percentage: float
        user_groups: List[str]
        start_date: Optional[str] = None
        end_date: Optional[str] = None
        conditions: Dict[str, Any] = None
    
    class FeatureFlagManager:
        def __init__(self, redis_client: redis.Redis):
            self.redis_client = redis_client
            self.flag_prefix = "feature_flag:"
    
        def set_flag(self, flag: AuthFeatureFlag, config: FeatureFlagConfig):
            """Set feature flag configuration"""
    
            flag_key = f"{self.flag_prefix}{flag.value}"
            flag_data = {
                "enabled": config.enabled,
                "rollout_percentage": config.rollout_percentage,
                "user_groups": config.user_groups,
                "start_date": config.start_date,
                "end_date": config.end_date,
                "conditions": config.conditions or {}
            }
    
            self.redis_client.set(flag_key, json.dumps(flag_data))
    
        def is_enabled(self, flag: AuthFeatureFlag, user_context: Dict[str, Any] = None) -> bool:
            """Check if feature flag is enabled for user"""
    
            flag_key = f"{self.flag_prefix}{flag.value}"
            flag_data = self.redis_client.get(flag_key)
    
            if not flag_data:
                return False
    
            try:
                config = json.loads(flag_data)
    
                # Check if flag is globally disabled
                if not config.get("enabled", False):
                    return False
    
                # Check rollout percentage
                if user_context and "user_id" in user_context:
                    user_hash = hash(str(user_context["user_id"])) % 100
                    if user_hash >= config.get("rollout_percentage", 0):
                        return False
    
                # Check user groups
                user_groups = user_context.get("groups", []) if user_context else []
                required_groups = config.get("user_groups", [])
    
                if required_groups and not any(group in user_groups for group in required_groups):
                    return False
    
                # Check date conditions
                from datetime import datetime
                now = datetime.utcnow()
    
                if config.get("start_date"):
                    start_date = datetime.fromisoformat(config["start_date"])
                    if now < start_date:
                        return False
    
                if config.get("end_date"):
                    end_date = datetime.fromisoformat(config["end_date"])
                    if now > end_date:
                        return False
    
                # Check custom conditions
                conditions = config.get("conditions", {})
                if conditions and not self._evaluate_conditions(conditions, user_context):
                    return False
    
                return True
    
            except (json.JSONDecodeError, ValueError):
                return False
    
        def _evaluate_conditions(self, conditions: Dict[str, Any], user_context: Dict[str, Any]) -> bool:
            """Evaluate custom conditions"""
    
            if not user_context:
                return False
    
            for key, expected_value in conditions.items():
                if key not in user_context or user_context[key] != expected_value:
                    return False
    
            return True
    
        def get_all_flags(self) -> Dict[str, Dict[str, Any]]:
            """Get all feature flags"""
    
            flags = {}
            flag_keys = self.redis_client.keys(f"{self.flag_prefix}*")
    
            for key in flag_keys:
                flag_name = key.decode('utf-8').replace(self.flag_prefix, "")
                flag_data = self.redis_client.get(key)
    
                if flag_data:
                    try:
                        flags[flag_name] = json.loads(flag_data)
                    except json.JSONDecodeError:
                        continue
    
            return flags
    
    class GradualMigrationController:
        """Controls gradual migration from session to JWT authentication"""
    
        def __init__(self, feature_flag_manager: FeatureFlagManager):
            self.feature_flags = feature_flag_manager
            self.migration_phases = [
                {"name": "Phase 1", "percentage": 10, "duration_days": 7},
                {"name": "Phase 2", "percentage": 25, "duration_days": 7},
                {"name": "Phase 3", "percentage": 50, "duration_days": 14},
                {"name": "Phase 4", "percentage": 75, "duration_days": 14},
                {"name": "Phase 5", "percentage": 100, "duration_days": 7}
            ]
    
        def initialize_migration(self):
            """Initialize gradual migration flags"""
    
            # Enable migration mode for all users
            self.feature_flags.set_flag(
                AuthFeatureFlag.MIGRATION_MODE,
                FeatureFlagConfig(
                    enabled=True,
                    rollout_percentage=100,
                    user_groups=[]
                )
            )
    
            # Start with JWT for small percentage
            self.feature_flags.set_flag(
                AuthFeatureFlag.JWT_AUTHENTICATION,
                FeatureFlagConfig(
                    enabled=True,
                    rollout_percentage=10,  # Start with 10%
                    user_groups=["beta_users", "admin"]
                )
            )
    
            # Keep session fallback enabled
            self.feature_flags.set_flag(
                AuthFeatureFlag.SESSION_FALLBACK,
                FeatureFlagConfig(
                    enabled=True,
                    rollout_percentage=100,
                    user_groups=[]
                )
            )
    
        def advance_migration_phase(self, phase_number: int):
            """Advance to next migration phase"""
    
            if phase_number <= 0 or phase_number > len(self.migration_phases):
                raise ValueError("Invalid phase number")
    
            phase = self.migration_phases[phase_number - 1]
    
            # Update JWT authentication rollout
            self.feature_flags.set_flag(
                AuthFeatureFlag.JWT_AUTHENTICATION,
                FeatureFlagConfig(
                    enabled=True,
                    rollout_percentage=phase["percentage"],
                    user_groups=["beta_users", "admin"]
                )
            )
    
            print(f"Advanced to {phase['name']}: {phase['percentage']}% rollout")
    
        def complete_migration(self):
            """Complete migration - disable session fallback"""
    
            # Enable JWT for all users
            self.feature_flags.set_flag(
                AuthFeatureFlag.JWT_AUTHENTICATION,
                FeatureFlagConfig(
                    enabled=True,
                    rollout_percentage=100,
                    user_groups=[]
                )
            )
    
            # Disable session fallback
            self.feature_flags.set_flag(
                AuthFeatureFlag.SESSION_FALLBACK,
                FeatureFlagConfig(
                    enabled=False,
                    rollout_percentage=0,
                    user_groups=[]
                )
            )
    
            print("Migration completed - JWT authentication is now primary")
    
        def rollback_migration(self):
            """Rollback migration in case of issues"""
    
            # Disable JWT authentication
            self.feature_flags.set_flag(
                AuthFeatureFlag.JWT_AUTHENTICATION,
                FeatureFlagConfig(
                    enabled=False,
                    rollout_percentage=0,
                    user_groups=[]
                )
            )
    
            # Re-enable session fallback
            self.feature_flags.set_flag(
                AuthFeatureFlag.SESSION_FALLBACK,
                FeatureFlagConfig(
                    enabled=True,
                    rollout_percentage=100,
                    user_groups=[]
                )
            )
    
            print("Migration rolled back - session authentication restored")
    
        def get_migration_status(self) -> Dict[str, Any]:
            """Get current migration status"""
    
            jwt_flag = self.feature_flags.redis_client.get(
                f"{self.feature_flags.flag_prefix}{AuthFeatureFlag.JWT_AUTHENTICATION.value}"
            )
    
            session_flag = self.feature_flags.redis_client.get(
                f"{self.feature_flags.flag_prefix}{AuthFeatureFlag.SESSION_FALLBACK.value}"
            )
    
            jwt_config = json.loads(jwt_flag) if jwt_flag else {}
            session_config = json.loads(session_flag) if session_flag else {}
    
            return {
                "jwt_enabled": jwt_config.get("enabled", False),
                "jwt_rollout_percentage": jwt_config.get("rollout_percentage", 0),
                "session_fallback_enabled": session_config.get("enabled", False),
                "migration_phase": self._get_current_phase(jwt_config.get("rollout_percentage", 0))
            }
    
        def _get_current_phase(self, percentage: float) -> str:
            """Determine current migration phase"""
    
            for i, phase in enumerate(self.migration_phases):
                if percentage <= phase["percentage"]:
                    return f"Phase {i + 1}"
    
            return "Complete"
    
    # Migration monitoring
    class MigrationMonitor:
        def __init__(self, redis_client: redis.Redis):
            self.redis_client = redis_client
    
        def log_authentication_event(self, event_type: str, user_id: int, auth_method: str, success: bool):
            """Log authentication events during migration"""
    
            event_data = {
                "timestamp": time.time(),
                "user_id": user_id,
                "auth_method": auth_method,
                "success": success,
                "event_type": event_type
            }
    
            # Store in Redis list for analysis
            self.redis_client.lpush(
                "migration:auth_events",
                json.dumps(event_data)
            )
    
            # Keep only last 10000 events
            self.redis_client.ltrim("migration:auth_events", 0, 9999)
    
        def get_migration_metrics(self) -> Dict[str, Any]:
            """Get migration performance metrics"""
    
            # Get recent events
            events = self.redis_client.lrange("migration:auth_events", 0, -1)
    
            jwt_events = []
            session_events = []
            total_events = 0
    
            for event_data in events:
                try:
                    event = json.loads(event_data)
                    total_events += 1
    
                    if event["auth_method"] == "jwt":
                        jwt_events.append(event)
                    elif event["auth_method"] == "session":
                        session_events.append(event)
    
                except json.JSONDecodeError:
                    continue
    
            # Calculate metrics
            jwt_success_rate = len([e for e in jwt_events if e["success"]]) / len(jwt_events) if jwt_events else 0
            session_success_rate = len([e for e in session_events if e["success"]]) / len(session_events) if session_events else 0
    
            return {
                "total_auth_events": total_events,
                "jwt_events": len(jwt_events),
                "session_events": len(session_events),
                "jwt_success_rate": jwt_success_rate * 100,
                "session_success_rate": session_success_rate * 100,
                "jwt_adoption_rate": (len(jwt_events) / total_events * 100) if total_events > 0 else 0
            }
    
    # Usage example
    def setup_gradual_migration():
        """Example setup for gradual migration"""
    
        import redis
        redis_client = redis.from_url("redis://localhost:6379")
    
        # Initialize components
        feature_flags = FeatureFlagManager(redis_client)
        migration_controller = GradualMigrationController(feature_flags)
        monitor = MigrationMonitor(redis_client)
    
        # Start migration
        migration_controller.initialize_migration()
    
        print("Gradual migration initialized")
        print("Phase 1: 10% of users will use JWT authentication")
        print("Monitor metrics and advance phases as needed")
    
        return migration_controller, monitor
    
    if __name__ == "__main__":
        setup_gradual_migration()
    Python

    This migration strategies chapter provides comprehensive guidance for transitioning from legacy authentication systems to JWT-based authentication, including dual authentication support, database migration utilities, and gradual rollout strategies with feature flags.


    This chapter explores emerging JWT standards, future authentication technologies, and evolving security practices that will shape the future of web authentication.

    Emerging JWT Standards

    JWT Profile for OAuth 2.1

    graph TB
        A[OAuth 2.1] --> B[Simplified Flows]
        A --> C[Enhanced Security]
        A --> D[JWT Profiles]
    
        B --> E[Authorization Code + PKCE]
        B --> F[Removed Implicit Flow]
        B --> G[Removed Resource Owner Password]
    
        C --> H[MTLS Support]
        C --> I[DPoP Tokens]
        C --> J[JWT Secured Authorization]
    
        D --> K[Structured Tokens]
        D --> L[Token Introspection]
        D --> M[Rich Authorization Requests]
    
        subgraph "Future Enhancements"
            N[Zero-Knowledge Proofs]
            O[Quantum-Resistant Algorithms]
            P[Selective Disclosure]
            Q[Credential Presentations]
        end

    JWT Secured Authorization Requests (JAR)

    Create future/jar_implementation.py:

    import jwt
    import json
    import time
    from typing import Dict, Any, Optional
    from cryptography.hazmat.primitives import hashes
    from cryptography.hazmat.primitives.asymmetric import rsa, padding
    from cryptography.hazmat.primitives.serialization import load_pem_private_key
    import base64
    import secrets
    
    class JWTSecuredAuthorizationRequest:
        """Implementation of JWT Secured Authorization Requests (RFC 9101)"""
    
        def __init__(self, client_id: str, private_key_pem: str, key_id: str):
            self.client_id = client_id
            self.private_key = load_pem_private_key(private_key_pem.encode(), password=None)
            self.key_id = key_id
            self.algorithm = "RS256"
    
        def create_request_object(
            self,
            authorization_details: Dict[str, Any],
            redirect_uri: str,
            state: str,
            nonce: Optional[str] = None
        ) -> str:
            """Create JWT-secured authorization request object"""
    
            # Create request object payload
            payload = {
                # Standard OAuth parameters
                "iss": self.client_id,
                "aud": "https://authorization-server.example.com",
                "client_id": self.client_id,
                "response_type": "code",
                "redirect_uri": redirect_uri,
                "scope": "openid profile email",
                "state": state,
    
                # JWT-specific claims
                "iat": int(time.time()),
                "exp": int(time.time()) + 300,  # 5 minutes
                "jti": secrets.token_urlsafe(16),
    
                # Rich Authorization Requests (RAR)
                "authorization_details": authorization_details,
    
                # PKCE parameters
                "code_challenge": self._generate_code_challenge(),
                "code_challenge_method": "S256",
    
                # Additional security parameters
                "max_age": 3600,  # Maximum authentication age
                "acr_values": "urn:mace:incommon:iap:silver"  # Authentication context
            }
    
            if nonce:
                payload["nonce"] = nonce
    
            # Create JWT header
            header = {
                "alg": self.algorithm,
                "kid": self.key_id,
                "typ": "oauth-authz-req+jwt"
            }
    
            # Sign the request object
            request_object = jwt.encode(
                payload,
                self.private_key,
                algorithm=self.algorithm,
                headers=header
            )
    
            return request_object
    
        def _generate_code_challenge(self) -> str:
            """Generate PKCE code challenge"""
            code_verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).decode('utf-8')
            code_verifier = code_verifier.rstrip('=')
    
            # Store code verifier for later use
            self.code_verifier = code_verifier
    
            # Create code challenge
            digest = hashes.Hash(hashes.SHA256())
            digest.update(code_verifier.encode('utf-8'))
            code_challenge = base64.urlsafe_b64encode(digest.finalize()).decode('utf-8')
            code_challenge = code_challenge.rstrip('=')
    
            return code_challenge
    
        def create_rich_authorization_request(
            self,
            resource_access: Dict[str, Any],
            redirect_uri: str,
            state: str
        ) -> str:
            """Create Rich Authorization Request (RAR) with detailed permissions"""
    
            # Example authorization details for different resource types
            authorization_details = [
                {
                    "type": "payment_initiation",
                    "actions": ["initiate", "status", "cancel"],
                    "creditor": {
                        "name": "Merchant Corp",
                        "account": "DE89370400440532013000"
                    },
                    "instructed_amount": {
                        "currency": "EUR",
                        "amount": "123.50"
                    }
                },
                {
                    "type": "account_information",
                    "actions": ["read"],
                    "accounts": [
                        {
                            "iban": "DE40100100103307118608",
                            "currency": "EUR"
                        }
                    ]
                },
                {
                    "type": "openid_credential",
                    "credential_type": "https://credentials.example.com/identity_credential",
                    "format": "jwt_vc_json",
                    "locations": ["https://credential-issuer.example.com"]
                }
            ]
    
            return self.create_request_object(
                authorization_details=authorization_details,
                redirect_uri=redirect_uri,
                state=state
            )
    
    class DemonstrableProofOfPossession:
        """Implementation of DPoP (Demonstrable Proof of Possession) tokens"""
    
        def __init__(self, private_key_pem: str):
            self.private_key = load_pem_private_key(private_key_pem.encode(), password=None)
            self.public_key = self.private_key.public_key()
    
        def create_dpop_proof(
            self,
            http_method: str,
            http_uri: str,
            access_token: Optional[str] = None,
            nonce: Optional[str] = None
        ) -> str:
            """Create DPoP proof JWT"""
    
            # Get public key JWK
            public_key_jwk = self._get_public_key_jwk()
    
            # Create DPoP proof payload
            payload = {
                "jti": secrets.token_urlsafe(16),
                "htm": http_method,
                "htu": http_uri,
                "iat": int(time.time())
            }
    
            if access_token:
                # Hash the access token
                digest = hashes.Hash(hashes.SHA256())
                digest.update(access_token.encode('utf-8'))
                ath = base64.urlsafe_b64encode(digest.finalize()).decode('utf-8').rstrip('=')
                payload["ath"] = ath
    
            if nonce:
                payload["nonce"] = nonce
    
            # Create JWT header with public key
            header = {
                "alg": "RS256",
                "typ": "dpop+jwt",
                "jwk": public_key_jwk
            }
    
            # Sign the DPoP proof
            dpop_proof = jwt.encode(
                payload,
                self.private_key,
                algorithm="RS256",
                headers=header
            )
    
            return dpop_proof
    
        def _get_public_key_jwk(self) -> Dict[str, Any]:
            """Get public key in JWK format"""
    
            # Extract public key components
            public_numbers = self.public_key.public_numbers()
    
            # Convert to JWK format
            def _int_to_base64url(value: int) -> str:
                """Convert integer to base64url encoding"""
                byte_length = (value.bit_length() + 7) // 8
                bytes_value = value.to_bytes(byte_length, byteorder='big')
                return base64.urlsafe_b64encode(bytes_value).decode('utf-8').rstrip('=')
    
            return {
                "kty": "RSA",
                "use": "sig",
                "alg": "RS256",
                "n": _int_to_base64url(public_numbers.n),
                "e": _int_to_base64url(public_numbers.e)
            }
    
        def verify_dpop_proof(
            self,
            dpop_proof: str,
            expected_method: str,
            expected_uri: str,
            access_token: Optional[str] = None
        ) -> bool:
            """Verify DPoP proof"""
    
            try:
                # Decode header to get public key
                header = jwt.get_unverified_header(dpop_proof)
    
                if header.get("typ") != "dpop+jwt":
                    return False
    
                # Extract public key from JWK
                jwk = header.get("jwk")
                if not jwk:
                    return False
    
                # Reconstruct public key
                n = int.from_bytes(
                    base64.urlsafe_b64decode(jwk["n"] + "=="),
                    byteorder='big'
                )
                e = int.from_bytes(
                    base64.urlsafe_b64decode(jwk["e"] + "=="),
                    byteorder='big'
                )
    
                public_key = rsa.RSAPublicNumbers(e, n).public_key()
    
                # Verify JWT
                payload = jwt.decode(
                    dpop_proof,
                    public_key,
                    algorithms=["RS256"]
                )
    
                # Verify claims
                if payload.get("htm") != expected_method:
                    return False
    
                if payload.get("htu") != expected_uri:
                    return False
    
                # Verify access token hash if provided
                if access_token and "ath" in payload:
                    digest = hashes.Hash(hashes.SHA256())
                    digest.update(access_token.encode('utf-8'))
                    expected_ath = base64.urlsafe_b64encode(digest.finalize()).decode('utf-8').rstrip('=')
    
                    if payload["ath"] != expected_ath:
                        return False
    
                return True
    
            except Exception:
                return False
    
    # Selective Disclosure for JWT
    class SelectiveDisclosureJWT:
        """Implementation of Selective Disclosure for JWTs (SD-JWT)"""
    
        def __init__(self, issuer_key: str):
            self.issuer_key = issuer_key
    
        def create_sd_jwt(
            self,
            claims: Dict[str, Any],
            disclosable_claims: list,
            holder_public_key: Optional[str] = None
        ) -> Dict[str, Any]:
            """Create Selective Disclosure JWT"""
    
            import hashlib
    
            # Separate disclosable and non-disclosable claims
            disclosed_claims = {}
            disclosures = []
            sd_hash_claims = {}
    
            for claim_name, claim_value in claims.items():
                if claim_name in disclosable_claims:
                    # Create disclosure for this claim
                    salt = secrets.token_urlsafe(16)
                    disclosure = [salt, claim_name, claim_value]
                    disclosure_json = json.dumps(disclosure, separators=(',', ':'))
                    disclosure_b64 = base64.urlsafe_b64encode(
                        disclosure_json.encode('utf-8')
                    ).decode('utf-8').rstrip('=')
    
                    disclosures.append(disclosure_b64)
    
                    # Create hash for SD claim
                    digest = hashlib.sha256(disclosure_b64.encode('utf-8')).digest()
                    sd_hash = base64.urlsafe_b64encode(digest).decode('utf-8').rstrip('=')
    
                    if "_sd" not in sd_hash_claims:
                        sd_hash_claims["_sd"] = []
                    sd_hash_claims["_sd"].append(sd_hash)
                else:
                    # Include claim directly
                    disclosed_claims[claim_name] = claim_value
    
            # Combine claims
            jwt_payload = {**disclosed_claims, **sd_hash_claims}
    
            if holder_public_key:
                jwt_payload["cnf"] = {"jwk": holder_public_key}
    
            # Add SD-JWT specific claims
            jwt_payload["_sd_alg"] = "sha-256"
            jwt_payload["iss"] = "https://issuer.example.com"
            jwt_payload["iat"] = int(time.time())
            jwt_payload["exp"] = int(time.time()) + 3600
    
            # Create JWT
            sd_jwt = jwt.encode(jwt_payload, self.issuer_key, algorithm="HS256")
    
            return {
                "jwt": sd_jwt,
                "disclosures": disclosures
            }
    
        def create_presentation(
            self,
            sd_jwt_data: Dict[str, Any],
            claims_to_disclose: list,
            holder_private_key: Optional[str] = None
        ) -> str:
            """Create presentation with selective disclosure"""
    
            # Select disclosures for claims to reveal
            selected_disclosures = []
    
            for disclosure_b64 in sd_jwt_data["disclosures"]:
                # Decode disclosure to check claim name
                disclosure_json = base64.urlsafe_b64decode(
                    disclosure_b64 + "=="
                ).decode('utf-8')
    
                disclosure = json.loads(disclosure_json)
                claim_name = disclosure[1]
    
                if claim_name in claims_to_disclose:
                    selected_disclosures.append(disclosure_b64)
    
            # Create presentation format: <JWT>~<disclosure>~<disclosure>~...
            presentation = sd_jwt_data["jwt"]
    
            for disclosure in selected_disclosures:
                presentation += f"~{disclosure}"
    
            # Add holder binding if private key provided
            if holder_private_key:
                # Create key binding JWT
                kb_payload = {
                    "iat": int(time.time()),
                    "aud": "https://verifier.example.com",
                    "nonce": secrets.token_urlsafe(16)
                }
    
                kb_jwt = jwt.encode(kb_payload, holder_private_key, algorithm="HS256")
                presentation += f"~{kb_jwt}"
            else:
                presentation += "~"
    
            return presentation
    
        def verify_presentation(
            self,
            presentation: str,
            issuer_public_key: str,
            expected_holder_key: Optional[str] = None
        ) -> Dict[str, Any]:
            """Verify selective disclosure presentation"""
    
            import hashlib
    
            # Parse presentation
            parts = presentation.split("~")
            jwt_part = parts[0]
            disclosure_parts = parts[1:-1]
            kb_jwt_part = parts[-1] if parts[-1] else None
    
            # Verify main JWT
            try:
                jwt_payload = jwt.decode(
                    jwt_part,
                    issuer_public_key,
                    algorithms=["HS256"]
                )
            except jwt.JWTError as e:
                raise ValueError(f"Invalid JWT: {e}")
    
            # Verify disclosures
            disclosed_claims = {}
            sd_hashes = jwt_payload.get("_sd", [])
    
            for disclosure_b64 in disclosure_parts:
                if not disclosure_b64:
                    continue
    
                # Verify disclosure hash
                digest = hashlib.sha256(disclosure_b64.encode('utf-8')).digest()
                disclosure_hash = base64.urlsafe_b64encode(digest).decode('utf-8').rstrip('=')
    
                if disclosure_hash not in sd_hashes:
                    raise ValueError("Invalid disclosure hash")
    
                # Decode disclosure
                disclosure_json = base64.urlsafe_b64decode(
                    disclosure_b64 + "=="
                ).decode('utf-8')
    
                disclosure = json.loads(disclosure_json)
                salt, claim_name, claim_value = disclosure
    
                disclosed_claims[claim_name] = claim_value
    
            # Verify holder binding if present
            if kb_jwt_part and expected_holder_key:
                try:
                    kb_payload = jwt.decode(
                        kb_jwt_part,
                        expected_holder_key,
                        algorithms=["HS256"]
                    )
                except jwt.JWTError as e:
                    raise ValueError(f"Invalid key binding: {e}")
    
            # Combine verified claims
            verified_claims = {
                k: v for k, v in jwt_payload.items()
                if not k.startswith("_sd")
            }
            verified_claims.update(disclosed_claims)
    
            return verified_claims
    
    # Example usage
    def demo_future_standards():
        """Demonstrate future JWT standards"""
    
        # Generate keys for demonstration
        private_key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=2048
        )
    
        from cryptography.hazmat.primitives import serialization
        private_key_pem = private_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.PKCS8,
            encryption_algorithm=serialization.NoEncryption()
        ).decode('utf-8')
    
        # 1. JWT Secured Authorization Request
        print("=== JWT Secured Authorization Request ===")
        jar = JWTSecuredAuthorizationRequest(
            client_id="client123",
            private_key_pem=private_key_pem,
            key_id="key1"
        )
    
        request_object = jar.create_rich_authorization_request(
            resource_access={"type": "payment", "amount": "100.00"},
            redirect_uri="https://client.example.com/callback",
            state="xyz123"
        )
    
        print(f"Request Object: {request_object[:100]}...")
    
        # 2. DPoP Implementation
        print("\n=== DPoP (Demonstrable Proof of Possession) ===")
        dpop = DemonstrableProofOfPossession(private_key_pem)
    
        dpop_proof = dpop.create_dpop_proof(
            http_method="POST",
            http_uri="https://api.example.com/resource",
            access_token="access_token_value"
        )
    
        print(f"DPoP Proof: {dpop_proof[:100]}...")
    
        # 3. Selective Disclosure JWT
        print("\n=== Selective Disclosure JWT ===")
        sd_jwt = SelectiveDisclosureJWT("secret_key")
    
        user_claims = {
            "sub": "user123",
            "name": "John Doe",
            "email": "john@example.com",
            "age": 30,
            "address": "123 Main St",
            "phone": "555-1234"
        }
    
        sd_jwt_data = sd_jwt.create_sd_jwt(
            claims=user_claims,
            disclosable_claims=["email", "age", "address", "phone"]
        )
    
        print(f"SD-JWT: {sd_jwt_data['jwt'][:100]}...")
        print(f"Disclosures count: {len(sd_jwt_data['disclosures'])}")
    
        # Create presentation with only name and email
        presentation = sd_jwt.create_presentation(
            sd_jwt_data,
            claims_to_disclose=["name", "email"]
        )
    
        print(f"Presentation: {presentation[:100]}...")
    
    if __name__ == "__main__":
        demo_future_standards()
    Python

    WebAuthn Integration with JWT

    FIDO2/WebAuthn + JWT Implementation

    Create future/webauthn_jwt.py:

    import base64
    import json
    import secrets
    import hashlib
    from typing import Dict, Any, Optional, List
    import jwt
    import time
    from cryptography.hazmat.primitives import hashes
    from cryptography.hazmat.primitives.asymmetric import ec
    from cryptography.hazmat.primitives.serialization import load_der_public_key
    
    class WebAuthnJWTManager:
        """Integration of WebAuthn with JWT for passwordless authentication"""
    
        def __init__(self, rp_id: str, rp_name: str, jwt_secret: str):
            self.rp_id = rp_id  # Relying Party ID
            self.rp_name = rp_name
            self.jwt_secret = jwt_secret
            self.origin = f"https://{rp_id}"
    
        def generate_registration_options(self, user_id: str, username: str) -> Dict[str, Any]:
            """Generate WebAuthn registration options"""
    
            challenge = secrets.token_bytes(32)
    
            options = {
                "publicKey": {
                    "challenge": base64.urlsafe_b64encode(challenge).decode('utf-8').rstrip('='),
                    "rp": {
                        "name": self.rp_name,
                        "id": self.rp_id
                    },
                    "user": {
                        "id": base64.urlsafe_b64encode(user_id.encode()).decode('utf-8').rstrip('='),
                        "name": username,
                        "displayName": username
                    },
                    "pubKeyCredParams": [
                        {"alg": -7, "type": "public-key"},   # ES256
                        {"alg": -257, "type": "public-key"}, # RS256
                        {"alg": -37, "type": "public-key"}   # PS256
                    ],
                    "authenticatorSelection": {
                        "authenticatorAttachment": "platform",  # or "cross-platform"
                        "userVerification": "required",
                        "residentKey": "preferred"
                    },
                    "timeout": 60000,
                    "attestation": "direct"
                }
            }
    
            # Store challenge for later verification
            self._store_challenge(user_id, challenge)
    
            return options
    
        def verify_registration(
            self,
            user_id: str,
            credential_response: Dict[str, Any]
        ) -> Dict[str, Any]:
            """Verify WebAuthn registration response and create credential"""
    
            # Extract response data
            response = credential_response["response"]
            client_data = json.loads(
                base64.urlsafe_b64decode(response["clientDataJSON"] + "==").decode('utf-8')
            )
    
            # Verify challenge
            stored_challenge = self._get_stored_challenge(user_id)
            received_challenge = base64.urlsafe_b64decode(client_data["challenge"] + "==")
    
            if stored_challenge != received_challenge:
                raise ValueError("Challenge mismatch")
    
            # Verify origin
            if client_data["origin"] != self.origin:
                raise ValueError("Origin mismatch")
    
            # Verify type
            if client_data["type"] != "webauthn.create":
                raise ValueError("Invalid ceremony type")
    
            # Parse attestation object
            attestation_object = base64.urlsafe_b64decode(
                response["attestationObject"] + "=="
            )
    
            # In a real implementation, you would:
            # 1. Parse CBOR attestation object
            # 2. Verify attestation statement
            # 3. Extract public key
            # 4. Store credential
    
            # For this example, we'll simulate the process
            credential_id = credential_response["id"]
    
            # Create credential record
            credential = {
                "id": credential_id,
                "user_id": user_id,
                "public_key": response["attestationObject"],  # Simplified
                "counter": 0,
                "created_at": time.time()
            }
    
            return credential
    
        def generate_authentication_options(self, user_id: Optional[str] = None) -> Dict[str, Any]:
            """Generate WebAuthn authentication options"""
    
            challenge = secrets.token_bytes(32)
    
            options = {
                "publicKey": {
                    "challenge": base64.urlsafe_b64encode(challenge).decode('utf-8').rstrip('='),
                    "timeout": 60000,
                    "rpId": self.rp_id,
                    "userVerification": "required"
                }
            }
    
            # Add allowed credentials if user is known
            if user_id:
                credentials = self._get_user_credentials(user_id)
                options["publicKey"]["allowCredentials"] = [
                    {
                        "type": "public-key",
                        "id": cred["id"]
                    }
                    for cred in credentials
                ]
    
                # Store challenge for specific user
                self._store_challenge(user_id, challenge)
            else:
                # Store challenge for any user (discoverable credentials)
                self._store_challenge("*", challenge)
    
            return options
    
        def verify_authentication_and_create_jwt(
            self,
            credential_response: Dict[str, Any],
            user_id: Optional[str] = None
        ) -> Dict[str, Any]:
            """Verify WebAuthn authentication and create JWT token"""
    
            # Extract response data
            response = credential_response["response"]
            credential_id = credential_response["id"]
    
            # Get stored credential
            credential = self._get_credential(credential_id)
            if not credential:
                raise ValueError("Credential not found")
    
            # Verify user if specified
            if user_id and credential["user_id"] != user_id:
                raise ValueError("Credential does not belong to user")
    
            # Parse client data
            client_data = json.loads(
                base64.urlsafe_b64decode(response["clientDataJSON"] + "==").decode('utf-8')
            )
    
            # Verify challenge
            stored_challenge = self._get_stored_challenge(
                credential["user_id"] if user_id else "*"
            )
            received_challenge = base64.urlsafe_b64decode(client_data["challenge"] + "==")
    
            if stored_challenge != received_challenge:
                raise ValueError("Challenge mismatch")
    
            # Verify origin and type
            if client_data["origin"] != self.origin:
                raise ValueError("Origin mismatch")
    
            if client_data["type"] != "webauthn.get":
                raise ValueError("Invalid ceremony type")
    
            # In a real implementation, verify the signature
            # This would involve:
            # 1. Parsing authenticator data
            # 2. Verifying signature with stored public key
            # 3. Checking counter value
    
            # For this example, we'll assume verification passed
    
            # Create JWT token
            payload = {
                "sub": credential["user_id"],
                "iss": f"https://{self.rp_id}",
                "aud": f"https://{self.rp_id}",
                "iat": int(time.time()),
                "exp": int(time.time()) + 3600,
                "auth_method": "webauthn",
                "credential_id": credential_id,
                "user_verification": True,
                "authenticator_attachment": "platform"  # or from response
            }
    
            token = jwt.encode(payload, self.jwt_secret, algorithm="HS256")
    
            return {
                "access_token": token,
                "token_type": "Bearer",
                "expires_in": 3600,
                "user_id": credential["user_id"]
            }
    
        def create_webauthn_jwt_middleware(self):
            """Create middleware for WebAuthn JWT verification"""
    
            async def webauthn_jwt_middleware(request, call_next):
                """Middleware to verify WebAuthn-issued JWTs"""
    
                # Extract token
                auth_header = request.headers.get("Authorization")
                if not auth_header or not auth_header.startswith("Bearer "):
                    return await call_next(request)
    
                token = auth_header.split(" ")[1]
    
                try:
                    # Decode and verify JWT
                    payload = jwt.decode(token, self.jwt_secret, algorithms=["HS256"])
    
                    # Additional WebAuthn-specific verification
                    if payload.get("auth_method") != "webauthn":
                        raise ValueError("Not a WebAuthn token")
    
                    # Verify credential still exists
                    credential_id = payload.get("credential_id")
                    if not self._get_credential(credential_id):
                        raise ValueError("Credential no longer valid")
    
                    # Add user context to request
                    request.state.current_user = {
                        "user_id": payload["sub"],
                        "auth_method": "webauthn",
                        "credential_id": credential_id,
                        "user_verification": payload.get("user_verification", False)
                    }
    
                    return await call_next(request)
    
                except (jwt.JWTError, ValueError) as e:
                    from fastapi import HTTPException, status
                    raise HTTPException(
                        status_code=status.HTTP_401_UNAUTHORIZED,
                        detail=f"Invalid WebAuthn token: {e}"
                    )
    
            return webauthn_jwt_middleware
    
        # Helper methods (simplified storage)
        def _store_challenge(self, user_id: str, challenge: bytes):
            """Store challenge for verification (use Redis in production)"""
            # Simplified storage - use proper storage in production
            if not hasattr(self, '_challenges'):
                self._challenges = {}
            self._challenges[user_id] = challenge
    
        def _get_stored_challenge(self, user_id: str) -> bytes:
            """Get stored challenge"""
            if not hasattr(self, '_challenges'):
                return None
            return self._challenges.get(user_id)
    
        def _get_credential(self, credential_id: str) -> Optional[Dict[str, Any]]:
            """Get stored credential (use database in production)"""
            # Simplified storage - use proper database in production
            if not hasattr(self, '_credentials'):
                self._credentials = {}
            return self._credentials.get(credential_id)
    
        def _get_user_credentials(self, user_id: str) -> List[Dict[str, Any]]:
            """Get all credentials for user"""
            if not hasattr(self, '_credentials'):
                return []
    
            return [
                cred for cred in self._credentials.values()
                if cred["user_id"] == user_id
            ]
    
    class PasskeyJWTManager(WebAuthnJWTManager):
        """Extended WebAuthn manager for Passkey support"""
    
        def __init__(self, rp_id: str, rp_name: str, jwt_secret: str):
            super().__init__(rp_id, rp_name, jwt_secret)
    
        def generate_passkey_registration_options(
            self,
            user_id: str,
            username: str
        ) -> Dict[str, Any]:
            """Generate registration options optimized for passkeys"""
    
            options = self.generate_registration_options(user_id, username)
    
            # Optimize for passkeys
            options["publicKey"]["authenticatorSelection"].update({
                "residentKey": "required",  # Must store credential
                "requireResidentKey": True,
                "userVerification": "required"
            })
    
            # Add hints for better UX
            options["publicKey"]["hints"] = ["client-device", "hybrid"]
    
            return options
    
        def create_cross_device_authentication_url(
            self,
            options: Dict[str, Any]
        ) -> str:
            """Create URL for cross-device authentication (QR code)"""
    
            # Encode options for cross-device transport
            options_b64 = base64.urlsafe_b64encode(
                json.dumps(options).encode('utf-8')
            ).decode('utf-8').rstrip('=')
    
            # Create URL with encoded options
            return f"https://auth.{self.rp_id}/cross-device?options={options_b64}"
    
        def verify_passkey_and_create_enhanced_jwt(
            self,
            credential_response: Dict[str, Any]
        ) -> Dict[str, Any]:
            """Verify passkey and create enhanced JWT with additional claims"""
    
            # Standard verification
            result = self.verify_authentication_and_create_jwt(credential_response)
    
            # Decode the JWT to add passkey-specific claims
            payload = jwt.decode(
                result["access_token"],
                self.jwt_secret,
                algorithms=["HS256"]
            )
    
            # Add passkey-specific claims
            payload.update({
                "auth_method": "passkey",
                "passkey_verified": True,
                "authenticator_type": "multi-device",  # or "single-device"
                "cross_platform": True,
                "backup_eligible": True,  # From authenticator data
                "backup_state": False    # From authenticator data
            })
    
            # Create new JWT with enhanced claims
            enhanced_token = jwt.encode(payload, self.jwt_secret, algorithm="HS256")
    
            result["access_token"] = enhanced_token
            return result
    
    # Frontend JavaScript for WebAuthn + JWT
    webauthn_frontend_js = '''
    class WebAuthnJWTClient {
        constructor(baseURL) {
            this.baseURL = baseURL;
            this.accessToken = localStorage.getItem('webauthn_token');
        }
    
        async registerPasskey(username) {
            try {
                // Get registration options from server
                const optionsResponse = await fetch(`${this.baseURL}/webauthn/register/begin`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ username })
                });
    
                const options = await optionsResponse.json();
    
                // Convert base64url to ArrayBuffer
                options.publicKey.challenge = this.base64urlToBuffer(options.publicKey.challenge);
                options.publicKey.user.id = this.base64urlToBuffer(options.publicKey.user.id);
    
                // Create credential
                const credential = await navigator.credentials.create(options);
    
                // Prepare response for server
                const credentialResponse = {
                    id: credential.id,
                    response: {
                        clientDataJSON: this.bufferToBase64url(credential.response.clientDataJSON),
                        attestationObject: this.bufferToBase64url(credential.response.attestationObject)
                    }
                };
    
                // Send to server for verification
                const verifyResponse = await fetch(`${this.baseURL}/webauthn/register/complete`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(credentialResponse)
                });
    
                const result = await verifyResponse.json();
    
                if (result.success) {
                    console.log('Passkey registered successfully');
                    return result;
                } else {
                    throw new Error(result.error);
                }
    
            } catch (error) {
                console.error('Passkey registration failed:', error);
                throw error;
            }
        }
    
        async authenticateWithPasskey(username = null) {
            try {
                // Get authentication options
                const optionsResponse = await fetch(`${this.baseURL}/webauthn/authenticate/begin`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ username })
                });
    
                const options = await optionsResponse.json();
    
                // Convert challenge to ArrayBuffer
                options.publicKey.challenge = this.base64urlToBuffer(options.publicKey.challenge);
    
                // Convert allowCredentials if present
                if (options.publicKey.allowCredentials) {
                    options.publicKey.allowCredentials.forEach(cred => {
                        cred.id = this.base64urlToBuffer(cred.id);
                    });
                }
    
                // Get credential
                const credential = await navigator.credentials.get(options);
    
                // Prepare response
                const credentialResponse = {
                    id: credential.id,
                    response: {
                        clientDataJSON: this.bufferToBase64url(credential.response.clientDataJSON),
                        authenticatorData: this.bufferToBase64url(credential.response.authenticatorData),
                        signature: this.bufferToBase64url(credential.response.signature),
                        userHandle: credential.response.userHandle ? 
                            this.bufferToBase64url(credential.response.userHandle) : null
                    }
                };
    
                // Send to server for verification and JWT creation
                const verifyResponse = await fetch(`${this.baseURL}/webauthn/authenticate/complete`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(credentialResponse)
                });
    
                const result = await verifyResponse.json();
    
                if (result.access_token) {
                    // Store JWT token
                    this.accessToken = result.access_token;
                    localStorage.setItem('webauthn_token', this.accessToken);
    
                    console.log('Authenticated successfully with passkey');
                    return result;
                } else {
                    throw new Error(result.error || 'Authentication failed');
                }
    
            } catch (error) {
                console.error('Passkey authentication failed:', error);
                throw error;
            }
        }
    
        async makeAuthenticatedRequest(url, options = {}) {
            if (!this.accessToken) {
                throw new Error('No access token available');
            }
    
            const requestOptions = {
                ...options,
                headers: {
                    'Authorization': `Bearer ${this.accessToken}`,
                    'Content-Type': 'application/json',
                    ...options.headers
                }
            };
    
            const response = await fetch(`${this.baseURL}${url}`, requestOptions);
    
            if (response.status === 401) {
                // Token might be expired
                this.accessToken = null;
                localStorage.removeItem('webauthn_token');
                throw new Error('Authentication expired');
            }
    
            return response;
        }
    
        // Utility methods
        base64urlToBuffer(base64url) {
            const padding = '='.repeat((4 - base64url.length % 4) % 4);
            const base64 = (base64url + padding).replace(/\-/g, '+').replace(/_/g, '/');
            return Uint8Array.from(atob(base64), c => c.charCodeAt(0));
        }
    
        bufferToBase64url(buffer) {
            const bytes = new Uint8Array(buffer);
            let str = '';
            for (const byte of bytes) {
                str += String.fromCharCode(byte);
            }
            return btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
        }
    
        logout() {
            this.accessToken = null;
            localStorage.removeItem('webauthn_token');
        }
    }
    
    // Usage example
    const webauthnClient = new WebAuthnJWTClient('/api');
    
    // Register passkey
    document.getElementById('register-btn').addEventListener('click', async () => {
        try {
            await webauthnClient.registerPasskey('user@example.com');
            alert('Passkey registered successfully!');
        } catch (error) {
            alert('Registration failed: ' + error.message);
        }
    });
    
    // Authenticate with passkey
    document.getElementById('login-btn').addEventListener('click', async () => {
        try {
            await webauthnClient.authenticateWithPasskey();
            alert('Logged in successfully!');
        } catch (error) {
            alert('Login failed: ' + error.message);
        }
    });
    '''
    
    def demo_webauthn_jwt():
        """Demonstrate WebAuthn + JWT integration"""
    
        # Initialize WebAuthn JWT manager
        webauthn_jwt = PasskeyJWTManager(
            rp_id="example.com",
            rp_name="Example Corp",
            jwt_secret="your-jwt-secret"
        )
    
        print("=== WebAuthn + JWT Integration Demo ===")
    
        # 1. Generate registration options
        print("1. Generating passkey registration options...")
        reg_options = webauthn_jwt.generate_passkey_registration_options(
            user_id="user123",
            username="john@example.com"
        )
        print(f"Challenge: {reg_options['publicKey']['challenge'][:20]}...")
    
        # 2. Generate authentication options
        print("2. Generating authentication options...")
        auth_options = webauthn_jwt.generate_authentication_options("user123")
        print(f"Challenge: {auth_options['publicKey']['challenge'][:20]}...")
    
        # 3. Create cross-device URL
        print("3. Creating cross-device authentication URL...")
        cross_device_url = webauthn_jwt.create_cross_device_authentication_url(auth_options)
        print(f"QR Code URL: {cross_device_url[:50]}...")
    
        print("\nWebAuthn + JWT integration provides:")
        print("- Passwordless authentication")
        print("- Cross-device support")
        print("- Enhanced security with biometrics")
        print("- Seamless JWT token issuance")
    
    if __name__ == "__main__":
        demo_webauthn_jwt()
    Python

    This future standards chapter covers emerging JWT technologies, selective disclosure, WebAuthn integration, and advanced authentication patterns that represent the future of web authentication security.


    24. Summary and Best Practices

    This comprehensive guide has taken you through the complete journey of JWT implementation, from basic concepts to expert-level production systems. Let’s summarize the key learnings and best practices.

    Key Takeaways

    JWT Fundamentals

    • JWT Structure: Header, Payload, Signature provide secure, stateless authentication
    • Cryptographic Security: Use strong algorithms (RS256/ES256 for production)
    • Token Lifecycle: Proper creation, validation, and expiration management
    • Security First: Always prioritize security over convenience

    Implementation Best Practices

    Backend Security
    # ✅ Secure JWT Configuration
    JWT_CONFIG = {
        "algorithm": "RS256",           # Use asymmetric algorithms
        "access_token_expire": 900,     # 15 minutes
        "refresh_token_expire": 604800, # 7 days
        "issuer": "your-app.com",
        "audience": "your-app-users",
        "verify_signature": True,
        "verify_exp": True,
        "verify_iat": True,
        "require_exp": True,
        "require_iat": True
    }
    
    # ✅ Proper Error Handling
    @app.exception_handler(JWTError)
    async def jwt_exception_handler(request: Request, exc: JWTError):
        return JSONResponse(
            status_code=401,
            content={"detail": "Invalid authentication credentials"}
        )
    
    # ✅ Secure Password Handling
    def verify_password(plain_password: str, hashed_password: str) -> bool:
        return pwd_context.verify(plain_password, hashed_password)
    
    def get_password_hash(password: str) -> str:
        return pwd_context.hash(password)
    Python
    Frontend Security
    // ✅ Secure Token Storage
    class SecureTokenManager {
        static setToken(token) {
            // Use httpOnly cookies for sensitive tokens
            document.cookie = `access_token=${token}; HttpOnly; Secure; SameSite=Strict`;
        }
    
        static getToken() {
            // Implement proper token extraction
            return this.getCookie('access_token');
        }
    
        static clearToken() {
            document.cookie = 'access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
        }
    }
    
    // ✅ Automatic Token Refresh
    class AuthManager {
        async makeRequest(url, options) {
            let token = TokenManager.getToken();
    
            // Check if token needs refresh
            if (this.tokenNeedsRefresh(token)) {
                token = await this.refreshToken();
            }
    
            return fetch(url, {
                ...options,
                headers: {
                    'Authorization': `Bearer ${token}`,
                    ...options.headers
                }
            });
        }
    }
    JavaScript

    Security Checklist

    Essential Security Measures
    • Use HTTPS in production
    • Implement proper CORS policies
    • Use strong, unique secret keys
    • Implement token rotation
    • Add rate limiting
    • Validate all inputs
    • Use secure headers
    • Implement proper logging
    • Regular security audits
    • Keep dependencies updated
    Token Security
    • Short-lived access tokens (15-30 minutes)
    • Secure refresh token storage
    • Token blacklisting capability
    • Proper token validation
    • Audience and issuer verification
    • Protection against replay attacks
    • Secure key management
    • Regular key rotation
    Production Deployment
    • Environment-specific configurations
    • Database connection pooling
    • Caching strategy implementation
    • Monitoring and alerting
    • Backup and recovery procedures
    • Performance optimization
    • Load balancing configuration
    • SSL/TLS certificates

    Architecture Patterns

    graph TB
        subgraph "Production Architecture"
            A[Load Balancer] --> B[API Gateway]
            B --> C[Auth Service]
            B --> D[User Service]
            B --> E[Business Services]
    
            C --> F[Redis Cache]
            C --> G[Database]
            D --> G
            E --> G
    
            H[Monitoring] --> A
            H --> B
            H --> C
            H --> D
            H --> E
        end
    
        subgraph "Security Layer"
            I[WAF]
            J[Rate Limiting]
            K[JWT Validation]
            L[RBAC]
        end
    
        A --> I
        I --> J
        J --> K
        K --> L

    Microservices Considerations

    • Service-to-Service Authentication: Use dedicated service tokens
    • API Gateway Pattern: Centralize authentication and authorization
    • Token Propagation: Securely pass user context between services
    • Circuit Breakers: Implement fault tolerance
    • Distributed Tracing: Track requests across services

    Performance Optimization

    Key Optimization Strategies

    1. Token Caching: Multi-level caching (memory + Redis)
    2. Database Optimization: Connection pooling and query optimization
    3. Token Size: Minimize payload size
    4. Algorithm Choice: Balance security and performance
    5. Network Optimization: Compression and CDN usage

    Monitoring Metrics

    # Essential metrics to track
    METRICS_TO_MONITOR = {
        "authentication": [
            "login_success_rate",
            "token_generation_time",
            "token_validation_time",
            "failed_authentication_attempts"
        ],
        "performance": [
            "api_response_time",
            "database_query_time",
            "cache_hit_ratio",
            "token_refresh_frequency"
        ],
        "security": [
            "suspicious_login_attempts",
            "token_validation_failures",
            "brute_force_attempts",
            "unusual_access_patterns"
        ]
    }
    JavaScript

    Migration Strategy

    Phased Approach

    1. Assessment Phase: Analyze current system
    2. Dual Authentication: Run both systems in parallel
    3. Gradual Migration: Use feature flags for rollout
    4. Legacy Sunset: Phase out old system
    5. Optimization: Fine-tune new system

    Future Considerations

    Emerging Technologies

    • WebAuthn/Passkeys: Passwordless authentication
    • Zero-Knowledge Proofs: Enhanced privacy
    • Quantum-Resistant Algorithms: Future-proof security
    • Selective Disclosure: Privacy-preserving claims
    • DeFi Integration: Blockchain-based identity

    Continuous Improvement

    • Stay updated with security best practices
    • Regular penetration testing
    • Community engagement and learning
    • Technology stack evaluation
    • Performance benchmarking

    Common Pitfalls to Avoid

    Security Pitfalls

    ❌ Storing sensitive data in JWT payload
    ❌ Using weak or default secret keys
    ❌ Ignoring token expiration
    ❌ Not validating token signatures
    ❌ Missing rate limiting
    ❌ Inadequate error handling
    ❌ Exposing debug information

    Implementation Pitfalls

    ❌ Overly complex token structures
    ❌ Poor error messages
    ❌ Lack of proper testing
    ❌ Missing monitoring
    ❌ Inadequate documentation
    ❌ Not planning for scalability

    Final Recommendations

    For Beginners

    1. Start with basic implementation
    2. Focus on security fundamentals
    3. Use established libraries
    4. Implement comprehensive testing
    5. Follow security best practices

    For Intermediate Developers

    1. Implement advanced features gradually
    2. Focus on performance optimization
    3. Add comprehensive monitoring
    4. Plan for scalability
    5. Consider microservices patterns

    For Expert Developers

    1. Contribute to open-source solutions
    2. Stay current with emerging standards
    3. Implement custom security enhancements
    4. Mentor junior developers
    5. Share knowledge with the community

    Conclusion

    JWT authentication, when implemented correctly, provides a robust, scalable, and secure solution for modern web applications. This guide has covered:

    • Fundamental Concepts: From basic JWT structure to advanced cryptographic principles
    • Practical Implementation: Complete backend and frontend solutions
    • Production Readiness: Deployment, monitoring, and optimization strategies
    • Advanced Patterns: Microservices, performance optimization, and migration strategies
    • Future Technologies: Emerging standards and authentication trends

    The key to successful JWT implementation is balancing security, performance, and usability while maintaining a clear understanding of the underlying principles. Continue learning, stay updated with security best practices, and always prioritize the security of your users’ data.

    Resources for Continued Learning

    Official Specifications

    Security Resources

    Tools and Libraries

    • Python: PyJWT, python-jose, authlib
    • JavaScript: jsonwebtoken, jose, node-jsonwebtoken
    • Testing: pytest, Jest, Playwright
    • Monitoring: Prometheus, Grafana, ELK Stack

    Thank you for following this comprehensive JWT guide. Apply these concepts responsibly and always prioritize security in your implementations.


    24. System Design and Architectural Patterns

    Introduction to JWT System Architecture

    Building production-ready JWT authentication systems requires careful architectural planning. This chapter covers enterprise-level design patterns, scalability considerations, and architectural best practices for JWT implementations.

    Core Architectural Principles

    1. Separation of Concerns

    graph TB
        subgraph "Authentication Layer"
            A[Auth Service]
            B[Token Generator]
            C[Token Validator]
        end
    
        subgraph "Business Logic Layer"
            D[User Service]
            E[Resource Service]
            F[Permission Service]
        end
    
        subgraph "Data Layer"
            G[User Database]
            H[Token Blacklist]
            I[Audit Logs]
        end
    
        A --> B
        A --> C
        B --> G
        C --> H
        D --> G
        E --> F
        F --> G

    2. Single Responsibility Principle

    Each component should have one clear responsibility:

    # ❌ Bad: Mixed responsibilities
    class AuthService:
        def authenticate_user(self, credentials):
            # Validates credentials
            # Generates token
            # Logs audit
            # Sends notification
            # Updates user status
            pass
    
    # ✅ Good: Single responsibility
    class AuthenticationValidator:
        def validate_credentials(self, credentials):
            """Only validates credentials"""
            pass
    
    class TokenGenerator:
        def create_token(self, user_data):
            """Only generates tokens"""
            pass
    
    class AuditLogger:
        def log_auth_event(self, event):
            """Only handles logging"""
            pass
    
    class NotificationService:
        def send_login_notification(self, user):
            """Only sends notifications"""
            pass
    Python

    Enterprise Architectural Patterns

    Pattern 1: Gateway Pattern (API Gateway)

    Use Case: Centralized entry point for all client requests

    sequenceDiagram
        participant Client
        participant Gateway as API Gateway
        participant Auth as Auth Service
        participant UserSvc as User Service
        participant OrderSvc as Order Service
    
        Client->>Gateway: Request with JWT
        Gateway->>Gateway: Extract & Validate Token
        Gateway->>Auth: Verify Token (Cache First)
        Auth->>Gateway: Token Valid + Claims
        Gateway->>Gateway: Check Rate Limits
        Gateway->>Gateway: Apply Routing Rules
        Gateway->>UserSvc: Forward Request (if user endpoint)
        Gateway->>OrderSvc: Forward Request (if order endpoint)
        UserSvc->>Gateway: Response
        Gateway->>Client: Response

    Implementation:

    # FastAPI API Gateway Example
    from fastapi import FastAPI, Request, HTTPException, Depends
    from fastapi.responses import JSONResponse
    import httpx
    from typing import Dict, Optional
    import redis
    from datetime import timedelta
    
    class APIGateway:
        def __init__(self):
            self.app = FastAPI(title="API Gateway")
            self.redis_client = redis.Redis(host='localhost', port=6379, db=0)
            self.token_cache_ttl = 300  # 5 minutes
    
            # Service registry
            self.services = {
                "auth": "http://auth-service:8001",
                "users": "http://user-service:8002",
                "orders": "http://order-service:8003",
                "products": "http://product-service:8004"
            }
    
            self.setup_routes()
    
        def setup_routes(self):
            @self.app.middleware("http")
            async def gateway_middleware(request: Request, call_next):
                # Extract token
                auth_header = request.headers.get("Authorization")
                if auth_header and auth_header.startswith("Bearer "):
                    token = auth_header.split(" ")[1]
    
                    # Validate token (with caching)
                    if not await self.validate_token_cached(token):
                        return JSONResponse(
                            status_code=401,
                            content={"detail": "Invalid or expired token"}
                        )
    
                    # Rate limiting
                    if not await self.check_rate_limit(token):
                        return JSONResponse(
                            status_code=429,
                            content={"detail": "Rate limit exceeded"}
                        )
    
                response = await call_next(request)
                return response
    
            @self.app.api_route("/{service}/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
            async def gateway_route(service: str, path: str, request: Request):
                """Route requests to appropriate microservice"""
    
                if service not in self.services:
                    raise HTTPException(status_code=404, detail="Service not found")
    
                # Build target URL
                target_url = f"{self.services[service]}/{path}"
    
                # Forward request
                async with httpx.AsyncClient() as client:
                    response = await client.request(
                        method=request.method,
                        url=target_url,
                        headers=dict(request.headers),
                        content=await request.body()
                    )
    
                return JSONResponse(
                    status_code=response.status_code,
                    content=response.json()
                )
    
        async def validate_token_cached(self, token: str) -> bool:
            """Validate token with Redis caching"""
            cache_key = f"token:valid:{token}"
    
            # Check cache first
            cached_result = self.redis_client.get(cache_key)
            if cached_result is not None:
                return cached_result.decode() == "1"
    
            # Validate with auth service
            async with httpx.AsyncClient() as client:
                try:
                    response = await client.post(
                        f"{self.services['auth']}/validate",
                        json={"token": token}
                    )
                    is_valid = response.status_code == 200
    
                    # Cache result
                    self.redis_client.setex(
                        cache_key,
                        self.token_cache_ttl,
                        "1" if is_valid else "0"
                    )
    
                    return is_valid
                except Exception:
                    return False
    
        async def check_rate_limit(self, token: str) -> bool:
            """Implement token bucket rate limiting"""
            rate_key = f"rate:{token}"
    
            # Get current count
            current = self.redis_client.get(rate_key)
    
            if current is None:
                # First request, set counter
                self.redis_client.setex(rate_key, 60, "1")
                return True
    
            count = int(current.decode())
            max_requests = 100  # 100 requests per minute
    
            if count >= max_requests:
                return False
    
            # Increment counter
            self.redis_client.incr(rate_key)
            return True
    
    # Initialize gateway
    gateway = APIGateway()
    app = gateway.app
    Python

    Pattern 2: Service Mesh Pattern

    Use Case: Decentralized service-to-service communication

    graph TB
        subgraph "Service Mesh Control Plane"
            A[Service Registry]
            B[Config Management]
            C[Policy Engine]
        end
    
        subgraph "Service Mesh Data Plane"
            D[Service A + Sidecar]
            E[Service B + Sidecar]
            F[Service C + Sidecar]
        end
    
        A --> D
        A --> E
        A --> F
    
        D <--> E
        E <--> F
        D <--> F
    
        style D fill:#e1f5ff
        style E fill:#e1f5ff
        style F fill:#e1f5ff

    Implementation with Istio:

    # JWT Authentication Policy for Istio
    apiVersion: security.istio.io/v1beta1
    kind: RequestAuthentication
    metadata:
      name: jwt-authentication
      namespace: default
    spec:
      selector:
        matchLabels:
          app: user-service
      jwtRules:
      - issuer: "https://your-auth-service.com"
        jwksUri: "https://your-auth-service.com/.well-known/jwks.json"
        audiences:
        - "user-service"
        - "api-gateway"
    ---
    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
      name: require-jwt
      namespace: default
    spec:
      selector:
        matchLabels:
          app: user-service
      action: ALLOW
      rules:
      - from:
        - source:
            requestPrincipals: ["*"]
        to:
        - operation:
            methods: ["GET", "POST"]
            paths: ["/api/users/*"]
        when:
        - key: request.auth.claims[role]
          values: ["admin", "user"]
    YAML

    Pattern 3: CQRS with JWT

    Use Case: Separate read and write operations with different security models

    graph LR
        A[Client] -->|Write Command + JWT| B[Command Service]
        A -->|Read Query + JWT| C[Query Service]
    
        B -->|Write| D[Write Database]
        D -->|Event Stream| E[Event Bus]
        E -->|Update| F[Read Database]
        C -->|Read| F
    
        B -->|Validate Token| G[Auth Service]
        C -->|Validate Token| G

    Implementation:

    from fastapi import FastAPI, Depends, HTTPException
    from typing import List, Optional
    from pydantic import BaseModel
    from datetime import datetime
    import asyncio
    
    # Command Models
    class CreateUserCommand(BaseModel):
        email: str
        full_name: str
        role: str
    
    class UpdateUserCommand(BaseModel):
        user_id: int
        full_name: Optional[str] = None
        role: Optional[str] = None
    
    # Query Models
    class UserQuery(BaseModel):
        user_id: Optional[int] = None
        email: Optional[str] = None
        role: Optional[str] = None
    
    # Command Handler (Write Side)
    class UserCommandHandler:
        def __init__(self, write_db, event_bus):
            self.write_db = write_db
            self.event_bus = event_bus
    
        async def handle_create_user(
            self, 
            command: CreateUserCommand,
            current_user: dict
        ):
            """Handle user creation command"""
            # Require admin role for write operations
            if current_user.get("role") != "admin":
                raise HTTPException(
                    status_code=403,
                    detail="Only admins can create users"
                )
    
            # Write to write database
            user_id = await self.write_db.create_user(
                email=command.email,
                full_name=command.full_name,
                role=command.role
            )
    
            # Publish event
            await self.event_bus.publish("UserCreated", {
                "user_id": user_id,
                "email": command.email,
                "full_name": command.full_name,
                "role": command.role,
                "timestamp": datetime.utcnow().isoformat()
            })
    
            return {"user_id": user_id, "status": "created"}
    
        async def handle_update_user(
            self,
            command: UpdateUserCommand,
            current_user: dict
        ):
            """Handle user update command"""
            # Users can update their own profile, admins can update anyone
            if current_user.get("role") != "admin" and \
               str(current_user.get("sub")) != str(command.user_id):
                raise HTTPException(
                    status_code=403,
                    detail="Insufficient permissions"
                )
    
            # Update write database
            await self.write_db.update_user(
                user_id=command.user_id,
                full_name=command.full_name,
                role=command.role
            )
    
            # Publish event
            await self.event_bus.publish("UserUpdated", {
                "user_id": command.user_id,
                "full_name": command.full_name,
                "role": command.role,
                "updated_by": current_user.get("sub"),
                "timestamp": datetime.utcnow().isoformat()
            })
    
            return {"status": "updated"}
    
    # Query Handler (Read Side)
    class UserQueryHandler:
        def __init__(self, read_db):
            self.read_db = read_db
    
        async def handle_get_user(
            self,
            query: UserQuery,
            current_user: dict
        ):
            """Handle user query - relaxed permissions for reads"""
    
            # Any authenticated user can read
            if query.user_id:
                user = await self.read_db.get_user_by_id(query.user_id)
            elif query.email:
                user = await self.read_db.get_user_by_email(query.email)
            else:
                # Filter by role if specified
                users = await self.read_db.get_users(role=query.role)
                return users
    
            return user
    
        async def handle_search_users(
            self,
            search_term: str,
            current_user: dict,
            limit: int = 10
        ):
            """Handle user search query"""
    
            # Implement search with read-optimized database
            results = await self.read_db.search_users(
                search_term=search_term,
                limit=limit
            )
    
            return results
    
    # Event Bus for synchronizing read and write sides
    class EventBus:
        def __init__(self):
            self.subscribers = {}
    
        def subscribe(self, event_type: str, handler):
            """Subscribe to events"""
            if event_type not in self.subscribers:
                self.subscribers[event_type] = []
            self.subscribers[event_type].append(handler)
    
        async def publish(self, event_type: str, event_data: dict):
            """Publish events to subscribers"""
            if event_type in self.subscribers:
                for handler in self.subscribers[event_type]:
                    await handler(event_data)
    
    # Read Model Updater
    class ReadModelUpdater:
        def __init__(self, read_db):
            self.read_db = read_db
    
        async def handle_user_created(self, event_data: dict):
            """Update read model when user is created"""
            await self.read_db.insert_user(event_data)
    
        async def handle_user_updated(self, event_data: dict):
            """Update read model when user is updated"""
            await self.read_db.update_user_view(event_data)
    
    # FastAPI Application Setup
    app_command = FastAPI(title="Command Service (Write)")
    app_query = FastAPI(title="Query Service (Read)")
    
    # Initialize components
    event_bus = EventBus()
    command_handler = UserCommandHandler(write_db=None, event_bus=event_bus)
    query_handler = UserQueryHandler(read_db=None)
    read_model_updater = ReadModelUpdater(read_db=None)
    
    # Subscribe read model updater to events
    event_bus.subscribe("UserCreated", read_model_updater.handle_user_created)
    event_bus.subscribe("UserUpdated", read_model_updater.handle_user_updated)
    
    # Command Endpoints (Write)
    @app_command.post("/commands/users")
    async def create_user(
        command: CreateUserCommand,
        current_user: dict = Depends(get_current_user_from_jwt)
    ):
        return await command_handler.handle_create_user(command, current_user)
    
    @app_command.put("/commands/users/{user_id}")
    async def update_user(
        user_id: int,
        command: UpdateUserCommand,
        current_user: dict = Depends(get_current_user_from_jwt)
    ):
        command.user_id = user_id
        return await command_handler.handle_update_user(command, current_user)
    
    # Query Endpoints (Read)
    @app_query.get("/queries/users")
    async def get_users(
        query: UserQuery = Depends(),
        current_user: dict = Depends(get_current_user_from_jwt)
    ):
        return await query_handler.handle_get_user(query, current_user)
    
    @app_query.get("/queries/users/search")
    async def search_users(
        q: str,
        limit: int = 10,
        current_user: dict = Depends(get_current_user_from_jwt)
    ):
        return await query_handler.handle_search_users(q, current_user, limit)
    Python

    Pattern 4: Event-Driven Architecture

    Use Case: Asynchronous, loosely coupled services

    sequenceDiagram
        participant Client
        participant Auth
        participant EventBus
        participant AuditService
        participant EmailService
        participant AnalyticsService
    
        Client->>Auth: Login Request
        Auth->>Auth: Validate Credentials
        Auth->>Auth: Generate JWT
        Auth->>EventBus: Publish LoginEvent
        Auth->>Client: Return JWT
    
        par Async Event Processing
            EventBus->>AuditService: LoginEvent
            AuditService->>AuditService: Log to Database
        and
            EventBus->>EmailService: LoginEvent
            EmailService->>EmailService: Send Welcome Email
        and
            EventBus->>AnalyticsService: LoginEvent
            AnalyticsService->>AnalyticsService: Update Metrics
        end

    Implementation with RabbitMQ:

    import pika
    import json
    from datetime import datetime
    from typing import Dict, Any, Callable
    import asyncio
    from functools import wraps
    
    class EventBusService:
        def __init__(self, rabbitmq_url: str = "amqp://localhost"):
            self.connection = pika.BlockingConnection(
                pika.URLParameters(rabbitmq_url)
            )
            self.channel = self.connection.channel()
    
            # Declare exchanges
            self.channel.exchange_declare(
                exchange='auth_events',
                exchange_type='topic',
                durable=True
            )
    
            self.channel.exchange_declare(
                exchange='user_events',
                exchange_type='topic',
                durable=True
            )
    
        def publish_event(
            self,
            exchange: str,
            routing_key: str,
            event_data: Dict[str, Any]
        ):
            """Publish event to message bus"""
    
            message = {
                "event_id": str(uuid.uuid4()),
                "timestamp": datetime.utcnow().isoformat(),
                "data": event_data
            }
    
            self.channel.basic_publish(
                exchange=exchange,
                routing_key=routing_key,
                body=json.dumps(message),
                properties=pika.BasicProperties(
                    delivery_mode=2,  # Persistent
                    content_type='application/json'
                )
            )
    
        def subscribe_to_events(
            self,
            exchange: str,
            routing_key: str,
            callback: Callable
        ):
            """Subscribe to events"""
    
            # Create queue
            result = self.channel.queue_declare(queue='', exclusive=True)
            queue_name = result.method.queue
    
            # Bind queue to exchange
            self.channel.queue_bind(
                exchange=exchange,
                queue=queue_name,
                routing_key=routing_key
            )
    
            # Set up consumer
            self.channel.basic_consume(
                queue=queue_name,
                on_message_callback=callback,
                auto_ack=True
            )
    
        def start_consuming(self):
            """Start consuming messages"""
            self.channel.start_consuming()
    
    # Authentication Service with Events
    class AuthenticationService:
        def __init__(self, event_bus: EventBusService):
            self.event_bus = event_bus
    
        async def login(self, credentials: dict) -> dict:
            """Login user and publish events"""
    
            # Validate credentials
            user = await self.validate_credentials(credentials)
    
            if not user:
                # Publish failed login event
                self.event_bus.publish_event(
                    exchange='auth_events',
                    routing_key='auth.login.failed',
                    event_data={
                        "email": credentials.get("email"),
                        "ip_address": credentials.get("ip_address"),
                        "reason": "Invalid credentials"
                    }
                )
                raise HTTPException(401, "Invalid credentials")
    
            # Generate JWT
            access_token = self.create_access_token(user)
            refresh_token = self.create_refresh_token(user)
    
            # Publish successful login event
            self.event_bus.publish_event(
                exchange='auth_events',
                routing_key='auth.login.success',
                event_data={
                    "user_id": user["id"],
                    "email": user["email"],
                    "ip_address": credentials.get("ip_address"),
                    "user_agent": credentials.get("user_agent"),
                    "timestamp": datetime.utcnow().isoformat()
                }
            )
    
            return {
                "access_token": access_token,
                "refresh_token": refresh_token
            }
    
        async def logout(self, token: str, user_id: int):
            """Logout user and publish event"""
    
            # Blacklist token
            await self.blacklist_token(token)
    
            # Publish logout event
            self.event_bus.publish_event(
                exchange='auth_events',
                routing_key='auth.logout',
                event_data={
                    "user_id": user_id,
                    "timestamp": datetime.utcnow().isoformat()
                }
            )
    
    # Audit Service - Subscribes to auth events
    class AuditService:
        def __init__(self, event_bus: EventBusService, database):
            self.event_bus = event_bus
            self.database = database
    
            # Subscribe to all auth events
            self.event_bus.subscribe_to_events(
                exchange='auth_events',
                routing_key='auth.#',
                callback=self.handle_auth_event
            )
    
        def handle_auth_event(self, ch, method, properties, body):
            """Handle authentication events"""
    
            message = json.loads(body)
            event_data = message["data"]
            routing_key = method.routing_key
    
            # Log to audit database
            self.database.insert_audit_log({
                "event_type": routing_key,
                "event_id": message["event_id"],
                "timestamp": message["timestamp"],
                "data": event_data
            })
    
            # Check for suspicious activity
            if routing_key == 'auth.login.failed':
                self.check_brute_force(event_data)
    
        def check_brute_force(self, event_data: dict):
            """Check for brute force attempts"""
    
            email = event_data.get("email")
    
            # Count failed attempts in last 10 minutes
            failed_count = self.database.count_failed_logins(
                email=email,
                since=datetime.utcnow() - timedelta(minutes=10)
            )
    
            if failed_count >= 5:
                # Publish security alert
                self.event_bus.publish_event(
                    exchange='security_events',
                    routing_key='security.alert.brute_force',
                    event_data={
                        "email": email,
                        "failed_attempts": failed_count,
                        "ip_address": event_data.get("ip_address")
                    }
                )
    
    # Email Service - Subscribes to user events
    class EmailService:
        def __init__(self, event_bus: EventBusService):
            self.event_bus = event_bus
    
            # Subscribe to login events
            self.event_bus.subscribe_to_events(
                exchange='auth_events',
                routing_key='auth.login.success',
                callback=self.handle_login_event
            )
    
        def handle_login_event(self, ch, method, properties, body):
            """Send email on successful login"""
    
            message = json.loads(body)
            event_data = message["data"]
    
            # Send notification email
            self.send_email(
                to=event_data["email"],
                subject="New Login Detected",
                body=f"""
                A new login was detected on your account.
    
                Time: {event_data['timestamp']}
                IP Address: {event_data.get('ip_address', 'Unknown')}
                Device: {event_data.get('user_agent', 'Unknown')}
    
                If this wasn't you, please secure your account immediately.
                """
            )
    Python

    Scalability Patterns

    Horizontal Scaling with Stateless Architecture

    graph TB
        A[Load Balancer] --> B1[App Instance 1]
        A --> B2[App Instance 2]
        A --> B3[App Instance 3]
        A --> B4[App Instance N]
    
        B1 --> C[Shared Redis Cache]
        B2 --> C
        B3 --> C
        B4 --> C
    
        B1 --> D[Database Cluster]
        B2 --> D
        B3 --> D
        B4 --> D
    
        style B1 fill:#e1f5ff
        style B2 fill:#e1f5ff
        style B3 fill:#e1f5ff
        style B4 fill:#e1f5ff

    Docker Compose Configuration:

    version: '3.8'
    
    services:
      # Load Balancer
      nginx:
        image: nginx:latest
        ports:
          - "80:80"
          - "443:443"
        volumes:
          - ./nginx.conf:/etc/nginx/nginx.conf
        depends_on:
          - app1
          - app2
          - app3
        networks:
          - jwt-network
    
      # Application Instances
      app1:
        build: ./app
        environment:
          - REDIS_URL=redis://redis:6379
          - DB_URL=postgresql://postgres:5432/jwtdb
          - INSTANCE_ID=app1
        depends_on:
          - redis
          - postgres
        networks:
          - jwt-network
    
      app2:
        build: ./app
        environment:
          - REDIS_URL=redis://redis:6379
          - DB_URL=postgresql://postgres:5432/jwtdb
          - INSTANCE_ID=app2
        depends_on:
          - redis
          - postgres
        networks:
          - jwt-network
    
      app3:
        build: ./app
        environment:
          - REDIS_URL=redis://redis:6379
          - DB_URL=postgresql://postgres:5432/jwtdb
          - INSTANCE_ID=app3
        depends_on:
          - redis
          - postgres
        networks:
          - jwt-network
    
      # Shared Cache
      redis:
        image: redis:7-alpine
        ports:
          - "6379:6379"
        volumes:
          - redis-data:/data
        networks:
          - jwt-network
    
      # Database Cluster
      postgres:
        image: postgres:15-alpine
        environment:
          - POSTGRES_DB=jwtdb
          - POSTGRES_USER=postgres
          - POSTGRES_PASSWORD=secure_password
        volumes:
          - postgres-data:/var/lib/postgresql/data
        networks:
          - jwt-network
    
      # Message Queue
      rabbitmq:
        image: rabbitmq:3-management
        ports:
          - "5672:5672"
          - "15672:15672"
        environment:
          - RABBITMQ_DEFAULT_USER=admin
          - RABBITMQ_DEFAULT_PASS=secure_password
        networks:
          - jwt-network
    
    volumes:
      redis-data:
      postgres-data:
    
    networks:
      jwt-network:
        driver: bridge
    YAML

    Nginx Load Balancer Configuration:

    upstream app_servers {
        least_conn;  # Use least connections algorithm
    
        server app1:8000 max_fails=3 fail_timeout=30s;
        server app2:8000 max_fails=3 fail_timeout=30s;
        server app3:8000 max_fails=3 fail_timeout=30s;
    
        # Health check
        check interval=3000 rise=2 fall=3 timeout=1000;
    }
    
    # Rate limiting zone
    limit_req_zone $binary_remote_addr zone=auth_limit:10m rate=10r/s;
    limit_req_zone $http_authorization zone=token_limit:10m rate=100r/s;
    
    server {
        listen 80;
        server_name api.example.com;
    
        # Security headers
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header X-XSS-Protection "1; mode=block" always;
        add_header Strict-Transport-Security "max-age=31536000" always;
    
        # Authentication endpoints with strict rate limiting
        location /auth/ {
            limit_req zone=auth_limit burst=5 nodelay;
    
            proxy_pass http://app_servers;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
    
            # Timeout settings
            proxy_connect_timeout 5s;
            proxy_send_timeout 10s;
            proxy_read_timeout 10s;
        }
    
        # Protected API endpoints
        location /api/ {
            limit_req zone=token_limit burst=20 nodelay;
    
            proxy_pass http://app_servers;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
    
            # Caching for GET requests
            proxy_cache api_cache;
            proxy_cache_valid 200 5m;
            proxy_cache_key "$scheme$request_method$host$request_uri$http_authorization";
        }
    
        # Health check endpoint
        location /health {
            access_log off;
            proxy_pass http://app_servers;
        }
    }
    Nginx

    Security Architecture Patterns

    Defense in Depth Strategy

    graph TB
        A[Client] --> B[WAF - Web Application Firewall]
        B --> C[DDoS Protection]
        C --> D[Load Balancer with SSL]
        D --> E[API Gateway - Rate Limiting]
        E --> F[JWT Validation]
        F --> G[Application Security]
        G --> H[Database Encryption]
    
        I[Monitoring & Alerts] -.-> B
        I -.-> C
        I -.-> D
        I -.-> E
        I -.-> F
        I -.-> G
        I -.-> H

    Multi-Layer Security Implementation:

    from fastapi import FastAPI, Request, HTTPException, Depends
    from fastapi.middleware.cors import CORSMiddleware
    from fastapi.middleware.trustedhost import TrustedHostMiddleware
    from slowapi import Limiter, _rate_limit_exceeded_handler
    from slowapi.util import get_remote_address
    from slowapi.errors import RateLimitExceeded
    import hashlib
    import hmac
    from typing import Optional
    from datetime import datetime, timedelta
    
    class SecurityMiddleware:
        """Multi-layer security middleware"""
    
        def __init__(self, app: FastAPI):
            self.app = app
            self.setup_security_layers()
    
        def setup_security_layers(self):
            # Layer 1: CORS Protection
            self.app.add_middleware(
                CORSMiddleware,
                allow_origins=["https://trusted-domain.com"],
                allow_credentials=True,
                allow_methods=["GET", "POST", "PUT", "DELETE"],
                allow_headers=["*"],
                max_age=3600,
            )
    
            # Layer 2: Trusted Host Protection
            self.app.add_middleware(
                TrustedHostMiddleware,
                allowed_hosts=["api.example.com", "*.example.com"]
            )
    
            # Layer 3: Rate Limiting
            limiter = Limiter(key_func=get_remote_address)
            self.app.state.limiter = limiter
            self.app.add_exception_handler(
                RateLimitExceeded,
                _rate_limit_exceeded_handler
            )
    
            # Layer 4: Request Signature Verification
            @self.app.middleware("http")
            async def verify_request_signature(request: Request, call_next):
                if request.url.path.startswith("/api/critical/"):
                    if not self.verify_signature(request):
                        return JSONResponse(
                            status_code=401,
                            content={"detail": "Invalid request signature"}
                        )
    
                response = await call_next(request)
                return response
    
            # Layer 5: JWT Token Validation
            @self.app.middleware("http")
            async def validate_jwt_middleware(request: Request, call_next):
                if request.url.path.startswith("/api/"):
                    token = self.extract_token(request)
                    if token and not self.is_token_blacklisted(token):
                        payload = self.verify_jwt(token)
                        if payload:
                            request.state.user = payload
                        else:
                            return JSONResponse(
                                status_code=401,
                                content={"detail": "Invalid token"}
                            )
    
                response = await call_next(request)
                return response
    
            # Layer 6: Audit Logging
            @self.app.middleware("http")
            async def audit_logging_middleware(request: Request, call_next):
                start_time = datetime.utcnow()
    
                response = await call_next(request)
    
                # Log request
                self.log_request(
                    path=request.url.path,
                    method=request.method,
                    user=getattr(request.state, "user", None),
                    status_code=response.status_code,
                    duration=(datetime.utcnow() - start_time).total_seconds()
                )
    
                return response
    
        def verify_signature(self, request: Request) -> bool:
            """Verify HMAC signature of request"""
            signature = request.headers.get("X-Signature")
            timestamp = request.headers.get("X-Timestamp")
    
            if not signature or not timestamp:
                return False
    
            # Check timestamp (prevent replay attacks)
            try:
                request_time = datetime.fromisoformat(timestamp)
                if abs((datetime.utcnow() - request_time).total_seconds()) > 300:
                    return False
            except:
                return False
    
            # Verify signature
            expected_signature = hmac.new(
                SIGNATURE_SECRET.encode(),
                f"{request.method}{request.url.path}{timestamp}".encode(),
                hashlib.sha256
            ).hexdigest()
    
            return hmac.compare_digest(signature, expected_signature)
    
    # Initialize app with security layers
    app = FastAPI()
    security = SecurityMiddleware(app)
    Python

    High Availability Patterns

    Active-Active Deployment

    graph TB
        subgraph "Region 1 - US East"
            A1[Load Balancer] --> B1[App Cluster]
            B1 --> C1[DB Primary]
            B1 --> D1[Redis Cluster]
        end
    
        subgraph "Region 2 - US West"
            A2[Load Balancer] --> B2[App Cluster]
            B2 --> C2[DB Primary]
            B2 --> D2[Redis Cluster]
        end
    
        E[Global Load Balancer] --> A1
        E --> A2
    
        C1 <-.Replication.-> C2
        D1 <-.Sync.-> D2

    Kubernetes Deployment Configuration:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: jwt-auth-service
      labels:
        app: jwt-auth
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: jwt-auth
      template:
        metadata:
          labels:
            app: jwt-auth
        spec:
          affinity:
            podAntiAffinity:
              preferredDuringSchedulingIgnoredDuringExecution:
              - weight: 100
                podAffinityTerm:
                  labelSelector:
                    matchExpressions:
                    - key: app
                      operator: In
                      values:
                      - jwt-auth
                  topologyKey: kubernetes.io/hostname
    
          containers:
          - name: auth-service
            image: your-registry/jwt-auth:latest
            ports:
            - containerPort: 8000
    
            env:
            - name: REDIS_URL
              valueFrom:
                configMapKeyRef:
                  name: app-config
                  key: redis.url
            - name: DB_URL
              valueFrom:
                secretKeyRef:
                  name: app-secrets
                  key: database.url
            - name: JWT_SECRET
              valueFrom:
                secretKeyRef:
                  name: app-secrets
                  key: jwt.secret
    
            resources:
              requests:
                memory: "256Mi"
                cpu: "250m"
              limits:
                memory: "512Mi"
                cpu: "500m"
    
            livenessProbe:
              httpGet:
                path: /health
                port: 8000
              initialDelaySeconds: 30
              periodSeconds: 10
              timeoutSeconds: 5
              failureThreshold: 3
    
            readinessProbe:
              httpGet:
                path: /ready
                port: 8000
              initialDelaySeconds: 10
              periodSeconds: 5
              timeoutSeconds: 3
              failureThreshold: 2
    
            # Graceful shutdown
            lifecycle:
              preStop:
                exec:
                  command: ["/bin/sh", "-c", "sleep 15"]
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: jwt-auth-service
    spec:
      type: LoadBalancer
      selector:
        app: jwt-auth
      ports:
      - protocol: TCP
        port: 80
        targetPort: 8000
      sessionAffinity: None
    ---
    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    metadata:
      name: jwt-auth-hpa
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: jwt-auth-service
      minReplicas: 3
      maxReplicas: 10
      metrics:
      - type: Resource
        resource:
          name: cpu
          target:
            type: Utilization
            averageUtilization: 70
      - type: Resource
        resource:
          name: memory
          target:
            type: Utilization
            averageUtilization: 80
      behavior:
        scaleDown:
          stabilizationWindowSeconds: 300
          policies:
          - type: Percent
            value: 50
            periodSeconds: 60
        scaleUp:
          stabilizationWindowSeconds: 0
          policies:
          - type: Percent
            value: 100
            periodSeconds: 15
          - type: Pods
            value: 2
            periodSeconds: 60
          selectPolicy: Max
    YAML

    Monitoring and Observability Architecture

    graph TB
        subgraph "Application Layer"
            A[Auth Service]
            B[User Service]
            C[API Gateway]
        end
    
        subgraph "Observability Stack"
            D[Metrics Collector - Prometheus]
            E[Log Aggregator - Loki]
            F[Tracing - Jaeger]
        end
    
        subgraph "Visualization & Alerting"
            G[Grafana Dashboards]
            H[Alert Manager]
        end
    
        A --> D
        A --> E
        A --> F
        B --> D
        B --> E
        B --> F
        C --> D
        C --> E
        C --> F
    
        D --> G
        E --> G
        F --> G
    
        D --> H
        E --> H

    Prometheus Metrics Implementation:

    from prometheus_client import Counter, Histogram, Gauge, generate_latest
    from fastapi import FastAPI, Response
    import time
    from functools import wraps
    
    # Define metrics
    jwt_tokens_generated = Counter(
        'jwt_tokens_generated_total',
        'Total number of JWT tokens generated',
        ['token_type']
    )
    
    jwt_tokens_validated = Counter(
        'jwt_tokens_validated_total',
        'Total number of JWT tokens validated',
        ['status']
    )
    
    jwt_validation_duration = Histogram(
        'jwt_validation_duration_seconds',
        'Time spent validating JWT tokens',
        buckets=[0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0]
    )
    
    active_sessions = Gauge(
        'active_sessions',
        'Number of active user sessions'
    )
    
    failed_login_attempts = Counter(
        'failed_login_attempts_total',
        'Total number of failed login attempts',
        ['reason']
    )
    
    # Instrumented JWT Handler
    class InstrumentedJWTHandler:
        def create_access_token(self, data: dict) -> str:
            """Create access token with metrics"""
            start_time = time.time()
    
            try:
                token = self._generate_token(data, "access")
                jwt_tokens_generated.labels(token_type='access').inc()
                active_sessions.inc()
                return token
            finally:
                duration = time.time() - start_time
                jwt_validation_duration.observe(duration)
    
        def verify_token(self, token: str) -> Optional[dict]:
            """Verify token with metrics"""
            start_time = time.time()
    
            try:
                payload = self._verify_token_internal(token)
    
                if payload:
                    jwt_tokens_validated.labels(status='valid').inc()
                else:
                    jwt_tokens_validated.labels(status='invalid').inc()
    
                return payload
            finally:
                duration = time.time() - start_time
                jwt_validation_duration.observe(duration)
    
        def handle_failed_login(self, reason: str):
            """Track failed login attempts"""
            failed_login_attempts.labels(reason=reason).inc()
    
    # Metrics endpoint
    @app.get("/metrics")
    async def metrics():
        return Response(
            content=generate_latest(),
            media_type="text/plain"
        )
    Python

    Best Practices Summary

    Architectural Best Practices

    1. Stateless Design: Keep application servers stateless for easy scaling
    2. Separation of Concerns: Divide responsibilities clearly across services
    3. Idempotency: Design operations to be safely retryable
    4. Circuit Breakers: Prevent cascading failures
    5. Health Checks: Implement comprehensive health monitoring
    6. Graceful Degradation: Handle failures gracefully
    7. Caching Strategy: Cache aggressively where appropriate
    8. Event-Driven: Use asynchronous communication for non-critical operations

    Security Best Practices

    1. Defense in Depth: Implement multiple security layers
    2. Least Privilege: Grant minimum necessary permissions
    3. Token Rotation: Regularly rotate cryptographic keys
    4. Audit Everything: Comprehensive logging and monitoring
    5. Secure Communication: Always use TLS/SSL
    6. Input Validation: Validate all inputs rigorously
    7. Rate Limiting: Protect against abuse
    8. Secrets Management: Use secure secret storage (Vault, AWS Secrets Manager)

    Scalability Best Practices

    1. Horizontal Scaling: Design for adding more instances
    2. Database Optimization: Use connection pooling, read replicas
    3. Caching Layers: Implement multi-tier caching
    4. Asynchronous Processing: Use message queues for heavy operations
    5. Content Delivery: Use CDNs for static content
    6. Load Testing: Regularly test system limits
    7. Auto-Scaling: Implement automatic scaling policies
    8. Performance Monitoring: Continuous performance tracking

    25. Summary and Best Practices {#25-summary-and-best-practices}

    Overview

    This comprehensive guide has covered JWT authentication from fundamentals to enterprise architecture. This chapter consolidates the key learnings, best practices, and critical security considerations into actionable guidelines.

    Core Concepts Recap

    What We’ve Learned

    mindmap
      root((JWT Mastery))
        Fundamentals
          Structure (Header.Payload.Signature)
          Claims & Payload
          Cryptographic Algorithms
          Token Lifecycle
        Implementation
          FastAPI Backend
          JavaScript Frontend
          Token Storage
          RBAC Authorization
        Security
          JWE Encryption
          OAuth 2.0 Integration
          Key Rotation
          Token Revocation
        Production
          Deployment Strategies
          Monitoring & Logging
          Performance Optimization
          Testing QA
        Enterprise
          Microservices
          System Design
          Architecture Patterns
          Future Standards

    Security Best Practices Checklist

    ✅ Critical Security Requirements

    1. Algorithm Security

    # ✅ DO: Use strong algorithms
    ALGORITHM = "RS256"  # or ES256
    KEY_SIZE = 2048  # minimum
    
    # ❌ DON'T: Use weak algorithms
    ALGORITHM = "none"  # NEVER!
    ALGORITHM = "HS256"  # Only for single service
    Python

    2. Token Expiration

    # ✅ DO: Set appropriate expiration times
    ACCESS_TOKEN_EXPIRE_MINUTES = 15  # Short-lived
    REFRESH_TOKEN_EXPIRE_DAYS = 7     # Longer-lived
    
    # ❌ DON'T: Use excessively long expiration
    ACCESS_TOKEN_EXPIRE_MINUTES = 43200  # 30 days - Too long!
    Python

    3. Secret Key Management

    # ✅ DO: Use strong, random secrets
    SECRET_KEY = secrets.token_urlsafe(32)
    
    # ❌ DON'T: Use weak or hardcoded secrets
    SECRET_KEY = "mysecret"  # Too weak!
    Python

    4. Token Validation

    # ✅ DO: Validate all claims
    payload = jwt.decode(
        token,
        public_key,
        algorithms=["RS256"],
        audience="your-api",
        issuer="your-auth-server",
        options={
            "verify_exp": True,
            "verify_aud": True,
            "verify_iss": True
        }
    )
    
    # ❌ DON'T: Skip validation
    payload = jwt.decode(
        token,
        options={"verify_signature": False}  # Dangerous!
    )
    Python

    5. Secure Storage

    // ✅ DO: Use HttpOnly cookies for sensitive tokens
    document.cookie = `refresh_token=${token}; HttpOnly; Secure; SameSite=Strict`;
    
    // ❌ DON'T: Store sensitive tokens in localStorage
    localStorage.setItem('refresh_token', token);  // Vulnerable to XSS
    JavaScript

    Architecture Decision Matrix

    Choose the right architecture based on your requirements:

    RequirementPatternWhen to UseChapter Reference
    Single service appHS256Simple apps, single backendChapter 4
    MicroservicesRS256/ES256Distributed systemsChapter 20
    High securityJWE + RS256Financial, healthcareChapter 11
    Third-party authOAuth 2.0Social login, SSOChapter 12
    Multi-tenantTenant isolationSaaS platformsExtended Patterns
    Legacy migrationStrangler FigGradual migrationExtended Patterns
    Mobile appsToken bindingNative appsExtended Patterns
    Zero trustContinuous authEnterprise securityExtended Patterns

    Performance Optimization Guidelines

    Token Size Optimization

    Problem: Large tokens increase bandwidth and latency

    Solutions:

    1. Minimize Claims
    // ✅ Good: Essential claims only (≈200 bytes)
    {
    "sub": "123",
    "role": "user",
    "exp": 1697648400
    }
    
    
    // ❌ Bad: Excessive data (≈2000 bytes)
    {
      "sub": "123",
      "user_profile": { /* large object */ },
      "preferences": { /* large object */ },
      "history": [ /* large array */ ]
    }
    
    JSON

    2. Use Short Claim Names

    // ✅ Optimized
    {"sub": "123", "r": "admin", "p": ["read", "write"]}
    
    // ❌ Verbose
    {"subject": "123", "role": "admin", "permissions": ["read", "write"]}
    JSON
    1. Reference Pattern
    // ✅ Reference to data
    {"sub": "123", "profile_id": "abc"}
    
    // ❌ Embed full data
    {"sub": "123", "profile": { /* 1KB of data */ }}
    JSON

    Caching Strategy

    # Redis caching for token validation
    class CachedTokenValidator:
        def __init__(self):
            self.redis = redis.Redis()
            self.cache_ttl = 300  # 5 minutes
    
        async def validate_token(self, token: str) -> dict:
            # Check cache first
            cache_key = f"token:{hashlib.sha256(token.encode()).hexdigest()}"
            cached_result = self.redis.get(cache_key)
    
            if cached_result:
                return json.loads(cached_result)
    
            # Validate token
            payload = jwt.decode(token, public_key, algorithms=["RS256"])
    
            # Cache result
            self.redis.setex(
                cache_key,
                self.cache_ttl,
                json.dumps(payload)
            )
    
            return payload
    Python

    Connection Pooling

    # Database connection pooling
    from sqlalchemy import create_engine
    from sqlalchemy.pool import QueuePool
    
    engine = create_engine(
        DATABASE_URL,
        poolclass=QueuePool,
        pool_size=20,
        max_overflow=40,
        pool_pre_ping=True,
        pool_recycle=3600
    )
    Python

    Testing Strategy

    Unit Tests

    # Test token creation
    def test_create_token():
        handler = JWTHandler()
        payload = {"sub": "user123", "role": "admin"}
        token = handler.create_access_token(payload)
    
        assert token is not None
        decoded = handler.verify_token(token)
        assert decoded["sub"] == "user123"
        assert decoded["role"] == "admin"
    
    # Test token expiration
    def test_expired_token():
        handler = JWTHandler()
        payload = {"sub": "user123", "exp": datetime.utcnow() - timedelta(hours=1)}
        token = jwt.encode(payload, handler.secret_key, algorithm="RS256")
    
        decoded = handler.verify_token(token)
        assert decoded is None
    
    # Test signature verification
    def test_invalid_signature():
        handler = JWTHandler()
        token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.invalid.signature"
    
        decoded = handler.verify_token(token)
        assert decoded is None
    Python

    Integration Tests

    # Test authentication flow
    async def test_authentication_flow():
        # Register
        response = await client.post("/auth/register", json={
            "email": "test@example.com",
            "password": "password123",
            "full_name": "Test User"
        })
        assert response.status_code == 201
    
        # Login
        response = await client.post("/auth/login", data={
            "username": "test@example.com",
            "password": "password123"
        })
        assert response.status_code == 200
        tokens = response.json()
    
        # Access protected route
        response = await client.get(
            "/users/profile",
            headers={"Authorization": f"Bearer {tokens['access_token']}"}
        )
        assert response.status_code == 200
    
        # Refresh token
        response = await client.post("/auth/refresh", json={
            "refresh_token": tokens['refresh_token']
        })
        assert response.status_code == 200
    Python

    Load Testing

    # Locust load test
    from locust import HttpUser, task, between
    
    class JWTAuthUser(HttpUser):
        wait_time = between(1, 3)
    
        def on_start(self):
            # Login and get token
            response = self.client.post("/auth/login", data={
                "username": "test@example.com",
                "password": "password123"
            })
            self.token = response.json()["access_token"]
    
        @task(3)
        def get_profile(self):
            self.client.get(
                "/users/profile",
                headers={"Authorization": f"Bearer {self.token}"}
            )
    
        @task(1)
        def get_protected_data(self):
            self.client.get(
                "/users/protected-data",
                headers={"Authorization": f"Bearer {self.token}"}
            )
    Python

    Monitoring and Observability

    Key Metrics to Track

    from prometheus_client import Counter, Histogram, Gauge
    
    # Token metrics
    token_created = Counter('jwt_tokens_created_total', 'Total tokens created')
    token_validated = Counter('jwt_tokens_validated_total', 'Total tokens validated')
    token_expired = Counter('jwt_tokens_expired_total', 'Total expired tokens')
    token_invalid = Counter('jwt_tokens_invalid_total', 'Total invalid tokens')
    
    # Performance metrics
    token_validation_duration = Histogram(
        'jwt_validation_duration_seconds',
        'Token validation duration'
    )
    active_sessions = Gauge('jwt_active_sessions', 'Number of active sessions')
    
    # Implementation
    @token_validation_duration.time()
    async def validate_token(token: str):
        try:
            payload = jwt.decode(token, public_key, algorithms=["RS256"])
            token_validated.inc()
            return payload
        except jwt.ExpiredSignatureError:
            token_expired.inc()
            return None
        except jwt.InvalidTokenError:
            token_invalid.inc()
            return None
    Python

    Logging Best Practices

    import logging
    import json
    
    # Structured logging
    logger = logging.getLogger(__name__)
    
    def log_authentication_event(event_type: str, user_id: str, **kwargs):
        log_entry = {
            "timestamp": datetime.utcnow().isoformat(),
            "event_type": event_type,
            "user_id": user_id,
            "ip_address": kwargs.get("ip_address"),
            "user_agent": kwargs.get("user_agent"),
            "success": kwargs.get("success", True),
            "error": kwargs.get("error")
        }
    
        if log_entry["success"]:
            logger.info(json.dumps(log_entry))
        else:
            logger.warning(json.dumps(log_entry))
    
    # Usage
    log_authentication_event(
        "login",
        user_id="123",
        ip_address="192.168.1.1",
        user_agent="Mozilla/5.0...",
        success=True
    )
    Python

    Common Pitfalls and Solutions

    1. Clock Skew Issues

    Problem: Token validation fails due to time differences between servers

    Solution:

    # Add clock skew tolerance
    payload = jwt.decode(
        token,
        public_key,
        algorithms=["RS256"],
        options={"verify_exp": True},
        leeway=timedelta(seconds=10)  # Allow 10 seconds skew
    )
    Python

    2. Token Size in URLs

    Problem: Tokens in URL query parameters get truncated

    Solution:

    // ❌ DON'T: Put tokens in URLs
    fetch(`/api/data?token=${token}`);
    
    // ✅ DO: Use Authorization header
    fetch('/api/data', {
        headers: {
            'Authorization': `Bearer ${token}`
        }
    });
    JavaScript

    3. Missing Token Revocation

    Problem: Logout doesn’t actually invalidate tokens

    Solution:

    # Implement token blacklist
    class TokenBlacklist:
        def __init__(self):
            self.redis = redis.Redis()
    
        async def revoke_token(self, token: str):
            # Decode to get expiration
            payload = jwt.decode(token, options={"verify_signature": False})
            exp = payload.get("exp")
    
            if exp:
                ttl = exp - int(datetime.utcnow().timestamp())
                if ttl > 0:
                    token_hash = hashlib.sha256(token.encode()).hexdigest()
                    self.redis.setex(f"blacklist:{token_hash}", ttl, "1")
    
        async def is_revoked(self, token: str) -> bool:
            token_hash = hashlib.sha256(token.encode()).hexdigest()
            return self.redis.exists(f"blacklist:{token_hash}") > 0
    Python

    4. CORS Configuration

    Problem: Frontend can’t access API due to CORS errors

    Solution:

    # Proper CORS configuration
    from fastapi.middleware.cors import CORSMiddleware
    
    app.add_middleware(
        CORSMiddleware,
        allow_origins=[
            "https://app.example.com",
            "https://admin.example.com"
        ],
        allow_credentials=True,
        allow_methods=["GET", "POST", "PUT", "DELETE"],
        allow_headers=["Authorization", "Content-Type"],
        expose_headers=["X-Total-Count"],
        max_age=3600
    )
    Python

    5. Refresh Token Rotation

    Problem: Refresh tokens never expire, security risk

    Solution:

    # Implement refresh token rotation
    async def refresh_access_token(refresh_token: str):
        # Validate refresh token
        payload = verify_token(refresh_token)
        if not payload or payload.get("type") != "refresh":
            raise HTTPException(status_code=401)
    
        # Create new access token
        new_access_token = create_access_token({
            "sub": payload["sub"],
            "role": payload["role"]
        })
    
        # Create new refresh token (rotation)
        new_refresh_token = create_refresh_token({
            "sub": payload["sub"]
        })
    
        # Revoke old refresh token
        await blacklist_token(refresh_token)
    
        return {
            "access_token": new_access_token,
            "refresh_token": new_refresh_token
        }
    Python

    Production Deployment Checklist

    Pre-Deployment

    • Security Audit
      • Review all algorithms and key sizes
      • Verify secret key strength (min 256 bits)
      • Test token expiration policies
      • Validate all input parameters
      • Enable HTTPS/TLS everywhere
    • Performance Testing
      • Load test authentication endpoints
      • Measure token validation latency
      • Test under concurrent users
      • Verify database connection pooling
      • Check Redis/cache performance
    • Infrastructure
      • Set up key rotation mechanism
      • Configure backup and recovery
      • Enable monitoring and alerting
      • Set up log aggregation
      • Configure auto-scaling

    Post-Deployment

    • Monitoring
      • Track token creation/validation rates
      • Monitor error rates
      • Watch for security anomalies
      • Track response times
      • Monitor cache hit rates
    • Security
      • Enable rate limiting
      • Set up intrusion detection
      • Monitor failed login attempts
      • Review audit logs
      • Scan for vulnerabilities

    Future-Proofing Your JWT Implementation

    Stay Updated with Standards

    Current Standards:

    • RFC 7519: JWT
    • RFC 7515: JWS
    • RFC 7516: JWE
    • RFC 7517: JWK
    • RFC 7518: JWA
    • RFC 8693: Token Exchange
    • RFC 7662: Token Introspection

    Emerging Standards:

    • DPoP (RFC 9449): Demonstrating Proof-of-Possession
    • SD-JWT: Selective Disclosure JWT
    • WebAuthn: Passwordless authentication
    • FIDO2: Strong authentication

    Adopt Modern Practices

    # 1. Implement DPoP for enhanced security
    class DPoPValidator:
        async def validate_dpop_proof(
            self,
            dpop_proof: str,
            http_method: str,
            http_uri: str
        ) -> bool:
            # Validate DPoP proof
            try:
                payload = jwt.decode(dpop_proof, options={"verify_signature": True})
    
                # Verify HTTP method and URI
                if payload.get("htm") != http_method:
                    return False
                if payload.get("htu") != http_uri:
                    return False
    
                # Verify timestamp
                iat = payload.get("iat")
                if abs(datetime.utcnow().timestamp() - iat) > 60:
                    return False
    
                return True
            except:
                return False
    
    # 2. Implement Selective Disclosure
    class SelectiveDisclosureJWT:
        def create_sd_jwt(self, claims: dict, disclosable_claims: list) -> str:
            # Create SD-JWT with selective disclosure
            sd_claims = {}
            disclosure_map = {}
    
            for claim in disclosable_claims:
                if claim in claims:
                    salt = secrets.token_urlsafe(16)
                    disclosure = f"{salt}.{claim}.{json.dumps(claims[claim])}"
                    disclosure_hash = hashlib.sha256(disclosure.encode()).hexdigest()
    
                    sd_claims[f"_sd_{claim}"] = disclosure_hash
                    disclosure_map[claim] = disclosure
    
            # Create JWT with hashed claims
            token = jwt.encode(sd_claims, private_key, algorithm="RS256")
    
            return token, disclosure_map
    Python

    Learning Path Summary

    Beginner → Expert Journey

    Phase 1: Foundations (Weeks 1-2)

    • ✅ Understand JWT structure
    • ✅ Learn basic authentication flow
    • ✅ Implement simple FastAPI backend
    • ✅ Build basic frontend integration

    Phase 2: Intermediate (Weeks 3-4)

    • ✅ Implement RBAC
    • ✅ Add token refresh mechanism
    • ✅ Secure token storage
    • ✅ Error handling and validation

    Phase 3: Advanced (Weeks 5-6)

    • ✅ JWE encryption
    • ✅ OAuth 2.0 integration
    • ✅ Key rotation
    • ✅ Token revocation

    Phase 4: Production (Weeks 7-8)

    • ✅ Deployment strategies
    • ✅ Monitoring and logging
    • ✅ Performance optimization
    • ✅ Comprehensive testing

    Phase 5: Enterprise (Weeks 9-12)

    • ✅ Microservices architecture
    • ✅ System design patterns
    • ✅ Zero trust implementation
    • ✅ Future standards adoption

    Quick Reference Cards

    1. Token Creation

    # FastAPI Token Creation
    def create_token(user_id: str, role: str) -> str:
        payload = {
            "sub": user_id,
            "role": role,
            "iat": datetime.utcnow(),
            "exp": datetime.utcnow() + timedelta(minutes=15)
        }
        return jwt.encode(payload, private_key, algorithm="RS256")
    Python

    2. Token Validation

    # FastAPI Token Validation
    def validate_token(token: str) -> dict:
        try:
            return jwt.decode(
                token,
                public_key,
                algorithms=["RS256"],
                options={"verify_exp": True}
            )
        except jwt.InvalidTokenError:
            raise HTTPException(status_code=401)
    Python

    3. Frontend Integration

    // JavaScript Token Usage
    class AuthClient {
        async login(email, password) {
            const response = await fetch('/auth/login', {
                method: 'POST',
                body: new FormData({username: email, password})
            });
            const {access_token} = await response.json();
            localStorage.setItem('token', access_token);
        }
    
        async fetchProtected(url) {
            return fetch(url, {
                headers: {
                    'Authorization': `Bearer ${localStorage.getItem('token')}`
                }
            });
        }
    }
    JavaScript

    Final Recommendations

    Do’s ✅

    1. Always use HTTPS in production
    2. Implement proper token expiration
    3. Use strong cryptographic algorithms (RS256, ES256)
    4. Validate all token claims
    5. Implement refresh token rotation
    6. Monitor and log authentication events
    7. Use HttpOnly cookies for refresh tokens
    8. Implement rate limiting
    9. Regular security audits
    10. Keep dependencies updated

    Don’ts ❌

    1. Never use the “none” algorithm
    2. Don’t store sensitive data in tokens
    3. Don’t use weak secrets
    4. Don’t skip signature verification
    5. Don’t trust client-side validation alone
    6. Don’t use excessively long expiration times
    7. Don’t expose tokens in URLs
    8. Don’t forget to implement logout
    9. Don’t ignore error handling
    10. Don’t deploy without testing

    Conclusion

    This comprehensive guide has equipped you with the knowledge and tools to implement JWT authentication from basic concepts to enterprise-grade systems. Key achievements:

    🎯 Technical Mastery

    • Deep understanding of JWT structure and cryptography
    • Production-ready FastAPI and JavaScript implementations
    • Advanced security patterns and best practices
    • Enterprise architecture and system design

    🔒 Security Excellence

    • Zero-trust architecture patterns
    • Token binding and DPoP
    • Multi-factor authentication
    • Comprehensive threat mitigation

    Performance & Scale

    • Microservices integration
    • Caching and optimization
    • Load balancing strategies
    • High-availability patterns

    🚀 Future Ready

    • Emerging standards (SD-JWT, DPoP, WebAuthn)
    • Cloud-native deployments
    • Kubernetes orchestration
    • GitOps workflows

    Next Steps

    1. Practice: Implement the examples in your projects
    2. Experiment: Try different architectural patterns
    3. Contribute: Share your learnings with the community
    4. Stay Updated: Follow JWT and OAuth standards evolution
    5. Secure: Regular security audits and updates

    Additional Resources

    Official Specifications:

    Security Resources:

    • OWASP JWT Security Cheat Sheet
    • NIST Cryptographic Standards
    • CWE Top 25 Security Weaknesses

    Community:

    • FastAPI Discord Community
    • JWT.io Slack Channel
    • Stack Overflow JWT Tag

    Thank you for completing this comprehensive JWT journey! 🎉

    You now have the expertise to build secure, scalable, and production-ready authentication systems. Remember: security is a continuous process, not a destination. Keep learning, stay vigilant, and build amazing applications!


    End of Guide


    Discover more from Altgr Blog

    Subscribe to get the latest posts sent to your email.

    Leave a Reply

    Your email address will not be published. Required fields are marked *