diff --git a/web/src/api/client.test.ts b/web/src/api/client.test.ts index 4417154..9f8070e 100644 --- a/web/src/api/client.test.ts +++ b/web/src/api/client.test.ts @@ -41,6 +41,13 @@ describe("api", () => { expect(fetch).not.toHaveBeenCalled(); }); + it("токен с недопустимыми символами — AuthError без запроса", async () => { + setToken("ток ен"); + const fetch = mockFetch(() => json(200, {})); + await expect(api.get("/board")).rejects.toBeInstanceOf(AuthError); + expect(fetch).not.toHaveBeenCalled(); + }); + it("checkToken проверяет переданный токен, не сохранённый", async () => { const fetch = mockFetch(() => json(200, { kind_labels: {} })); await checkToken("candidate"); diff --git a/web/src/api/client.ts b/web/src/api/client.ts index 9c796d5..8ea73d6 100644 --- a/web/src/api/client.ts +++ b/web/src/api/client.ts @@ -1,6 +1,6 @@ // единственный клиент HTTP API хаба: тот же хост, клиентский токен в Authorization, ответы — JSON. // Ошибки: AuthError — 401 или нет токена; HubDownError — сеть или 5xx; ApiError — прочие 4xx с {error, detail} -import { getToken } from "../auth/token.ts"; +import { getToken, TOKEN_RE } from "../auth/token.ts"; import type { HubSettings } from "./types.ts"; export class AuthError extends Error {} @@ -29,6 +29,8 @@ function withParams(path: string, params?: Params): string { async function call(method: string, path: string, body?: unknown, token = getToken()): Promise { if (!token) throw new AuthError("нет токена"); + // токен с недопустимыми символами (например, оставленный прежней доской) — сразу на экран входа, а не «хаб не отвечает» + if (!TOKEN_RE.test(token)) throw new AuthError("недопустимые символы в токене"); const headers: Record = { Authorization: "Bearer " + token, Accept: "application/json" }; if (body !== undefined) headers["Content-Type"] = "application/json; charset=utf-8"; let resp: Response;