🔐 Firestore-Witnessed Authentication System

Advanced cryptographic authentication that replaces static admin keys with time-limited HMAC proofs stored in Firestore.

Overview

Sauron Pro implements a sophisticated Firestore-witnessed authentication system that replaces static admin keys with time-limited cryptographic proofs. This provides enhanced security by ensuring admin credentials are never transmitted over the network.

✅ Security Benefits

  • • No admin keys in transit
  • • Time-limited proofs (24 hours)
  • • HMAC-SHA256 signatures
  • • Replay attack prevention

🚀 Performance Features

  • • 24-hour proof caching
  • • Automatic cleanup
  • • Fast validation
  • • Legacy compatibility

How It Works

1. Authentication Flow

Admin Panel → Firestore (Store Proof) → Sauron Server → Firestore (Retrieve Proof) → Validate → Response

2. Proof Generation

The admin panel generates a cryptographic proof using HMAC-SHA256:

// TypeScript - Admin Panel
const payload = `${requestId}:${userAgent}:${clientIP}:${validUntil}:${createdAt}`;
const signature = crypto
  .createHmac('sha256', adminKey)
  .update(payload)
  .digest('hex');

// Store in Firestore
await db.collection('auth_proofs').doc(requestId).set({
  proof: signature,
  valid_until: validUntil,
  created_at: createdAt,
  user_agent: userAgent,
  client_ip: clientIP,
  proof_type: 'admin_auth'
});

3. Firestore Storage Structure

{
  "request_id": "a35905f9c363f8be3e297a5b59d8cf29",
  "proof": "3e69289a81eb9ef8774df5a5bfdef287ff8549c57614a5a3afd1a85dd4e17487",
  "valid_until": 1755734576,
  "created_at": 1755648976,
  "user_agent": "NextJS-Admin-Panel/1.0",
  "client_ip": "::1",
  "proof_type": "admin_auth"
}

Implementation Examples

Go Server Implementation

// auth/firestore_witness.go
func ValidateFirestoreAuth(requestID, validUntilStr string) (*AuthResult, error) {
    // Retrieve proof from Firestore
    doc, err := firestoreClient.Collection("auth_proofs").Doc(requestID).Get(ctx)
    if err != nil {
        return nil, fmt.Errorf("proof not found: %w", err)
    }

    var proof FirestoreProof
    if err := doc.DataTo(&proof); err != nil {
        return nil, fmt.Errorf("invalid proof format: %w", err)
    }

    // Validate timing
    if time.Now().Unix() > proof.ValidUntil {
        return nil, errors.New("proof expired")
    }

    // Validate HMAC signature
    adminKey := configdb.GetAdminKey()
    payload := fmt.Sprintf("%s:%s:%s:%d:%d", 
        requestID, proof.UserAgent, proof.ClientIP, 
        proof.ValidUntil, proof.CreatedAt)
    
    expectedHMAC := hmac.New(sha256.New, []byte(adminKey))
    expectedHMAC.Write([]byte(payload))
    expectedSignature := hex.EncodeToString(expectedHMAC.Sum(nil))

    if !hmac.Equal([]byte(proof.Proof), []byte(expectedSignature)) {
        return nil, errors.New("invalid signature")
    }

    return &AuthResult{
        Method: "firestore-witnessed",
        Valid:  true,
        Source: "firestore",
    }, nil
}

TypeScript Client Implementation

// src/lib/authManager.ts
export class AuthManager {
  async authenticatedFetch(endpoint: string, options: RequestInit = {}): Promise<Response> {
    const requestId = this.generateRequestId();
    const validUntil = Math.floor(Date.now() / 1000) + (24 * 60 * 60); // 24 hours
    const createdAt = Math.floor(Date.now() / 1000);
    
    // Generate HMAC proof
    const payload = `${requestId}:NextJS-Admin-Panel/1.0:::${validUntil}:${createdAt}`;
    const signature = crypto
      .createHmac('sha256', this.adminKey)
      .update(payload)
      .digest('hex');

    // Store proof in Firestore
    await this.db.collection('auth_proofs').doc(requestId).set({
      proof: signature,
      valid_until: validUntil,
      created_at: createdAt,
      user_agent: 'NextJS-Admin-Panel/1.0',
      client_ip: '::1',
      proof_type: 'admin_auth'
    });

    // Make authenticated request
    const headers = {
      'X-Request-ID': requestId,
      'X-Valid-Until': (validUntil * 1000).toString(),
      'Content-Type': 'application/json',
      ...options.headers
    };

    return fetch(endpoint, { ...options, headers });
  }
}

Usage Guide

Step 1: Generate Proof

// Generate authentication proof
GET /api/auth/proof

Response:
{
  "success": true,
  "requestId": "a35905f9c363f8be3e297a5b59d8cf29",
  "validUntil": "2025-08-21T00:02:56.000Z",
  "signature": "3e69289a81eb...",
  "usage": {
    "headers": {
      "X-Request-ID": "a35905f9c363f8be3e297a5b59d8cf29",
      "X-Valid-Until": "1755734576000"
    }
  }
}

Step 2: Use Headers

// Use authentication headers
curl -X POST \
  -H "X-Request-ID: a35905f9c363f8be3e297a5b59d8cf29" \
  -H "X-Valid-Until: 1755734576000" \
  -H "Content-Type: application/json" \
  -d '{"action": "cleanup"}' \
  https://your-domain.com/api/admin/cleanup

Response:
{
  "success": true,
  "authMethod": "firestore-witnessed",
  "data": {...}
}

Security Features

🔒 Cryptographic Security

  • HMAC-SHA256: Industry-standard message authentication
  • Admin Key Protection: Never transmitted over network
  • Unique Proofs: Each request has unique signature
  • Time-limited: 24-hour maximum validity

🛡️ Attack Prevention

  • Replay Attacks: Prevented by time validation
  • MITM Attacks: Signature validation required
  • Brute Force: No keys to compromise
  • Forensics: Automatic proof cleanup

Troubleshooting

⚠️ Common Issues

Invalid Signature: Check admin key configuration and payload format

Proof Expired: Generate new proof - maximum 24 hours validity

Firestore Connection: Verify Firestore credentials and project setup

Debug Authentication

// Enable debug logging
curl -X GET \
  -H "X-Request-ID: your-request-id" \
  -H "X-Valid-Until: timestamp" \
  https://your-domain.com/api/auth/debug

// Check Firestore proof
curl -X GET \
  https://your-domain.com/api/auth/proof/your-request-id

Part of the Sauron Pro Documentation