From 5abdc84b6b9316dc39e90628d795dee417fc539c Mon Sep 17 00:00:00 2001 From: Baocang Nie <16043697+baocang@users.noreply.github.com> Date: Fri, 4 Sep 2026 17:17:24 +0800 Subject: [PATCH] * fix: reject invalid post-login tokens --- src/auth/login.ts | 7 ++++--- tests/auth.test.ts | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/auth/login.ts b/src/auth/login.ts index d667c79..157b0d7 100644 --- a/src/auth/login.ts +++ b/src/auth/login.ts @@ -40,8 +40,9 @@ export async function login( let serverConfig: ServerConfig | undefined; try { ({ serverConfig, user } = await verifyToken(client, account)); - } catch { - // Token valid but couldn't fetch user details - not fatal + } catch (error) { + // Authentication failure is fatal; enrichment failures remain best-effort. + if (error instanceof ZentaoError && error.code === '1004') throw error; } return { token, user, serverConfig }; @@ -50,7 +51,7 @@ export async function login( /** * 拉取服务器配置与用户列表,用于验证 Token 是否可用。 * - 服务端配置失败抛 E1002(服务不可达) - * - /users 401 由 SDK 映射为 E1004(Token 失效) + * - /users 401 或 HTTP 200 登录页映射为 E1004(Token 失效) * - /users 返回空列表也按 E1004 处理 */ export async function verifyToken( diff --git a/tests/auth.test.ts b/tests/auth.test.ts index 7279a6e..603e258 100644 --- a/tests/auth.test.ts +++ b/tests/auth.test.ts @@ -1,6 +1,6 @@ import { describe, test, expect, afterEach, beforeEach } from 'bun:test'; import { createClient } from '../src/api/index'; -import { verifyToken, getEnvCredentials } from '../src/auth/login'; +import { login, verifyToken, getEnvCredentials } from '../src/auth/login'; import { ZentaoError } from '../src/errors'; type RouteHandler = (req: Request, url: URL) => Response | Promise; @@ -31,6 +31,46 @@ function makeClient(server: { url: URL }, token = 'test-token') { return createClient(server.url.toString(), token); } +describe('login', () => { + test('does not accept a token when post-login verification returns a login page', async () => { + const server = createMockServer({ + users: () => new Response( + '
', + { headers: { 'Content-Type': 'text/html' } }, + ), + fallback: (_req, url) => url.pathname === '/api.php/v2/users/login' + ? Response.json({ status: 'success', token: 'immediately-invalid-token' }) + : new Response('not found', { status: 404 }), + }); + + try { + await expect(login(server.url.toString(), 'admin', 'password')) + .rejects.toMatchObject({ code: '1004' }); + } finally { + server.stop(); + } + }); + + test('keeps user and server details best-effort after a valid login', async () => { + const server = createMockServer({ + serverConfig: () => new Response('unavailable', { status: 503 }), + fallback: (_req, url) => url.pathname === '/api.php/v2/users/login' + ? Response.json({ status: 'success', token: 'valid-token' }) + : new Response('not found', { status: 404 }), + }); + + try { + await expect(login(server.url.toString(), 'admin', 'password')).resolves.toEqual({ + token: 'valid-token', + user: undefined, + serverConfig: undefined, + }); + } finally { + server.stop(); + } + }); +}); + describe('verifyToken', () => { test('返回 serverConfig 和匹配账号的 user', async () => { const server = createMockServer({