fix(web): skip empty Authorization header for forward-auth proxy compatibility

Centralize duplicated getToken() + Bearer header logic into a single
formatHeaders() helper in lib/api.ts. When no token exists, the
Authorization header is omitted entirely instead of sending an empty
Bearer token, which breaks forward-auth proxies like Authelia behind
Caddy.

Changes:
- Add formatHeaders() with try-catch around localStorage access
- Replace 20+ duplicated getToken() definitions across tool components
- Migrate all call sites including file-details, settings, change-password
- Update tests to verify header omission on empty token

Based on the fix proposed by @jules2689 in #6, with improvements:
file placement (lib/api.ts vs components), localStorage error handling,
simplified truthiness check, and complete call-site coverage.

Co-Authored-By: Julian Nadeau <julian@jnadeau.ca>
This commit is contained in:
Siddharth Kumar Sah
2026-04-05 18:41:06 +08:00
co-authored by Julian Nadeau
parent f21579c7a3
commit d0c69d6a46
26 changed files with 104 additions and 177 deletions
@@ -4,6 +4,7 @@ import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import {
apiGetFileDetails,
formatHeaders,
getFileDownloadUrl,
getFileThumbnailUrl,
type UserFileDetail,
@@ -57,11 +58,10 @@ export function FileDetails({ mobile = false }: FileDetailsProps) {
? allFiles.filter((f) => checkedIds.has(f.id))
: [{ id: details.id, originalName: details.originalName, mimeType: details.mimeType }];
const token = localStorage.getItem("stirling-token") || "";
const downloaded = await Promise.all(
filesToOpen.map(async (f) => {
const res = await fetch(getFileDownloadUrl(f.id), {
headers: { Authorization: `Bearer ${token}` },
headers: formatHeaders(),
});
if (!res.ok) return null;
const blob = await res.blob();