---
type: "query"
date: "2026-04-21T15:08:17.550855+00:00"
question: "show the auth flow"
contributor: "graphify"
source_nodes: ["auth_login", "auth_logout", "apps_web_src_lib_auth_ts", "claudemd_auth_architecture", "claudemd_rp_initiated_logout"]
---

# Q: show the auth flow

## Answer

Auth is Option B (GoTrue as OIDC proxy for Keycloak). Login (apps/web/src/lib/auth.ts:L20) calls getSupabase() and runs Supabase PKCE signInWithOAuth(provider=keycloak); Keycloak returns to /auth/callback; GoTrue exchanges the code for a Supabase JWT kept in localStorage. Logout (apps/web/src/lib/auth.ts:L50) is RP-initiated: signOut() then window.location.assign to Keycloak end_session with post_logout_redirect_uri=/logged-out; a module isLoggingOut flag suppresses the (app)/+layout.svelte $effect so signOut doesn't race with login() and clobber the PKCE verifier. /logged-out is a public route outside the (app) group. Two prod bugs shaped the flow: Bug 1 (empty auth.users.role on self-registration) fixed by migrations 013+014 (INSERT + UPDATE triggers); Bug 2 (logout didn't log out + PKCE race) fixed by RP-initiated logout. Covered by apps/web/tests/e2e/auth.test.ts A-05 + A-07. Related gotchas in the same cluster: onAuthStateChange query deadlock (defer with setTimeout+pass-through auth.lock), INITIAL_SESSION fires on refresh not SIGNED_IN, don't gate auth in SvelteKit load(), auth.users pre-seeded with Keycloak UUIDs, Playwright uses live Keycloak login not storageState.

## Source Nodes

- auth_login
- auth_logout
- apps_web_src_lib_auth_ts
- claudemd_auth_architecture
- claudemd_rp_initiated_logout