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.
55 lines
2.2 KiB
TypeScript
55 lines
2.2 KiB
TypeScript
// 认证模块行为测试:密钥存取(无 DOM 回退内存)、凭证注入、资源 URL 追加 key。
|
||
import { describe, expect, it, vi, beforeEach } from 'vitest'
|
||
import { auth } from '../src/auth'
|
||
|
||
describe('auth 凭证模块', () => {
|
||
beforeEach(() => {
|
||
auth.clear()
|
||
vi.unstubAllGlobals()
|
||
})
|
||
|
||
it('未设置密钥时 fetch 透传(无 Authorization 头)', async () => {
|
||
const spy = vi.fn().mockResolvedValue(new Response('{}'))
|
||
vi.stubGlobal('fetch', spy)
|
||
await auth.fetch('/api/projects')
|
||
const [, init] = spy.mock.calls[0] as [string, RequestInit | undefined]
|
||
const headers = new Headers(init?.headers ?? {})
|
||
expect(headers.get('Authorization')).toBeNull()
|
||
})
|
||
|
||
it('设置密钥后 fetch 注入 Bearer 头', async () => {
|
||
auth.setKey('ask_test123')
|
||
const spy = vi.fn().mockResolvedValue(new Response('{}'))
|
||
vi.stubGlobal('fetch', spy)
|
||
await auth.fetch('/api/projects', { method: 'POST' })
|
||
const [, init] = spy.mock.calls[0] as [string, RequestInit]
|
||
expect(new Headers(init.headers).get('Authorization')).toBe('Bearer ask_test123')
|
||
expect(init.method).toBe('POST') // 原有 init 语义保留
|
||
})
|
||
|
||
it('withKey:无密钥不改 URL;有密钥按是否含 ? 选择连接符', () => {
|
||
expect(auth.withKey('/api/artifacts/a/content')).toBe('/api/artifacts/a/content')
|
||
auth.setKey('ask_k')
|
||
expect(auth.withKey('/api/events?conversationId=c1')).toBe('/api/events?conversationId=c1&key=ask_k')
|
||
expect(auth.withKey('/api/artifacts/a/content')).toBe('/api/artifacts/a/content?key=ask_k')
|
||
})
|
||
|
||
it('clear 后恢复无凭证状态', () => {
|
||
auth.setKey('ask_x')
|
||
auth.clear()
|
||
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()
|
||
})
|
||
})
|