Skip to content

Add Supabase database schema and configuration#1

Open
JTVOLPS wants to merge 1 commit into
claude/real-estate-crm-system-rBWhzfrom
claude/setup-local-dev-LwWzy
Open

Add Supabase database schema and configuration#1
JTVOLPS wants to merge 1 commit into
claude/real-estate-crm-system-rBWhzfrom
claude/setup-local-dev-LwWzy

Conversation

@JTVOLPS

@JTVOLPS JTVOLPS commented Apr 10, 2026

Copy link
Copy Markdown
Owner

Summary

This PR introduces the complete Supabase database schema and local development configuration for the real estate deal management platform. It consolidates all migration files into a single comprehensive schema file and adds Supabase CLI configuration.

Key Changes

  • Consolidated Migration File: Created supabase/all_migrations.sql containing all 24 migrations in a single file for easier management and deployment

  • Supabase Configuration: Added supabase/config.toml with complete local development setup including:

    • API configuration (port 54321, schema exposure)
    • Database configuration (PostgreSQL 17, port 54322)
    • Auth settings (JWT expiry, signup/email configuration)
    • Storage configuration (50MB file size limit, S3 protocol support)
    • Studio and Inbucket (email testing) configuration
  • Database Schema: Comprehensive schema includes:

    • Core tables: organizations, user profiles, invitations
    • Contact management: contacts, companies, contact-company relationships
    • Property management: properties with geospatial indexing
    • Deal management: deals, deal economics, deal contacts, stage history, custom field definitions
    • Future modules: documents, tasks (schema only, no Phase 1 UI)
    • Audit logging: audit_log table for tracking changes
    • Search infrastructure: tsvector columns, trigram indexes, global_search function
  • Security & Access Control:

    • Row-level security (RLS) policies on all tables
    • Organization-scoped data isolation via auth.org_id() helper function
    • Role-based access control (owner, admin, member) via auth.user_role() helper
    • Proper foreign key constraints with cascade delete
  • Search & Indexing:

    • Full-text search vectors for contacts, companies, deals, properties
    • Trigram (gin_trgm_ops) indexes for fuzzy matching
    • Global search function combining all searchable entities
    • Automatic search vector updates via triggers
  • Data Integrity:

    • Automatic updated_at timestamp management via triggers
    • Predefined custom field definitions for all asset classes (IOS, Marina, Hospitality, Multifamily, Transitional)
    • Comprehensive check constraints on enum-like fields
  • Git Configuration: Added .gitignore for Supabase local development artifacts

  • Documentation: Updated README.md with simplified setup instructions referencing the consolidated migration file

Notable Implementation Details

  • All migrations are timestamped with 20240101000001 prefix for proper ordering
  • RLS policies use organization context from JWT claims for multi-tenant isolation
  • Search functionality combines full-text search (tsvector) with fuzzy matching (trigram similarity)
  • Deal field definitions are asset-class-specific and globally accessible
  • Audit logging includes both extension-based (commented) and fallback table-based approaches
  • Storage bucket configuration ready for document management in future phases

https://claude.ai/code/session_01JGosDHvo9xTpimALDPkZFn

Summary by CodeRabbit

  • Documentation

    • Simplified Supabase setup instructions with consolidated migration approach and added CLI-based alternative workflow
  • New Features

    • Initialized comprehensive database schema supporting multi-tenant operations, full-text and similarity search, CRM functionality, deal and lease tracking, portfolio performance analytics, and investor relationship management
  • Chores

    • Added Supabase configuration and environment variable management files

- Initialize Supabase CLI config (config.toml)
- Rename migration files to Supabase CLI timestamp format for compatibility
- Add combined all_migrations.sql for easy one-step DB setup via SQL Editor
- Update README with simplified setup instructions

https://claude.ai/code/session_01JGosDHvo9xTpimALDPkZFn
@coderabbitai

coderabbitai Bot commented Apr 10, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Introduced comprehensive Supabase database infrastructure with a complete schema migration file, configuration settings for local Supabase development, associated ignore patterns, and updated documentation. The migration establishes multi-tenant support, full-text search, row-level security policies, analytics functions, and core business entities across organizational, CRM, deal, tenant, and financial domains.

Changes

Cohort / File(s) Summary
Documentation
README.md
Updated Supabase setup instructions to reference a consolidated migration file and document automatic pg_trgm extension enablement. Added alternative CLI-based workflow using npx supabase start.
Configuration & Ignore Patterns
supabase/config.toml, supabase/.gitignore
Added local Supabase development configuration with ports, authentication, storage, and subsystem settings. Added ignore patterns for temporary/environment files within the Supabase directory.
Database Migrations
supabase/all_migrations.sql
Comprehensive schema migration enabling pg_trgm, defining auth helper functions, establishing 25+ tables with multi-tenant organization scoping, RLS policies, search infrastructure with full-text and trigram matching, trigger-based tsvector updates, KPI analytics functions, document storage integration, task management, tenant/lease operations, debt instruments, and investor waterfall tracking.

Poem

🐰 A burrow of tables now dug with care,
With indexes and triggers beyond compare,
RLS guards the secrets tight,
While search hops left and right,
Migrations hop in—the schema's all there! ✨

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Add Supabase database schema and configuration' accurately and concisely summarizes the main change: introducing a complete Supabase database schema and local configuration setup.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/setup-local-dev-LwWzy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
91.2% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@supabase/all_migrations.sql`:
- Around line 1099-1105: The computed column total_monthly_rent currently uses
coalesce(l.rent_amount, 0) + coalesce(l.cam_charges, 0) without adjusting for
lease frequency; update the SELECT expression that defines total_monthly_rent
(referencing l.rent_amount, l.cam_charges, and l.rent_frequency) to normalize
amounts to a monthly basis (e.g., divide by 3 for quarterly, by 12 for annual,
leave monthly as-is) using a CASE or similar expression and keep COALESCE around
rent_amount and cam_charges to avoid NULLs so the result correctly represents
monthly rent.
- Around line 631-639: The policies cc_select, cc_insert, cc_update, and
cc_delete on public.contact_companies only validate contacts.org_id; update each
policy to validate both sides of the junction by checking that the contact row
and the company row both belong to the caller's org (i.e., ensure exists(...)
for public.contacts where contacts.id = contact_id and contacts.org_id =
auth.org_id() AND exists(...) for public.companies where companies.id =
company_id and companies.org_id = auth.org_id()). Apply the same dual-side
validation to the other similar junction policy block referenced (lines
685-691).
- Around line 1219-1228: The query double-counts p.total_sf because properties
are joined to multiple active tenants; fix by aggregating per property before
summing: compute occupied square footage per property (e.g., sum(t.occupied_sf)
grouped by t.property_id or use max per property) and join that per-property
aggregate to public.properties so you sum each p.total_sf only once (or use
sum(distinct p.total_sf) if appropriate); update the SELECT block that
references public.properties p and public.tenants t so the denominator uses the
deduplicated property totals and the numerator uses the per-property occupied
aggregate (referencing p.total_sf, t.occupied_sf, and t.property_id to locate
the logic).
- Around line 880-886: The triggers (e.g., set_tenants_updated_at,
set_leases_updated_at) call a non-existent function public.set_updated_at();
replace those calls so they execute the actual function defined earlier,
public.update_updated_at(), i.e., change "execute function
public.set_updated_at()" to "execute function public.update_updated_at()" for
these triggers and for every other trigger in the script that currently
references public.set_updated_at().
- Around line 699-706: The policies "dfd_insert" and "dfd_update" on
public.deal_field_definitions currently allow org-level roles (auth.user_role()
in ('owner','admin')) to modify a global table; change those policies to
restrict writes to a system-level admin identity instead (e.g., replace the
condition with a system-only check such as auth.is_system_admin() OR
auth.user_role() = 'system_admin' depending on your auth helpers) so only
platform/system admins can INSERT/UPDATE the global deal_field_definitions while
leaving "dfd_select" unchanged; update the policy definitions named "dfd_insert"
and "dfd_update" accordingly and audit other global definition tables for
similar fixes.
- Around line 737-759: The audit table as created (public.audit_log) is writable
by any authenticated user via the "audit_insert" policy and has no triggers, so
it can be spoofed or left empty; fix by removing or tightening the
"audit_insert" policy so ordinary users cannot directly INSERT (e.g., allow only
auth.role() = 'service_role' or remove the policy entirely), and add a secure
trigger/function pair that writes canonical audit entries: implement a plpgsql
function (e.g., function audit.log_change() or audit_log_record()) that ON
INSERT/UPDATE/DELETE on target tables inserts into public.audit_log (capturing
action, table_name, record_id, old_data, new_data, user id from current_setting
or auth context), then create triggers (FOR EACH ROW) on the relevant tables to
call that function; ensure triggers run with a privileged role or rely on the
service role so only the trigger can populate public.audit_log and regular
clients cannot INSERT directly.

In `@supabase/config.toml`:
- Around line 154-156: Replace the IP-based and mismatched-scheme values in the
Supabase config by updating the site_url and additional_redirect_urls entries:
change site_url from "http://127.0.0.1:3000" to "http://localhost:3000" and
change additional_redirect_urls to use "http://localhost:3000" (remove the
https/127.0.0.1 variant) so Supabase Auth accepts the local redirect and the
scheme matches.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1d262159-c10b-418c-9d36-52b786932c57

📥 Commits

Reviewing files that changed from the base of the PR and between 51c9701 and 736b64c.

📒 Files selected for processing (28)
  • README.md
  • supabase/.gitignore
  • supabase/all_migrations.sql
  • supabase/config.toml
  • supabase/migrations/20240101000001_extensions_and_functions.sql
  • supabase/migrations/20240101000002_core_tables.sql
  • supabase/migrations/20240101000003_contacts.sql
  • supabase/migrations/20240101000004_properties.sql
  • supabase/migrations/20240101000005_deals.sql
  • supabase/migrations/20240101000006_future_schema.sql
  • supabase/migrations/20240101000007_search_triggers.sql
  • supabase/migrations/20240101000008_global_search.sql
  • supabase/migrations/20240101000009_rls_policies.sql
  • supabase/migrations/20240101000010_audit.sql
  • supabase/migrations/20240101000011_seed_field_definitions.sql
  • supabase/migrations/20240101000012_tenants_leases.sql
  • supabase/migrations/20240101000013_operating_statements.sql
  • supabase/migrations/20240101000014_capex_projects.sql
  • supabase/migrations/20240101000015_debt_instruments.sql
  • supabase/migrations/20240101000016_alter_documents.sql
  • supabase/migrations/20240101000017_phase2_rls.sql
  • supabase/migrations/20240101000018_views_functions.sql
  • supabase/migrations/20240101000019_storage_bucket.sql
  • supabase/migrations/20240101000020_task_enhancements.sql
  • supabase/migrations/20240101000021_portfolio_kpi_functions.sql
  • supabase/migrations/20240101000022_investors.sql
  • supabase/migrations/20240101000023_investor_rls.sql
  • supabase/migrations/20240101000024_notifications.sql

Comment on lines +631 to +639
-- Contact companies (junction -- derive from contact)
create policy "cc_select" on public.contact_companies for select
using (exists (select 1 from public.contacts where contacts.id = contact_id and contacts.org_id = auth.org_id()));
create policy "cc_insert" on public.contact_companies for insert
with check (exists (select 1 from public.contacts where contacts.id = contact_id and contacts.org_id = auth.org_id()));
create policy "cc_update" on public.contact_companies for update
using (exists (select 1 from public.contacts where contacts.id = contact_id and contacts.org_id = auth.org_id()));
create policy "cc_delete" on public.contact_companies for delete
using (exists (select 1 from public.contacts where contacts.id = contact_id and contacts.org_id = auth.org_id()));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Validate both sides of tenant-scoped junction rows.

These policies only prove ownership of one side of the relationship. A user who learns another org’s company_id or contact_id can create cross-tenant links, because the second foreign key is never checked against the caller’s org.

Also applies to: 685-691

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/all_migrations.sql` around lines 631 - 639, The policies cc_select,
cc_insert, cc_update, and cc_delete on public.contact_companies only validate
contacts.org_id; update each policy to validate both sides of the junction by
checking that the contact row and the company row both belong to the caller's
org (i.e., ensure exists(...) for public.contacts where contacts.id = contact_id
and contacts.org_id = auth.org_id() AND exists(...) for public.companies where
companies.id = company_id and companies.org_id = auth.org_id()). Apply the same
dual-side validation to the other similar junction policy block referenced
(lines 685-691).

Comment on lines +699 to +706
-- Deal field definitions (global, not org-scoped)
create policy "dfd_select" on public.deal_field_definitions for select
using (true);
create policy "dfd_insert" on public.deal_field_definitions for insert
with check (auth.user_role() in ('owner', 'admin'));
create policy "dfd_update" on public.deal_field_definitions for update
using (auth.user_role() in ('owner', 'admin'));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Don't let tenant admins write to a global definitions table.

deal_field_definitions is shared across every org, but any org owner or admin can insert/update it here. That gives one tenant write access to every tenant’s form schema.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/all_migrations.sql` around lines 699 - 706, The policies
"dfd_insert" and "dfd_update" on public.deal_field_definitions currently allow
org-level roles (auth.user_role() in ('owner','admin')) to modify a global
table; change those policies to restrict writes to a system-level admin identity
instead (e.g., replace the condition with a system-only check such as
auth.is_system_admin() OR auth.user_role() = 'system_admin' depending on your
auth helpers) so only platform/system admins can INSERT/UPDATE the global
deal_field_definitions while leaving "dfd_select" unchanged; update the policy
definitions named "dfd_insert" and "dfd_update" accordingly and audit other
global definition tables for similar fixes.

Comment on lines +737 to +759
-- Fallback: simple audit log table for tracking changes
create table public.audit_log (
id uuid primary key default gen_random_uuid(),
org_id uuid references public.orgs(id) on delete cascade,
user_id uuid references auth.users(id),
action text not null check (action in ('insert', 'update', 'delete')),
table_name text not null,
record_id uuid not null,
old_data jsonb,
new_data jsonb,
created_at timestamptz not null default now()
);

create index idx_audit_log_org on public.audit_log(org_id);
create index idx_audit_log_record on public.audit_log(table_name, record_id);
create index idx_audit_log_created on public.audit_log(created_at);

alter table public.audit_log enable row level security;

create policy "audit_select" on public.audit_log for select
using (org_id = auth.org_id());
create policy "audit_insert" on public.audit_log for insert
with check (org_id = auth.org_id());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

The fallback audit log is neither automatic nor tamper-resistant.

There are no triggers in this migration that write to public.audit_log, and audit_insert lets authenticated users insert arbitrary rows for their org. That means this table can be empty or user-fabricated, so it cannot serve as a reliable audit trail.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/all_migrations.sql` around lines 737 - 759, The audit table as
created (public.audit_log) is writable by any authenticated user via the
"audit_insert" policy and has no triggers, so it can be spoofed or left empty;
fix by removing or tightening the "audit_insert" policy so ordinary users cannot
directly INSERT (e.g., allow only auth.role() = 'service_role' or remove the
policy entirely), and add a secure trigger/function pair that writes canonical
audit entries: implement a plpgsql function (e.g., function audit.log_change()
or audit_log_record()) that ON INSERT/UPDATE/DELETE on target tables inserts
into public.audit_log (capturing action, table_name, record_id, old_data,
new_data, user id from current_setting or auth context), then create triggers
(FOR EACH ROW) on the relevant tables to call that function; ensure triggers run
with a privileged role or rely on the service role so only the trigger can
populate public.audit_log and regular clients cannot INSERT directly.

Comment on lines +880 to +886
create trigger set_tenants_updated_at
before update on public.tenants
for each row execute function public.set_updated_at();

create trigger set_leases_updated_at
before update on public.leases
for each row execute function public.set_updated_at();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

These triggers call a function that does not exist.

The migration defines update_updated_at() earlier, but every trigger here executes public.set_updated_at(). The consolidated script will stop at the first one, so the rest of the schema never gets created.

Suggested fix
-  for each row execute function public.set_updated_at();
+  for each row execute function public.update_updated_at();

Also applies to: 912-914, 945-947, 989-991, 1014-1016, 1181-1183, 1322-1324, 1350-1352, 1379-1381, 1405-1407

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/all_migrations.sql` around lines 880 - 886, The triggers (e.g.,
set_tenants_updated_at, set_leases_updated_at) call a non-existent function
public.set_updated_at(); replace those calls so they execute the actual function
defined earlier, public.update_updated_at(), i.e., change "execute function
public.set_updated_at()" to "execute function public.update_updated_at()" for
these triggers and for every other trigger in the script that currently
references public.set_updated_at().

Comment on lines +1099 to +1105
l.lease_type,
l.start_date,
l.end_date,
l.rent_amount,
l.rent_frequency,
l.cam_charges,
coalesce(l.rent_amount, 0) + coalesce(l.cam_charges, 0) as total_monthly_rent,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Normalize lease frequency before exposing total_monthly_rent.

rent_frequency allows quarterly and annual leases, but total_monthly_rent is calculated as a raw rent_amount + cam_charges. That overstates non-monthly leases and will skew rent-roll totals.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/all_migrations.sql` around lines 1099 - 1105, The computed column
total_monthly_rent currently uses coalesce(l.rent_amount, 0) +
coalesce(l.cam_charges, 0) without adjusting for lease frequency; update the
SELECT expression that defines total_monthly_rent (referencing l.rent_amount,
l.cam_charges, and l.rent_frequency) to normalize amounts to a monthly basis
(e.g., divide by 3 for quarterly, by 12 for annual, leave monthly as-is) using a
CASE or similar expression and keep COALESCE around rent_amount and cam_charges
to avoid NULLs so the result correctly represents monthly rent.

Comment on lines +1219 to +1228
select case
when coalesce(sum(p.total_sf), 0) = 0 then 0
else round(
coalesce(sum(t.occupied_sf), 0) / sum(p.total_sf) * 100,
1
)
end
from public.properties p
left join public.tenants t on t.property_id = p.id and t.status = 'active'
where p.org_id = auth.org_id();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

The occupancy denominator is duplicated per active tenant.

This query joins properties directly to tenants and then sums p.total_sf, so a property with multiple active tenants contributes its total square footage multiple times. Occupancy will be understated for multi-tenant assets.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/all_migrations.sql` around lines 1219 - 1228, The query
double-counts p.total_sf because properties are joined to multiple active
tenants; fix by aggregating per property before summing: compute occupied square
footage per property (e.g., sum(t.occupied_sf) grouped by t.property_id or use
max per property) and join that per-property aggregate to public.properties so
you sum each p.total_sf only once (or use sum(distinct p.total_sf) if
appropriate); update the SELECT block that references public.properties p and
public.tenants t so the denominator uses the deduplicated property totals and
the numerator uses the per-property occupied aggregate (referencing p.total_sf,
t.occupied_sf, and t.property_id to locate the logic).

Comment thread supabase/config.toml
Comment on lines +154 to +156
site_url = "http://127.0.0.1:3000"
# A list of *exact* URLs that auth providers are permitted to redirect to post authentication.
additional_redirect_urls = ["https://127.0.0.1:3000"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find and examine README.md to verify documented app URL
fd -t f "README" . | head -10

Repository: JTVOLPS/conca

Length of output: 68


🏁 Script executed:

# Check the supabase/config.toml file at the specified lines
cat -n supabase/config.toml | sed -n '150,160p'

Repository: JTVOLPS/conca

Length of output: 680


🏁 Script executed:

# Search for all references to localhost and 127.0.0.1 in the codebase
rg "localhost:3000|127\.0\.0\.1:3000" --type-list | head -20

Repository: JTVOLPS/conca

Length of output: 446


🏁 Script executed:

# Search for environment configuration and app URL setup
rg "NEXT_PUBLIC_APP_URL|APP_URL" --type-list | head -20

Repository: JTVOLPS/conca

Length of output: 446


🏁 Script executed:

# Read README.md to check documented app URL
cat README.md

Repository: JTVOLPS/conca

Length of output: 4919


🏁 Script executed:

# Search for NEXT_PUBLIC_APP_URL and app URL references
rg "NEXT_PUBLIC_APP_URL|localhost:3000|app.*url" -i

Repository: JTVOLPS/conca

Length of output: 2029


🏁 Script executed:

# Check for environment setup files
fd -t f "\.env|env\.example|\.env\.local" .

Repository: JTVOLPS/conca

Length of output: 77


🏁 Script executed:

# Search for any Supabase auth configuration or references to redirect matching
rg "redirect|localhost|127\.0\.0\.1" supabase/ -i

Repository: JTVOLPS/conca

Length of output: 796


🏁 Script executed:

# Check .env.local.example for complete configuration
cat .env.local.example

Repository: JTVOLPS/conca

Length of output: 407


🌐 Web query:

Supabase Auth redirect URL matching localhost vs 127.0.0.1 behavior

💡 Result:

Supabase Auth treats redirect URLs with "localhost" and "127.0.0.1" differently due to validation rules in the auth server that reject IP addresses (like 127.0.0.1) for security reasons, while allowing "localhost" for local development. Key points: - Official docs always use and recommend "http://localhost:3000/" (or similar) for local dev redirects, never 127.0.0.1. - In GitHub issue #36640 (2025), a user reported redirect issues with an IP address in redirect URLs; replacing it with "localhost" fixed it. Comment references code in supabase/auth/internal/utilities/request.go (lines 103-108) validating redirects. - PR #1984 (merged April 2025, title "fix: redirects must not be to ip addresses") explicitly enforces no IP addresses in redirects. - Reddit thread reports mismatches when app binds to 127.0.0.1 but Site URL uses localhost, causing CORS or redirect failures; solution is matching exactly, preferring localhost. - Matching must be exact (scheme, host, path); wildcards like / allowed, but host must pass validation. Behavior summary: - http://localhost:PORT/ : Allowed and works for local dev. - http://127.0.0.1:PORT/ : Rejected by validation as IP address. - Use window.location.origin carefully; if browser resolves to 127.0.0.1 but config is localhost, mismatch fails. - For self-hosted/local Supabase, same rules apply via config.toml. To avoid issues: Always configure Site URL and additional redirects with "localhost" for local dev, use dynamic origin detection that prefers localhost.

Citations:


Auth configuration blocks local development—Supabase rejects 127.0.0.1 redirects.

The config uses http://127.0.0.1:3000 as the site URL and https://127.0.0.1:3000 for redirects, but Supabase Auth explicitly rejects IP addresses in redirect URLs (see PR supabase/auth#1984). Additionally, the scheme mismatch (http vs https) will cause validation to fail. The README and .env.local.example correctly document http://localhost:3000, which is what Supabase Auth accepts for local development.

Update both site_url and additional_redirect_urls to use http://localhost:3000:

Suggested fix
-site_url = "http://127.0.0.1:3000"
+site_url = "http://localhost:3000"
-additional_redirect_urls = ["https://127.0.0.1:3000"]
+additional_redirect_urls = ["http://localhost:3000"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/config.toml` around lines 154 - 156, Replace the IP-based and
mismatched-scheme values in the Supabase config by updating the site_url and
additional_redirect_urls entries: change site_url from "http://127.0.0.1:3000"
to "http://localhost:3000" and change additional_redirect_urls to use
"http://localhost:3000" (remove the https/127.0.0.1 variant) so Supabase Auth
accepts the local redirect and the scheme matches.

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.

2 participants