mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
fix(panel): the /tg surface is exempt from the global 401→login redirect (#600)
The app-level agent-roster sync fires /api/agents on every surface; on /tg it races the initData sign-in, takes the cloud-auth 401, and the interceptor bounced the Telegram webview to the password /login page it cannot complete — hijacking the cockpit into the dashboard. The Mini App owns its auth UX (initData sign-in + its own wall), so the redirect now exempts /tg via an exported, tested path predicate. Co-authored-by: Renn F <rennf93@users.noreply.github.com>
This commit is contained in:
@@ -30,7 +30,7 @@ vi.mock("@/store/rate-limit-store", () => ({
|
||||
// ---------------------------------------------------------------------------
|
||||
// Import the function under test AFTER mocks are in place
|
||||
// ---------------------------------------------------------------------------
|
||||
import { getErrorMessage } from "@/lib/api/client";
|
||||
import { getErrorMessage, isTgSurfacePath } from "@/lib/api/client";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
@@ -212,3 +212,17 @@ describe("getErrorMessage — non-Axios fallbacks", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isTgSurfacePath — the /tg login-redirect exemption", () => {
|
||||
it("matches the cockpit root and subpaths", () => {
|
||||
expect(isTgSurfacePath("/tg")).toBe(true);
|
||||
expect(isTgSurfacePath("/tg/")).toBe(true);
|
||||
});
|
||||
|
||||
it("does not match the dashboard or lookalike segments", () => {
|
||||
expect(isTgSurfacePath("/overview")).toBe(false);
|
||||
expect(isTgSurfacePath("/tgsomething")).toBe(false);
|
||||
expect(isTgSurfacePath("/login")).toBe(false);
|
||||
expect(isTgSurfacePath("/")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -61,6 +61,16 @@ api.interceptors.request.use(
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* The Telegram Mini App owns its auth UX: /tg signs in via initData and
|
||||
* renders its own wall. Global mounts (the agent-roster sync) can 401 there
|
||||
* before that sign-in lands — bouncing the webview to the password /login
|
||||
* page it cannot complete would hijack the cockpit.
|
||||
*/
|
||||
export function isTgSurfacePath(pathname: string): boolean {
|
||||
return /^\/tg(\/|$)/.test(pathname);
|
||||
}
|
||||
|
||||
// A 401 only means "log in" when cloud auth is actually on. In header-trust /
|
||||
// secure mode (cloud auth off) a 401 is a misconfigured agent token, not a
|
||||
// missing session — bouncing to /login would dead-end on a page whose backend
|
||||
@@ -70,6 +80,9 @@ async function redirectToLoginIfCloudAuth(): Promise<void> {
|
||||
if (typeof window === "undefined" || window.location.pathname === "/login") {
|
||||
return;
|
||||
}
|
||||
if (isTgSurfacePath(window.location.pathname)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await fetch(`${API_URL}/auth/status`, {
|
||||
credentials: "include",
|
||||
|
||||
Reference in New Issue
Block a user