Removed Old CSRF logic
This commit is contained in:
@@ -30,6 +30,15 @@
|
|||||||
- **start.sh**
|
- **start.sh**
|
||||||
- Session directory setup
|
- Session directory setup
|
||||||
|
|
||||||
|
- Always sends `credentials: 'include'` and `X-CSRF-Token: window.csrfToken` s
|
||||||
|
- On HTTP 403, automatically fetches a fresh CSRF token (from the response header or `/api/auth/token.php`) and retries the request once
|
||||||
|
- Always returns the real `Response` object (no more “clone.json” on every 200)
|
||||||
|
- Now calls `fetchWithCsrf('/api/auth/token.php')` to guarantee a fresh token
|
||||||
|
- Checks `res.ok`, then parses JSON to extract `csrf_token` and `share_url`
|
||||||
|
- Updates both `window.csrfToken` and the `<meta name="csrf-token">` & `<meta name="share-url">` tags
|
||||||
|
- Removed Old CSRF logic that cloned every successful response and parsed its JSON body
|
||||||
|
- Removed Any “soft-failure” JSON peek on non-403 responses
|
||||||
|
|
||||||
## Changes 4/22/2025 v1.2.3
|
## Changes 4/22/2025 v1.2.3
|
||||||
|
|
||||||
- Support for custom PUID/PGID via `PUID`/`PGID` environment variables, replacing the need to run the container with `--user`
|
- Support for custom PUID/PGID via `PUID`/`PGID` environment variables, replacing the need to run the container with `--user`
|
||||||
|
|||||||
@@ -52,28 +52,24 @@ const originalFetch = window.fetch;
|
|||||||
* @returns {Promise<Response>}
|
* @returns {Promise<Response>}
|
||||||
*/
|
*/
|
||||||
export async function fetchWithCsrf(url, options = {}) {
|
export async function fetchWithCsrf(url, options = {}) {
|
||||||
options = { credentials: 'include', headers: {}, ...options };
|
// 1) Merge in credentials + header
|
||||||
options.headers['X-CSRF-Token'] = window.csrfToken;
|
options = {
|
||||||
|
credentials: 'include',
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
options.headers = {
|
||||||
|
...(options.headers || {}),
|
||||||
|
'X-CSRF-Token': window.csrfToken,
|
||||||
|
};
|
||||||
|
|
||||||
// 1) First attempt using the original fetch
|
// 2) First attempt
|
||||||
let res = await originalFetch(url, options);
|
let res = await originalFetch(url, options);
|
||||||
|
|
||||||
// 2) Soft‐failure JSON check (200 + {csrf_expired})
|
// 3) If we got a 403, try to refresh token & retry
|
||||||
if (res.ok && res.headers.get('content-type')?.includes('application/json')) {
|
|
||||||
const clone = res.clone();
|
|
||||||
const data = await clone.json();
|
|
||||||
if (data.csrf_expired) {
|
|
||||||
const newToken = data.csrf_token;
|
|
||||||
window.csrfToken = newToken;
|
|
||||||
document.querySelector('meta[name="csrf-token"]').content = newToken;
|
|
||||||
options.headers['X-CSRF-Token'] = newToken;
|
|
||||||
return originalFetch(url, options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3) HTTP 403 fallback
|
|
||||||
if (res.status === 403) {
|
if (res.status === 403) {
|
||||||
|
// 3a) See if the server gave us a new token header
|
||||||
let newToken = res.headers.get('X-CSRF-Token');
|
let newToken = res.headers.get('X-CSRF-Token');
|
||||||
|
// 3b) Otherwise fall back to the /api/auth/token endpoint
|
||||||
if (!newToken) {
|
if (!newToken) {
|
||||||
const tokRes = await originalFetch('/api/auth/token.php', { credentials: 'include' });
|
const tokRes = await originalFetch('/api/auth/token.php', { credentials: 'include' });
|
||||||
if (tokRes.ok) {
|
if (tokRes.ok) {
|
||||||
@@ -82,17 +78,21 @@ export async function fetchWithCsrf(url, options = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (newToken) {
|
if (newToken) {
|
||||||
|
// 3c) Update global + meta
|
||||||
window.csrfToken = newToken;
|
window.csrfToken = newToken;
|
||||||
document.querySelector('meta[name="csrf-token"]').content = newToken;
|
const meta = document.querySelector('meta[name="csrf-token"]');
|
||||||
|
if (meta) meta.content = newToken;
|
||||||
|
|
||||||
|
// 3d) Retry the original request with the new token
|
||||||
options.headers['X-CSRF-Token'] = newToken;
|
options.headers['X-CSRF-Token'] = newToken;
|
||||||
res = await originalFetch(url, options);
|
res = await originalFetch(url, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4) Return the real Response—no body peeking here!
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// wrap the TOTP modal opener to disable other login buttons only for Basic/OIDC flows
|
// wrap the TOTP modal opener to disable other login buttons only for Basic/OIDC flows
|
||||||
function openTOTPLoginModal() {
|
function openTOTPLoginModal() {
|
||||||
originalOpenTOTPLoginModal();
|
originalOpenTOTPLoginModal();
|
||||||
|
|||||||
@@ -14,36 +14,20 @@ import { initFileActions, renameFile, openDownloadModal, confirmSingleDownload }
|
|||||||
import { editFile, saveFile } from './fileEditor.js';
|
import { editFile, saveFile } from './fileEditor.js';
|
||||||
import { t, applyTranslations, setLocale } from './i18n.js';
|
import { t, applyTranslations, setLocale } from './i18n.js';
|
||||||
|
|
||||||
// Remove the retry logic version and just use loadCsrfToken directly:
|
|
||||||
/**
|
|
||||||
* Fetches the current CSRF token (and share URL), updates window globals
|
|
||||||
* and <meta> tags, and returns the data.
|
|
||||||
*
|
|
||||||
* @returns {Promise<{csrf_token: string, share_url: string}>}
|
|
||||||
*/
|
|
||||||
export function loadCsrfToken() {
|
export function loadCsrfToken() {
|
||||||
return fetch('/api/auth/token.php', {
|
return fetchWithCsrf('/api/auth/token.php', {
|
||||||
method: 'GET',
|
method: 'GET'
|
||||||
credentials: 'include'
|
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(res => {
|
||||||
if (!response.ok) {
|
if (!res.ok) {
|
||||||
throw new Error(`Token fetch failed with status: ${response.status}`);
|
throw new Error(`Token fetch failed with status ${res.status}`);
|
||||||
}
|
}
|
||||||
// Prefer header if set, otherwise fall back to body
|
return res.json();
|
||||||
const headerToken = response.headers.get('X-CSRF-Token');
|
|
||||||
return response.json()
|
|
||||||
.then(body => ({
|
|
||||||
csrf_token: headerToken || body.csrf_token,
|
|
||||||
share_url: body.share_url
|
|
||||||
}));
|
|
||||||
})
|
})
|
||||||
.then(({ csrf_token, share_url }) => {
|
.then(({ csrf_token, share_url }) => {
|
||||||
// Update globals
|
// Update global and <meta>
|
||||||
window.csrfToken = csrf_token;
|
window.csrfToken = csrf_token;
|
||||||
window.SHARE_URL = share_url;
|
|
||||||
|
|
||||||
// Sync <meta name="csrf-token">
|
|
||||||
let meta = document.querySelector('meta[name="csrf-token"]');
|
let meta = document.querySelector('meta[name="csrf-token"]');
|
||||||
if (!meta) {
|
if (!meta) {
|
||||||
meta = document.createElement('meta');
|
meta = document.createElement('meta');
|
||||||
@@ -52,7 +36,6 @@ export function loadCsrfToken() {
|
|||||||
}
|
}
|
||||||
meta.content = csrf_token;
|
meta.content = csrf_token;
|
||||||
|
|
||||||
// Sync <meta name="share-url">
|
|
||||||
let shareMeta = document.querySelector('meta[name="share-url"]');
|
let shareMeta = document.querySelector('meta[name="share-url"]');
|
||||||
if (!shareMeta) {
|
if (!shareMeta) {
|
||||||
shareMeta = document.createElement('meta');
|
shareMeta = document.createElement('meta');
|
||||||
|
|||||||
Reference in New Issue
Block a user