Supabase Auth Configuration — Redirect URL Allowlist
Project: wrwkszmobuhvcfjipasi (ChurchWiseAI — shared instance for all properties)
Dashboard: https://supabase.com/dashboard/project/wrwkszmobuhvcfjipasi/auth/url-configuration
Site URL (primary fallback): https://churchwiseai.com
IMPORTANT: This file IS the source of truth. If you change the Supabase dashboard, update this file in the same session. If this file and the dashboard disagree, the dashboard is authoritative but the file must be corrected immediately.
Auth Flow for User-Facing Apps
Canonical Redirect URL Allowlist
29 entries as of 2026-03-26 → target: 18 entries after cleanup (11 stale entries to remove).
✅ Correct and Active
# ChurchWiseAI (churchwiseai.com)
https://churchwiseai.com/**
https://www.churchwiseai.com/**
https://*.churchwiseai.com/**
https://churchwiseai.com/auth/callback
https://www.churchwiseai.com/auth/callback
https://churchwiseai.com/sermons/auth/callback
# Vercel preview deployments
https://*.vercel.app/**
# IllustrateTheWord (illustratetheword.com) — uses Supabase Auth (email + OAuth)
https://illustratetheword.com/**
https://www.illustratetheword.com/**
https://illustratetheword.com/auth/callback
https://www.illustratetheword.com/auth/callback
# SermonWise (sermonwise.ai) — shares CWA Supabase instance, routes at /sermons
https://sermonwise.ai/**
https://www.sermonwise.ai/**
# ShareWiseAI (sharewiseai.com) — Coming Soon, OAuth pending
https://sharewiseai.com/**
https://www.sharewiseai.com/**
https://sharewiseai.com/auth/callback
https://www.sharewiseai.com/auth/callback
# Local development
http://localhost:*/auth/callback
Total: 19 entries
❌ Stale Entries to Remove from Dashboard
These exist in the dashboard but should be removed. None of these routes or properties use Supabase Auth:
| Entry | Reason to remove |
|---|---|
https://churchwiseai.com/platform/auth/callback | Route /platform does not exist |
https://churchwiseai.com/admin/auth/callback | Admin uses token-based auth, not Supabase |
https://churchwiseai.com/teaching/auth/callback | Route /teaching does not exist |
https://churchwiseai.com/study/auth/callback | Route /study does not exist |
https://churchwiseai.com/devotions/auth/callback | Route /devotions does not exist |
https://churchwiseai.com/goodnews/auth/callback | Route /goodnews does not exist |
https://churchwiseai.com/chatbot/auth/callback | Chatbot uses token auth, not Supabase |
https://admin.churchwiseai.com/auth/callback | No admin subdomain; *.churchwiseai.com/** covers it |
https://pewsearch.com/auth/callback | PewSearch uses token auth only, no Supabase Auth |
https://www.pewsearch.com/auth/callback | Same |
https://www.pewsearch.com/** | Same |
⚠️ Wrong Wildcards to Fix
| Current | Correct | Why |
|---|---|---|
https://sharewiseai.com/* | https://sharewiseai.com/** | /* doesn't match nested paths like /social/app/calendar |
https://www.sharewiseai.com/* | https://www.sharewiseai.com/** | Same |
Which Properties Use Supabase Auth
| Property | Uses Supabase Auth? | Auth flow |
|---|---|---|
| churchwiseai.com | Partial | SermonWise only (/sermons/*). Main CWA admin uses token-based auth. |
| sermonwise.ai | Yes | Email signup/login at /sermons/login, /sermons/signup |
| illustratetheword.com | Yes | Email signup/login at /signin, /signup; OAuth via /auth/callback |
| sharewiseai.com | Yes (Coming Soon) | Will use Supabase Auth when OAuth goes live |
| pewsearch.com | No | Token-based only (/admin/[token]) |
Adding New Redirect URLs
When a new property or auth flow is added:
- Add the URL(s) to the Supabase dashboard
- Add to the Correct and Active list above
- Update
last-verifieddate - Run the knowledge integrity check:
cd C:\dev\knowledge && pnpm test
Pattern rules:
- Always use
/**(not/*) for app domains — catches nested paths - Add both
domain.comandwww.domain.comvariants - Specific
/auth/callbackentries are redundant when/**is present, but harmless and kept for clarity - Never add entries for properties that don't use Supabase Auth
Email Template Note
The confirmation email subject currently says "Welcome to ChurchwiseAI" for all properties — this is a shared Supabase instance. Consider updating the template to use {{ .SiteURL }} for dynamic branding:
Dashboard: Authentication → Email Templates → Confirm signup
This is cosmetic (non-blocking) but affects ITW and SermonWise users who see the wrong brand name in their confirmation email.
PKCE Callback Failure Handling — flow=signup flag
PKCE code exchange in /auth/callback can fail for benign reasons (the code_verifier cookie isn't present in the browser context that opens the email link — common when the user signs up in Chrome profile A but their default email client opens links in profile B, or in an in-app browser like Gmail iOS). When this happens, the user's email IS already confirmed at Supabase verify time — only the session-establishment step fails. They just need to sign in.
To deliver an honest message in that case without re-introducing the misleading "Email confirmed!" banner that PR #327 (2026-05-06) removed for OAuth login failures, SignupForm.tsx stamps &flow=signup on the redirect URL passed to Supabase. The callback reads it on failure:
| Path | flow param | Failure redirect | Banner shown |
|---|---|---|---|
| Email signup confirmation, code exchange fails | signup | /sermons/login?confirmed=1 | Green: "Email confirmed! Sign in to continue." |
| OAuth login (Google), code exchange fails | absent | /sermons/login?error=auth_callback_failed | Red: "Something went wrong during sign-in." |
This works because the email-verify step at auth.churchwiseai.com/auth/v1/verify runs before Supabase issues the redirect to /auth/callback?code=... — email_confirmed_at is already set in auth.users by the time we attempt the PKCE exchange. Telling the user "Email confirmed! Sign in to continue." after that is factually correct.
OAuth-from-signup-page also carries flow=signup (signup intent), and the same banner is shown — Google OAuth identities are pre-confirmed too, so the wording remains accurate.
The confirmed=1 URL param is consumed by SigninForm.tsx, which also strips it from window.history after first render so the banner doesn't resurrect on back-button navigation.