File History: src/main/resources/static/js/csrf.js

← View file content

File Content at Commit 4617f76

1 // CSRF Token Management
2 let csrfToken = null;
3 let tokenPromise = null;
4
5 // Get CSRF token from server (force refresh if needed)
6 async function getCsrfToken(forceRefresh = false) {
7 // If forcing refresh, clear existing token
8 if (forceRefresh) {
9 csrfToken = null;
10 tokenPromise = null;
11 }
12
13 // Return existing token if available and not forcing refresh
14 if (csrfToken && !forceRefresh) {
15 return csrfToken;
16 }
17
18 // If a request is already in progress, wait for it
19 if (tokenPromise) {
20 return tokenPromise;
21 }
22
23 // Fetch new token
24 tokenPromise = (async () => {
25 try {
26 const response = await fetch('/api/csrf-token', {
27 credentials: 'include' // Include cookies
28 });
29 const data = await response.json();
30
31 if (data.success) {
32 csrfToken = data.csrfToken;
33 tokenPromise = null;
34 return csrfToken;
35 }
36 } catch (error) {
37 console.error('Error fetching CSRF token:', error);
38 tokenPromise = null;
39 }
40
41 return null;
42 })();
43
44 return tokenPromise;
45 }
46
47 // Refresh CSRF token (alias for force refresh)
48 async function refreshCsrfToken() {
49 return await getCsrfToken(true);
50 }
51
52 // Add CSRF token to fetch request
53 async function fetchWithCsrf(url, options = {}) {
54 const method = options.method || 'GET';
55 const stateChangingMethods = ['POST', 'PUT', 'PATCH', 'DELETE'];
56
57 // For state-changing requests, always get a fresh token first
58 if (stateChangingMethods.includes(method.toUpperCase())) {
59 // Always refresh token before state-changing requests
60 // This ensures we have a valid token even if the previous one was invalidated
61 await refreshCsrfToken();
62 } else if (!csrfToken) {
63 // For GET requests, only fetch if we don't have a token
64 await getCsrfToken();
65 }
66
67 // Add CSRF token to headers
68 const headers = {
69 ...options.headers,
70 'X-CSRF-Token': csrfToken || ''
71 };
72
73 const response = await fetch(url, {
74 ...options,
75 headers,
76 credentials: 'include' // Include cookies
77 });
78
79 // After state-changing requests, always refresh the token
80 // The server invalidates the used token, so we need a new one for the next request
81 if (stateChangingMethods.includes(method.toUpperCase())) {
82 // Try to get new token from response header (server sends it)
83 const newToken = response.headers.get('X-New-CSRF-Token');
84 if (newToken) {
85 csrfToken = newToken;
86 } else {
87 // If no header (or header not accessible), refresh token immediately
88 // This ensures we always have a fresh token for the next request
89 try {
90 await refreshCsrfToken();
91 } catch (err) {
92 console.error('Error refreshing CSRF token:', err);
93 }
94 }
95 }
96
97 return response;
98 }
99
100 // Initialize CSRF token on page load
101 if (document.readyState === 'loading') {
102 document.addEventListener('DOMContentLoaded', async () => {
103 await getCsrfToken();
104 });
105 } else {
106 // DOM already loaded
107 getCsrfToken();
108 }
109
110 // Make functions globally available
111 window.getCsrfToken = getCsrfToken;
112 window.refreshCsrfToken = refreshCsrfToken;
113 window.fetchWithCsrf = fetchWithCsrf;
114
115
116

Commits

Commit Author Date Message File SHA Actions
f0438c2 <f69e50@finnacloud.com> 1766443042 +0300 12/22/2025, 10:37:22 PM increment once more a2a3e99 View
188fc92 <f69e50@finnacloud.com> 1766442998 +0300 12/22/2025, 10:36:38 PM increment a2a3e99 View
4617f76 <f69e50@finnacloud.com> 1766442953 +0300 12/22/2025, 10:35:53 PM rename branch from main to master oops a2a3e99 Hide
e6d1548 <f69e50@finnacloud.com> 1766442769 +0300 12/22/2025, 10:32:49 PM add initial test workflow file a2a3e99 View
9c24ca4 <f69e50@finnacloud.com> 1766442705 +0300 12/22/2025, 10:31:45 PM add CI configuration and test script for Jenkins build a2a3e99 View
690c1f6 <f69e50@finnacloud.com> 1766368110 +0300 12/22/2025, 1:48:30 AM initialize backend structure with controllers, DTOs, and configuration files a2a3e99 View