Clear stale API-key credentials when a password session begins
Why: the auth middleware prefers Bearer keys over the session cookie, so a stale key left in localStorage shadows valid cookie sessions and every request after password login returns 401 (observed at http://192.168.3.2:8788 on 2026-10-11). Evidence: modules/frontend vitest 19 files / 88 tests green; auth.test.ts covers beginSession clearing the stored Bearer before any fetch. Impact: password logins switch to the HttpOnly cookie session cleanly; API-key logins unchanged. Refs: task card feature-login-clear-stale-key in Agent-System-Dev.
This commit is contained in:
@@ -22,6 +22,9 @@ export const auth = {
|
||||
apiKey(): string { return storage.getItem(KEY) ?? '' },
|
||||
setKey(key: string) { storage.setItem(KEY, key.trim()) },
|
||||
clear() { storage.removeItem(KEY) },
|
||||
// 密码登录成功后切换到 HttpOnly 会话 cookie:必须清掉残留 Bearer——服务端
|
||||
// 认证中间件 Bearer 优先于会话 cookie,失效残留 key 会把有效会话也判成 401。
|
||||
beginSession() { this.clear() },
|
||||
// 带凭证的 fetch:未设置密钥时与原生 fetch 等价(嵌入式档无认证)。
|
||||
fetch(input: string, init?: RequestInit): Promise<Response> {
|
||||
const key = this.apiKey()
|
||||
|
||||
@@ -41,6 +41,8 @@ export function LoginPage({ onAuthed, onMustChange }: { onAuthed: () => void; on
|
||||
setErr(''); setBusy(true)
|
||||
try {
|
||||
const me = await api.login(owner.trim(), password)
|
||||
// 会话凭证是 HttpOnly cookie;清残留 Bearer,避免其遮蔽有效会话。
|
||||
auth.beginSession()
|
||||
if (me.mustChangePassword) {
|
||||
// 初始/重置密码:先完成强制改密再放行。
|
||||
onMustChange(me.owner)
|
||||
|
||||
@@ -40,4 +40,15 @@ describe('auth 凭证模块', () => {
|
||||
expect(auth.apiKey()).toBe('')
|
||||
expect(auth.withKey('/x')).toBe('/x')
|
||||
})
|
||||
|
||||
it('beginSession:密码会话生效后清残留 Bearer,fetch 不再带头', async () => {
|
||||
auth.setKey('ask_stale')
|
||||
auth.beginSession()
|
||||
expect(auth.apiKey()).toBe('')
|
||||
const spy = vi.fn().mockResolvedValue(new Response('{}'))
|
||||
vi.stubGlobal('fetch', spy)
|
||||
await auth.fetch('/api/me')
|
||||
const [, init] = spy.mock.calls[0] as [string, RequestInit | undefined]
|
||||
expect(new Headers(init?.headers ?? {}).get('Authorization')).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user