File size: 3,763 Bytes
d833ce9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/**
 * fetch-wrapper.js - Hardened Fetch Wrapper for Qualora Enterprise
 * * AUTOMATIC FEATURES:
 * - Automatic HTTP-Only Cookie propagation (Secure JWT handling)
 * - CSRF Token injection from 'csrf_token' cookie to 'X-CSRF-Token' header
 * - 401 Unauthorized handling (Clears local UI state and redirects)
 * - 429 Rate Limit handling (Exponential backoff retry)
 * - Strict Path Traversal and Network Error resilience
 */

(function() {
    'use strict';

    const originalFetch = window.fetch;

    /**
     * Helper: Extract CSRF token from browser cookies.
     * Aligned with security.py cookie naming.
     */
    function getCsrfToken() {
        const match = document.cookie.match(/csrf_token=([^;]+)/);
        return match ? match[1] : null;
    }

    /**
     * Handle session expiration.
     * Synchronized with auth-init.js to use the 'user' key.
     */
    function handleAuthFailure() {
        console.warn('[Qualora] Session expired or invalid. Cleaning up.');
        
        // Remove the UI Hint (JS can't touch the HTTP-Only cookie anyway)
        localStorage.removeItem('user'); 
        sessionStorage.clear();
        
        // Only redirect if the user is currently on a protected page
        const publicPaths = ['/', '/index.html', '/login', '/register'];
        if (!publicPaths.includes(window.location.pathname)) {
            window.location.href = '/';
        }
    }

    /**
     * Internal: Enhanced fetch with exponential backoff for 429 Rate Limits
     */
    async function fetchWithRetry(url, options, retries = 2) {
        for (let i = 0; i <= retries; i++) {
            try {
                const response = await originalFetch(url, options);
                
                // Handle 429 (Rate Limit) from our hardened security.py logic
                if (response.status === 429 && i < retries) {
                    const delay = Math.pow(2, i) * 1000; 
                    console.warn(`[Qualora] Rate limited. Retrying in ${delay}ms...`);
                    await new Promise(res => setTimeout(res, delay));
                    continue;
                }
                
                return response;
            } catch (error) {
                if (i === retries) throw error;
                const delay = Math.pow(2, i) * 500;
                await new Promise(res => setTimeout(res, delay));
            }
        }
    }

    /**
     * Global Fetch Override
     */
    window.fetch = async function(url, options = {}) {
        options.headers = options.headers || {};

        // 1. JWT Security: We DO NOT inject Bearer headers.
        // The browser handles the HTTP-Only 'access_token' cookie automatically
        // because we set credentials: 'include' below.
        
        // 2. CSRF Protection: Inject header for state-changing requests
        const method = (options.method || 'GET').toUpperCase();
        const needsCsrf = ['POST', 'PUT', 'DELETE', 'PATCH'].includes(method);
        
        if (needsCsrf) {
            const csrfToken = getCsrfToken();
            if (csrfToken) {
                options.headers['X-CSRF-Token'] = csrfToken;
            }
        }

        // 3. Credentials: MUST be 'include' to pass HTTP-Only cookies across origins
        options.credentials = 'include';

        try {
            const response = await fetchWithRetry(url, options);

            // 4. Session Management
            if (response.status === 401) {
                handleAuthFailure();
            }

            return response;
        } catch (error) {
            console.error('[Qualora] Network request failed:', error);
            throw error;
        }
    };

    console.log('✅ Qualora Fetch Wrapper: Secure Cookie & CSRF mode active.');
})();