fix(auth): move onboarding redirect from AuthShell useEffect to proxy

The post-hydration redirect for users with `onboardingCompletedAt ===
null` caused a brief dashboard flash before the client-side effect
could fire. The redirect now lives in `src/proxy.ts` so it lands on the
first server response.

The proxy runs in the Edge runtime and can't reach Prisma, so the auth
routes (login / passkey-verify / register / password / /api/auth/me)
mirror the DB `onboardingCompletedAt` column into a non-httpOnly
`hl_onboarding` cookie. The cookie is a UX hint, not a security
signal — a user editing it locally only skips the dashboard flash;
they still can't bypass any server check. `/api/onboarding/complete`
clears the cookie so the next navigation drops the redirect
immediately, and `destroySession` + expired-session purge wipe it
alongside the session cookie.

The e2e onboarding-flicker spec can return to testing the actual
incomplete-onboarding case: the proxy reads the cookie (absent for
the fully-onboarded storage-state user), not the mocked `/api/auth/me`
response, so the test now sets `onboardingCompletedAt: null` honestly
instead of pinning it to a non-null value as a workaround.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marc-André BombeckandClaude Opus 4.7 committed 2026-05-10 21:29:17 +02:00
1 parent b281c0655b
commit 15796c148b
14 files changed
+208 -32

No files matched your search

+6 -20
View File
@@ -41,26 +41,12 @@ export function AuthShell({ children }: { children: React.ReactNode }) {
}
}, [isLoading, isAuthenticated, isAdminPage, user?.role, router]);
// Redirect to onboarding if not completed
useEffect(() => {
if (
!isLoading &&
isAuthenticated &&
!isOnboardingPage &&
!isPublicPage &&
user &&
!user.onboardingCompletedAt
) {
router.replace("/onboarding");
}
}, [
isLoading,
isAuthenticated,
isOnboardingPage,
isPublicPage,
user,
router,
]);
// v1.4.22 C4 — onboarding redirect moved to proxy.ts so it lands on
// the first server response instead of post-hydration. The proxy
// reads the `hl_onboarding` cookie (mirrored from the DB by the auth
// routes) and 307s to /onboarding before this shell ever paints. The
// previous `useEffect`-based redirect caused a brief dashboard flash
// for users with `onboardingCompletedAt === null`.
// Loading state
if (isLoading) {