fix(auth): harden OIDC browser transactions

This commit is contained in:
2026-08-17 06:18:49 +02:00
parent 4fe51cbeb1
commit 8573500121
9 changed files with 384 additions and 59 deletions
+37
View File
@@ -84,6 +84,43 @@ describe("LoginPage", () => {
expect(screen.getByRole("button", { name: /single sign-on/i })).toBeEnabled();
});
test("routes every rendered SSO affordance through the held logout barrier", async () => {
let releaseLogout!: () => void;
let logoutStarted!: () => void;
const logoutGate = new Promise<void>((resolve) => { releaseLogout = resolve; });
const logoutRequest = new Promise<void>((resolve) => { logoutStarted = resolve; });
server.use(http.post("/api/auth/logout", async () => {
logoutStarted();
await logoutGate;
return new HttpResponse(null, { status: 204 });
}));
setAuthState({ ...authenticated, subject: "user-a" });
const logoutPromise = authApi.logout();
await logoutRequest;
const navigate = vi.fn();
const beginOidcLogin = authApi.beginOidcLogin;
const begin = vi.spyOn(authApi, "beginOidcLogin")
.mockImplementation(() => beginOidcLogin(navigate));
try {
render(<LoginPage config={oidcConfig} onAuthenticated={vi.fn()} />);
const buttons = screen.getAllByRole("button", { name: /single sign-on/i });
expect(screen.queryAllByRole("link", { name: /single sign-on/i })).toHaveLength(0);
expect(buttons).toHaveLength(1);
await userEvent.click(buttons[0]);
await Promise.resolve();
expect(begin).toHaveBeenCalledOnce();
expect(navigate).not.toHaveBeenCalled();
releaseLogout();
await Promise.all([logoutPromise, vi.waitFor(() => expect(navigate).toHaveBeenCalledWith("/api/auth/oidc/login"))]);
} finally {
releaseLogout();
begin.mockRestore();
}
});
test("does not dispatch local login until an in-flight logout response settles", async () => {
let releaseLogout!: () => void;
let logoutStarted!: () => void;
-10
View File
@@ -150,16 +150,6 @@ export function LoginPage({ config, onAuthenticated, onRetry }: LoginPageProps)
</Button>
)}
{config.oidcLogin && (
<a
href="/api/auth/oidc/login"
className="mt-4 inline-flex h-10 w-full items-center justify-center gap-2 rounded-md border border-border bg-card px-4 text-sm font-semibold shadow-xs outline-none transition-colors hover:bg-muted focus-visible:ring-3 focus-visible:ring-ring/25"
>
Continue with single sign-on
<ArrowRight aria-hidden="true" className="size-4" />
</a>
)}
{!localLogin && !config.oidcLogin && (
<p role="status" className="rounded-md border border-border bg-muted/40 p-3 text-sm text-muted-foreground">
No browser sign-in method is enabled for this installation.