feat: CSRF token auto-refresh après expiration session
- app/main.py: GET /api/csrf-token → retourne un token frais JSON + cookie
- app/middleware/csrf.py: /api/csrf-token ajouté aux EXCLUDED_PATHS
- app/templates/base.html:
- FlowDeck.refreshCsrfToken() → appelle /api/csrf-token
- FlowDeck.csrfFetch() → wrapper fetch avec auto-refresh sur 403 CSRF
- Si un POST/PUT/DELETE reçoit 403 'CSRF', refresh automatique + retry
- ROADMAP: item CSRF token refresh marqué ✅
- 143 tests passent
This commit is contained in:
@@ -590,6 +590,47 @@
|
||||
return m ? m[1] : '';
|
||||
},
|
||||
|
||||
/** Refresh CSRF token from the server. Returns a promise resolving to the new token. */
|
||||
refreshCsrfToken: async function() {
|
||||
try {
|
||||
var resp = await fetch('/api/csrf-token', { method: 'GET', credentials: 'same-origin' });
|
||||
var data = await resp.json();
|
||||
if (data.csrf_token) {
|
||||
return data.csrf_token;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[FlowDeck] CSRF refresh failed:', e);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/** Fetch wrapper that auto-refreshes CSRF token on 403 and retries once. */
|
||||
csrfFetch: async function(url, options) {
|
||||
options = options || {};
|
||||
options.credentials = options.credentials || 'same-origin';
|
||||
if (!options.headers) options.headers = {};
|
||||
if (!options.headers['X-CSRF-Token']) {
|
||||
options.headers['X-CSRF-Token'] = this.getCsrfToken();
|
||||
}
|
||||
|
||||
var response = await fetch(url, options);
|
||||
|
||||
// Auto-refresh on CSRF failure and retry once
|
||||
if (response.status === 403) {
|
||||
var body = '';
|
||||
try { body = await response.clone().text(); } catch(e) {}
|
||||
if (body.indexOf('CSRF') !== -1 || body.indexOf('csrf') !== -1) {
|
||||
var newToken = await this.refreshCsrfToken();
|
||||
if (newToken) {
|
||||
options.headers['X-CSRF-Token'] = newToken;
|
||||
return fetch(url, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
},
|
||||
|
||||
/** Detect the active workspace context from multiple sources.
|
||||
* Returns { wsId: number, isGitea: boolean, workspaceKey: string } */
|
||||
_getContext: function() {
|
||||
|
||||
Reference in New Issue
Block a user