Skip to content

Latest commit

 

History

History
299 lines (217 loc) · 7.73 KB

File metadata and controls

299 lines (217 loc) · 7.73 KB

Performance Optimization: Authentication Caching

🔥 The Problem

Current behavior:

  • Every API request checks database for user authentication
  • 1000 users × 10 requests/min = 10,000 database queries/minute just for auth
  • This doesn't scale!

✅ Solution 1: JWT Tokens (RECOMMENDED)

How It Works

  1. User logs in once → Database query to verify credentials
  2. Server returns JWT token → Contains user info + role
  3. User sends token with every request → No database query needed!
  4. Server validates token signature → Cryptographically secure, no DB

Benefits

  • 99.8% fewer database queries for authentication
  • Stateless - scales horizontally
  • Faster - JWT validation is microseconds vs milliseconds for DB
  • Already implemented in your API!

Implementation

Step 1: User Login (once per session)

# Login request (1 database query)
curl -X POST http://your-api?action=login \
  -d "username=john&password=SecurePass123!"

# Response:
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqb2huIiwicm9sZSI6InJlYWRvbmx5IiwiaWF0IjoxNjk4MTIzNDU2LCJleHAiOjE2OTgxMjcwNTZ9.abcd1234..."
}

Step 2: Use Token for All Requests (0 database queries)

# All subsequent requests use the token
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
     http://your-api?action=list&table=posts

# No database authentication query!
# JWT is validated in memory (microseconds)

Configuration

Update config/api.php:

'auth_method' => 'jwt',  // Change from 'basic' to 'jwt'
'jwt_secret' => 'YourSuperSecretKeyChangeMe123!',
'jwt_expiration' => 3600,  // 1 hour (adjust as needed)

Performance Comparison

Scenario Basic Auth JWT Auth Improvement
1 user, 10 req/min 10 DB queries 0.17 DB queries 98% faster
100 users, 10 req/min 1,000 DB queries 1.67 DB queries 99.8% faster
1,000 users, 10 req/min 10,000 DB queries 16.7 DB queries 99.8% faster

Assumes token refresh every hour


✅ Solution 2: Session Caching (Quick Fix)

If you want to keep Basic Auth but improve performance:

Implementation

Add caching to Authenticator:

private function authenticateFromDatabase(string $username, string $password): bool
{
    // Check session cache first
    $cacheKey = "auth_" . md5($username . $password);
    
    if (!empty($_SESSION[$cacheKey]) && $_SESSION[$cacheKey]['expires'] > time()) {
        $this->currentUser = $_SESSION[$cacheKey]['user'];
        return true; // Cache hit - no database query!
    }
    
    // Cache miss - query database
    if (!$this->pdo) {
        return false;
    }
    
    try {
        $stmt = $this->pdo->prepare(
            "SELECT id, username, email, password_hash, role, active 
             FROM api_users 
             WHERE username = :username AND active = 1"
        );
        $stmt->execute(['username' => $username]);
        $user = $stmt->fetch(\PDO::FETCH_ASSOC);
        
        if (!$user || !password_verify($password, $user['password_hash'])) {
            return false;
        }
        
        // Store in session cache (5 minutes)
        $_SESSION[$cacheKey] = [
            'user' => [
                'id' => $user['id'],
                'username' => $user['username'],
                'email' => $user['email'],
                'role' => $user['role']
            ],
            'expires' => time() + 300  // 5 minutes
        ];
        
        $this->currentUser = $_SESSION[$cacheKey]['user'];
        return true;
        
    } catch (\PDOException $e) {
        return false;
    }
}

Performance

  • First request: Database query
  • Next 5 minutes: Cached (no queries)
  • After 5 minutes: Database query again

Result: 10,000 queries/min → ~2,000 queries/min (80% reduction)


✅ Solution 3: Redis/Memcached (Enterprise)

Cache all API keys in Redis for instant lookup:

// On server start or periodic refresh
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

// Load all users into Redis (runs every 5 minutes)
$stmt = $pdo->query("SELECT username, password_hash, role FROM api_users WHERE active = 1");
while ($user = $stmt->fetch()) {
    $redis->setex("user:{$user['username']}", 300, json_encode($user));
}

// Authentication lookup (no database!)
$userData = $redis->get("user:$username");
if ($userData && password_verify($password, $userData['password_hash'])) {
    return true;
}

📊 Comparison Table

Method DB Queries/Min (1000 users) Setup Time Scalability Security
Current (Basic Auth) 10,000 Done Poor Good
Session Cache ~2,000 10 min Okay Good
JWT (Recommended) ~17 15 min Excellent Excellent
Redis Cache ~33 1-2 hours Excellent Good

🎯 Recommended Implementation: JWT

Why JWT?

  1. Already implemented in your API
  2. Industry standard (used by Google, GitHub, etc.)
  3. Best performance (99.8% fewer queries)
  4. Stateless (scales to millions of users)
  5. Secure (cryptographically signed)

How To Switch (5 minutes)

1. Update config/api.php:

'auth_method' => 'jwt',  // Change this line

2. Update login endpoint:

Already exists! Your Router has JWT login at:

POST /api.php?action=login
Body: username=john&password=SecurePass123!

3. Client workflow:

// Step 1: Login once
const loginResponse = await fetch('http://api.com?action=login', {
    method: 'POST',
    body: new URLSearchParams({
        username: 'john',
        password: 'SecurePass123!'
    })
});
const { token } = await loginResponse.json();

// Step 2: Save token
localStorage.setItem('jwt_token', token);

// Step 3: Use token for all requests
const apiResponse = await fetch('http://api.com?action=list&table=posts', {
    headers: {
        'Authorization': `Bearer ${token}`
    }
});

4. That's it! No more database queries for authentication.


🔒 Security Notes

JWT Best Practices

  1. Short expiration (1 hour recommended)
  2. HTTPS only (prevent token interception)
  3. Refresh tokens (for longer sessions)
  4. Token blacklist (for logout/revocation)

Implementation

// config/api.php
'jwt_expiration' => 3600,  // 1 hour
'jwt_refresh_expiration' => 604800,  // 1 week (for refresh tokens)

📈 Real-World Example

Scenario: 1,000 users, each making 10 requests per minute

Before (Basic Auth + Database)

  • Auth queries: 10,000/minute
  • API queries: 10,000/minute
  • Total: 20,000/minute
  • Database CPU: 80%
  • Avg response time: 150ms

After (JWT)

  • Auth queries: ~17/minute (login only)
  • API queries: 10,000/minute
  • Total: 10,017/minute (50% reduction!)
  • Database CPU: 40%
  • Avg response time: 45ms (3× faster!)

🚀 Quick Start: Switch to JWT Now

# 1. Update config
# Change 'auth_method' => 'jwt' in config/api.php

# 2. Test login
curl -X POST http://localhost/PHP-CRUD-API-Generator/public/index.php?action=login \
  -d "username=john&password=SecurePass123!"

# 3. Use token
TOKEN="eyJhbGciOiJIUzI1..."
curl -H "Authorization: Bearer $TOKEN" \
     http://localhost/PHP-CRUD-API-Generator/public/index.php?action=tables

# Done! 99.8% fewer database queries

💡 Summary

Your concern is valid and critical!

  • ❌ Current: 10,000 auth DB queries/minute
  • ✅ With JWT: 17 auth DB queries/minute
  • 🎯 99.8% performance improvement

Recommendation: Switch to JWT authentication (already implemented in your system). Change one line in config and you're done!