fix(web): exempt /register from auth guard so bootstrap admin can self-register

The +layout.svelte auth guard only treated /login as public, so any
unauthenticated visit to /register bounced to /login?returnTo=%2Fregister.
The "Register" link on the login page therefore looped back to itself,
making the first-user-becomes-admin bootstrap path (handleRegister +
CreateUserFirstAdminRace) unreachable in production.

Extracted isPublicRoute() into web/src/lib/auth/publicRoutes.ts so the
public-route set has one source of truth and is unit-testable. Test file
explicitly comment-tags /register as a #376 regression guard.

Closes the last gap in user-mgmt umbrella #376.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-08 17:59:43 -04:00
parent 3f2822dfc6
commit c5dc3bd256
3 changed files with 46 additions and 3 deletions
+4 -3
View File
@@ -5,6 +5,7 @@
import { QueryClientProvider } from '@tanstack/svelte-query';
import { queryClient } from '$lib/query/client';
import { user } from '$lib/auth/store.svelte';
import { isPublicRoute } from '$lib/auth/publicRoutes';
import Shell from '$lib/components/Shell.svelte';
import QueueDrawer from '$lib/components/QueueDrawer.svelte';
import ToastHost from '$lib/components/ToastHost.svelte';
@@ -27,11 +28,11 @@
// Reactive guard: runs every time page.url or user.value changes.
$effect(() => {
const path = page.url.pathname;
const isLogin = path === '/login';
if (user.value === null && !isLogin) {
const isPublic = isPublicRoute(path);
if (user.value === null && !isPublic) {
const returnTo = path + page.url.search;
goto('/login?returnTo=' + encodeURIComponent(returnTo), { replaceState: true });
} else if (user.value !== null && isLogin) {
} else if (user.value !== null && isPublic) {
goto('/', { replaceState: true });
}
});