2 Commits

Author SHA1 Message Date
ca12516005 fix(auth): RP-initiated Keycloak logout + PKCE verifier race
Clicking logout previously only called supabase.auth.signOut(): Keycloak's
SSO cookie was untouched, so the (app) layout's $effect immediately
re-fired login() and Keycloak silently re-authenticated — logout looked
broken. That same chain also surfaced "PKCE code verifier not found in
storage" on prod, since signOut()'s async storage clear could race
signInWithOAuth()'s verifier write, or the $effect could double-fire and
overwrite verifiers mid-flight.

- logout(): signOut() then redirect to Keycloak's end_session endpoint
  (client_id + post_logout_redirect_uri=/logged-out). Keycloak shows a
  one-click "Do you want to log out?" confirmation because GoTrue's proxy
  flow does not expose the id_token, so we can't pass id_token_hint.
- isLoggingOut flag + guard in (app) layout $effect: prevents auto-relogin
  during the logout navigation.
- New public /logged-out route, outside the (app) group.
- A-05 rewritten, new A-07 (fresh login must prompt for credentials after
  RP-initiated logout).
- pgTAP 008 extended with 4 assertions for migration 014's UPDATE trigger.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 09:35:17 +02:00
2294a31942 fix(auth): default empty role/aud on auth.users → 'authenticated'
Found on ambrosio after the first real self-registration: GoTrue v2.158.1
persists OIDC-provisioned users with empty-string role and aud. PostgREST
then dies with `role "" does not exist` on every REST call because its
per-request `SET LOCAL role = $claim` can't resolve ''. Dev never caught
this because seed.sql hardcodes both columns.

- supabase/migrations/013_auth_users_role_default.sql: backfill existing
  rows + BEFORE INSERT trigger on auth.users that fills empty/NULL role +
  aud with 'authenticated'. Idempotent, preserves explicit values.
- supabase/tests/008_auth_user_role_default.sql: pgTAP coverage — function
  + trigger exist, backfill left nothing empty, empty-string and NULL
  values get defaulted, explicit non-empty role is preserved.
- CLAUDE.md: status line updated to 255 tests (was 248), new gotcha #19
  documenting the GoTrue quirk + that users in existing sessions must
  re-login once for a fresh JWT after the fix lands on any given env.

Total: 41 pgTAP (was 34) + 140 integration + 15 unit + 58 Playwright + 1
gated rate-limit. Full `just test-all` green.
2026-04-15 00:23:00 +02:00