Skip to content

fix: enforce tenant data isolation and centralize auth#13

Merged
mindsers merged 2 commits intomainfrom
fix/tenant-data-scoping
Apr 10, 2026
Merged

fix: enforce tenant data isolation and centralize auth#13
mindsers merged 2 commits intomainfrom
fix/tenant-data-scoping

Conversation

@mindsers
Copy link
Copy Markdown
Contributor

Summary

  • Subdomain validation: unauthenticated routes (login, password-forgot, password-reset) now validate the subdomain and redirect to /congregation-not-found for unknown subdomains
  • Data scoping fix: the Prisma 7 pg adapter breaks AsyncLocalStorage after awaited queries, causing the scoped db client to silently return unscoped data. The extension now throws on missing context (fail-safe) and re-enters context after each query
  • authenticateAndAuthorize() wrapper: replaces the fragile 3-step pattern (verifySession + verifyRole + restoreCongregationContext) across 83 route files with a single function call. Returns a can(Role) function for cleaner permission checks. Role checks run via Promise.all (faster than sequential)
  • Fallback for concurrent loaders: module-level fallback in the db extension handles cases where React Router runs parallel loaders that break each other's ALS context
  • Code comments in English: translated all French comments introduced in this branch

Builds on #11 which handled session/login tenant validation.

Test plan

  • Log into congregation A, navigate to congregation B → see B's login page (session destroyed)
  • On congregation B's login, use congregation A's credentials → "Email ou mot de passe invalide"
  • Visit unknown subdomain → "Assemblée non trouvée" error page
  • Log into congregation A → see only congregation A's data (users, territories, publishers)
  • Navigate all pages — no context errors in console
  • Single-tenant mode (MULTI_TENANT unset) → no behavioral change
  • pnpm test:unit — 321 tests pass
  • pnpm test:typecheck — clean
  • pnpm test:lint — clean

The Prisma 7 pg adapter breaks AsyncLocalStorage context after awaited
queries. enterWith() called inside verifyRole/verifySession does not
propagate back to the caller across the await boundary.

- Add restoreCongregationContext() helper to db.server.ts
- Call it in all 76 route loaders/actions after verifySession/verifyRole
  and before the first db or service function call
- Covers both direct db usage and indirect usage via service functions
Replace the fragile 3-step pattern (verifySession + verifyRole +
restoreCongregationContext) across 83 route files with a single
authenticateAndAuthorize() call that handles everything internally.

- Create auth.server.ts with authenticateAndAuthorize(request, roles)
  that combines auth, role checks (via Promise.all), and ALS restore
- Return a can(Role) function for cleaner permission checks
- Add module-level fallback in db extension for concurrent loaders
  where React Router runs parallel loaders that break each other's ALS
- Translate all French code comments to English
- Remove direct verifySession/verifyRole/restoreCongregationContext
  imports from all route files
@mindsers mindsers force-pushed the fix/tenant-data-scoping branch from c5a7182 to ed983af Compare April 10, 2026 10:00
@mindsers mindsers merged commit 5451fda into main Apr 10, 2026
4 checks passed
@mindsers mindsers deleted the fix/tenant-data-scoping branch April 10, 2026 10:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant