mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-17 19:36:28 +02:00
Issue #257 Fix:
- Changed the 401 handling in request() to only clear the auth token when the error message indicates the token is actually invalid - Previously: ANY 401 response cleared the token (could cause cascading failures) - Now: Only clears on specific messages like "Token has expired", "Could not validate credentials", etc. - "Authentication required" (which might be a timing issue) no longer clears the token Closes #257
This commit is contained in:
@@ -95,11 +95,11 @@ describe('API Client Auth Header', () => {
|
||||
expect(capturedHeaders!.get('Authorization')).toBeNull();
|
||||
});
|
||||
|
||||
it('clears token on 401 Unauthorized response', async () => {
|
||||
it('clears token on 401 with invalid token message', async () => {
|
||||
server.use(
|
||||
http.get('/api/v1/settings/spoolman', () => {
|
||||
return HttpResponse.json(
|
||||
{ detail: 'Not authenticated' },
|
||||
{ detail: 'Could not validate credentials' },
|
||||
{ status: 401 }
|
||||
);
|
||||
})
|
||||
@@ -117,6 +117,29 @@ describe('API Client Auth Header', () => {
|
||||
expect(getAuthToken()).toBeNull();
|
||||
expect(localStorageMock.removeItem).toHaveBeenCalledWith('auth_token');
|
||||
});
|
||||
|
||||
it('does not clear token on 401 with generic auth error', async () => {
|
||||
server.use(
|
||||
http.get('/api/v1/settings/spoolman', () => {
|
||||
return HttpResponse.json(
|
||||
{ detail: 'Authentication required' },
|
||||
{ status: 401 }
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
setAuthToken('valid-token');
|
||||
expect(getAuthToken()).toBe('valid-token');
|
||||
|
||||
try {
|
||||
await api.getSpoolmanSettings();
|
||||
} catch {
|
||||
// Expected to throw
|
||||
}
|
||||
|
||||
// Token should NOT be cleared for generic auth errors (might be timing issue)
|
||||
expect(getAuthToken()).toBe('valid-token');
|
||||
});
|
||||
});
|
||||
|
||||
describe('FormData requests include auth header', () => {
|
||||
|
||||
@@ -39,16 +39,27 @@ async function request<T>(
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
// Handle 401 Unauthorized - clear token and redirect to login
|
||||
if (response.status === 401) {
|
||||
setAuthToken(null);
|
||||
// Don't throw here - let the auth context handle redirect
|
||||
}
|
||||
const error = await response.json().catch(() => ({}));
|
||||
const detail = error.detail;
|
||||
const message = typeof detail === 'string'
|
||||
? detail
|
||||
: (detail ? JSON.stringify(detail) : `HTTP ${response.status}`);
|
||||
|
||||
// Handle 401 Unauthorized - only clear token if it's actually invalid
|
||||
// Don't clear on "Authentication required" which might be a timing issue
|
||||
if (response.status === 401) {
|
||||
const invalidTokenMessages = [
|
||||
'Could not validate credentials',
|
||||
'Token has expired',
|
||||
'User not found or inactive',
|
||||
'Invalid API key',
|
||||
'API key has expired',
|
||||
];
|
||||
if (invalidTokenMessages.some(m => message.includes(m))) {
|
||||
setAuthToken(null);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -23,7 +23,7 @@
|
||||
|
||||
<!-- Splash screens for iOS -->
|
||||
<link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
|
||||
<script type="module" crossorigin src="/assets/index-DLGvpmyc.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-DzQ3-JjU.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-fdAEMOwp.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user