fix(auth): keep session on a wrong-current-password 401

The change-password endpoint returns 401 for a wrong *current* password,
the same status the global on401 hook uses to detect an expired session.
The hook fired first and cleared session.user, so the account form's
"still signed in?" guard always saw null and bounced the user to /login
instead of surfacing the error inline.

Add a per-call `suppressOn401` option to `request`; changePassword opts
in, so a 401 there stays inline and the session is left intact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-01 20:11:07 +02:00
parent 5596e1920d
commit 0865659ab3
6 changed files with 53 additions and 10 deletions

View File

@@ -103,6 +103,25 @@ describe('on401 hook', () => {
expect(hook).toHaveBeenCalledTimes(1);
});
it('does not invoke the hook on a 401 when suppressOn401 is set', async () => {
// The change-password endpoint returns 401 for a *wrong current
// password*, not an expired session — clearing the cached user
// there would spuriously log the (still-authenticated) user out.
// Such calls opt out of the hook while still throwing the 401.
const hook = vi.fn();
setOn401Hook(hook);
fetchSpy.mockResolvedValueOnce(
new Response(
JSON.stringify({ error: { code: 'unauthenticated', message: 'wrong password' } }),
{ status: 401, headers: { 'content-type': 'application/json' } }
)
);
await expect(
request('/v1/auth/me/password', { method: 'PATCH' }, { suppressOn401: true })
).rejects.toMatchObject({ status: 401, code: 'unauthenticated' });
expect(hook).not.toHaveBeenCalled();
});
it('does not invoke the hook on non-401 errors', async () => {
const hook = vi.fn();
setOn401Hook(hook);