fix(workspace): break NoAccessPage redirect loop by clearing stale cookie

The web proxy redirects / to /<lastSlug>/issues based on the
last_workspace_slug cookie alone, with no access check. When a user
gets evicted from a workspace, the cookie still points at it; clicking
"Go to my workspaces" then loops: NoAccessPage -> / -> proxy ->
same bad slug -> NoAccessPage.

Clear the cookie on mount so the proxy falls through to the landing
page, which resolves the correct destination via the workspace list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Naiyuan Qing
2026-05-06 13:56:04 +08:00
parent d28298cda9
commit 931afa05cf
2 changed files with 24 additions and 0 deletions

View File

@@ -32,6 +32,16 @@ describe("NoAccessPage", () => {
expect(navigate).toHaveBeenCalledWith("/");
});
it("clears last_workspace_slug cookie on mount so the proxy stops looping us back", () => {
document.cookie = "last_workspace_slug=stale; path=/";
render(<NoAccessPage />);
// Assert empty value, not just absence of "stale" — the proxy reads any
// truthy value as a redirect target, so a buggy clear that left e.g.
// `last_workspace_slug=other` would still trap users.
const value = document.cookie.match(/last_workspace_slug=([^;]*)/)?.[1];
expect(value ?? "").toBe("");
});
it("fully logs out on 'Sign in as a different user' instead of just navigating", () => {
render(<NoAccessPage />);
fireEvent.click(

View File

@@ -1,5 +1,6 @@
"use client";
import { useEffect } from "react";
import { Button } from "@multica/ui/components/ui/button";
import { paths } from "@multica/core/paths";
import { useNavigation } from "../navigation";
@@ -15,6 +16,19 @@ import { DragStrip } from "../platform";
export function NoAccessPage() {
const nav = useNavigation();
const logout = useLogout();
// Clear stale `last_workspace_slug` cookie. The web proxy redirects `/` to
// `/<lastSlug>/issues` based on this cookie alone (no access check). When
// the cookie points at a workspace the user has just lost access to, the
// user gets trapped in a loop: NoAccessPage → click "Go to my workspaces"
// → `/` → proxy redirects back to the same bad slug → NoAccessPage.
// Clearing the cookie here lets the proxy fall through to the landing page,
// which then resolves the correct destination via the workspace list.
// No-op outside the browser (desktop renderer also has document, harmless).
useEffect(() => {
if (typeof document === "undefined") return;
document.cookie = "last_workspace_slug=; path=/; max-age=0; SameSite=Lax";
}, []);
return (
<div className="flex min-h-svh flex-col">
<DragStrip />