From c69ccb695b0e6c4cf7730ece5a457b7bdafe268f Mon Sep 17 00:00:00 2001 From: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com> Date: Wed, 8 Jul 2026 08:50:41 +0000 Subject: [PATCH] fix(auth): resolve Clerk-Supabase integration and searchParams error - Refactored lib/db/index.ts to manually parse DATABASE_URL using pg-connection-string, bypassing problematic internal URL parsing in the pg driver that triggered searchParams errors in Next.js 15. - Added public.clerk_id() and public.is_clerk_user() helper functions to the Supabase database to support Clerk JWT authentication in RLS policies. - Updated RLS policies for users, chats, messages, and other tables to authorize Clerk users. - Improved error handling and logging in lib/auth/get-current-user.ts. - Created Drizzle migration 0003_add_clerk_auth_helpers.sql to track database changes. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- .../0003_add_clerk_auth_helpers.sql | 166 ++++++++++++++++++ drizzle/migrations/meta/_journal.json | 9 +- lib/auth/get-current-user.ts | 14 +- lib/db/index.ts | 26 ++- lib/db/migrate.ts | 16 +- 5 files changed, 215 insertions(+), 16 deletions(-) create mode 100644 drizzle/migrations/0003_add_clerk_auth_helpers.sql diff --git a/drizzle/migrations/0003_add_clerk_auth_helpers.sql b/drizzle/migrations/0003_add_clerk_auth_helpers.sql new file mode 100644 index 00000000..2cdcc55a --- /dev/null +++ b/drizzle/migrations/0003_add_clerk_auth_helpers.sql @@ -0,0 +1,166 @@ +-- Create a function to extract the Clerk user ID from the JWT +CREATE OR REPLACE FUNCTION public.clerk_id() +RETURNS text AS $$ + SELECT (auth.jwt() ->> 'sub'); +$$ LANGUAGE sql STABLE; +--> statement-breakpoint + +-- Create a function to check if a user matches the Clerk ID +CREATE OR REPLACE FUNCTION public.is_clerk_user(user_id uuid) +RETURNS boolean AS $$ + SELECT EXISTS ( + SELECT 1 FROM public.users + WHERE id = user_id AND clerk_user_id = public.clerk_id() + ); +$$ LANGUAGE sql STABLE; +--> statement-breakpoint + +-- Update RLS policies for users table +DROP POLICY IF EXISTS "Users can manage their own profile" ON public.users; +CREATE POLICY "Users can manage their own profile" ON public.users +FOR ALL USING ( + id = auth.uid() -- Original Supabase Auth + OR + clerk_user_id = public.clerk_id() -- Clerk Auth +); +--> statement-breakpoint + +-- Update RLS policies for chats table +DROP POLICY IF EXISTS "Users can manage their own chats" ON public.chats; +CREATE POLICY "Users can manage their own chats" ON public.chats +FOR ALL USING ( + user_id = auth.uid() -- Original Supabase Auth + OR + public.is_clerk_user(user_id) -- Clerk Auth +); +--> statement-breakpoint + +DROP POLICY IF EXISTS "Users can insert their own chats" ON public.chats; +CREATE POLICY "Users can insert their own chats" ON public.chats +FOR INSERT WITH CHECK ( + user_id = auth.uid() -- Original Supabase Auth + OR + public.is_clerk_user(user_id) -- Clerk Auth +); +--> statement-breakpoint + +DROP POLICY IF EXISTS "Users can select chats they are a part of" ON public.chats; +CREATE POLICY "Users can select chats they are a part of" ON public.chats +FOR SELECT USING ( + user_id = auth.uid() -- Original Supabase Auth + OR + public.is_clerk_user(user_id) -- Clerk Auth + OR + EXISTS ( + SELECT 1 FROM public.chat_participants + WHERE chat_id = chats.id AND (user_id = auth.uid() OR public.is_clerk_user(user_id)) + ) +); +--> statement-breakpoint + +-- Messages +DROP POLICY IF EXISTS "messages_owner_participant_access" ON public.messages; +CREATE POLICY "messages_owner_participant_access" ON public.messages +FOR ALL USING ( + user_id = auth.uid() + OR + public.is_clerk_user(user_id) + OR + EXISTS ( + SELECT 1 FROM public.chats + WHERE chats.id = messages.chat_id AND (chats.user_id = auth.uid() OR public.is_clerk_user(chats.user_id)) + ) + OR + EXISTS ( + SELECT 1 FROM public.chat_participants + WHERE chat_participants.chat_id = messages.chat_id AND (chat_participants.user_id = auth.uid() OR public.is_clerk_user(chat_participants.user_id)) + ) +); +--> statement-breakpoint + +-- Calendar Notes +DROP POLICY IF EXISTS "Users can manage their own calendar notes" ON public.calendar_notes; +CREATE POLICY "Users can manage their own calendar notes" ON public.calendar_notes +FOR ALL USING ( + user_id = auth.uid() + OR + public.is_clerk_user(user_id) +); +--> statement-breakpoint + +-- System Prompts +DROP POLICY IF EXISTS "Users can manage their own system prompts" ON public.system_prompts; +CREATE POLICY "Users can manage their own system prompts" ON public.system_prompts +FOR ALL USING ( + user_id = auth.uid() + OR + public.is_clerk_user(user_id) +); +--> statement-breakpoint + +-- Locations +DROP POLICY IF EXISTS "Users can manage their own locations" ON public.locations; +CREATE POLICY "Users can manage their own locations" ON public.locations +FOR ALL USING ( + user_id = auth.uid() + OR + public.is_clerk_user(user_id) +); +--> statement-breakpoint + +-- Visualizations +DROP POLICY IF EXISTS "Users can manage their own visualizations" ON public.visualizations; +CREATE POLICY "Users can manage their own visualizations" ON public.visualizations +FOR ALL USING ( + user_id = auth.uid() + OR + public.is_clerk_user(user_id) +); +--> statement-breakpoint + +-- Chat Participants +DROP POLICY IF EXISTS "Only owners can delete participants" ON public.chat_participants; +CREATE POLICY "Only owners can delete participants" ON public.chat_participants +FOR DELETE USING ( + EXISTS ( + SELECT 1 FROM public.chat_participants cp + WHERE cp.chat_id = chat_participants.chat_id + AND (cp.user_id = auth.uid() OR public.is_clerk_user(cp.user_id)) + AND cp.role = 'owner' + ) +); +--> statement-breakpoint + +DROP POLICY IF EXISTS "Only owners can insert participants" ON public.chat_participants; +CREATE POLICY "Only owners can insert participants" ON public.chat_participants +FOR INSERT WITH CHECK ( + EXISTS ( + SELECT 1 FROM public.chat_participants cp + WHERE cp.chat_id = chat_participants.chat_id + AND (cp.user_id = auth.uid() OR public.is_clerk_user(cp.user_id)) + AND cp.role = 'owner' + ) +); +--> statement-breakpoint + +DROP POLICY IF EXISTS "Only owners can update participants" ON public.chat_participants; +CREATE POLICY "Only owners can update participants" ON public.chat_participants +FOR UPDATE USING ( + EXISTS ( + SELECT 1 FROM public.chat_participants cp + WHERE cp.chat_id = chat_participants.chat_id + AND (cp.user_id = auth.uid() OR public.is_clerk_user(cp.user_id)) + AND cp.role = 'owner' + ) +); +--> statement-breakpoint + +DROP POLICY IF EXISTS "Participants can view other participants" ON public.chat_participants; +CREATE POLICY "Participants can view other participants" ON public.chat_participants +FOR SELECT USING ( + EXISTS ( + SELECT 1 FROM public.chat_participants cp + WHERE cp.chat_id = chat_participants.chat_id + AND (cp.user_id = auth.uid() OR public.is_clerk_user(cp.user_id)) + ) +); diff --git a/drizzle/migrations/meta/_journal.json b/drizzle/migrations/meta/_journal.json index 259f488a..b3c7bd38 100644 --- a/drizzle/migrations/meta/_journal.json +++ b/drizzle/migrations/meta/_journal.json @@ -22,6 +22,13 @@ "when": 1782395315062, "tag": "0002_lively_black_widow", "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1783500279587, + "tag": "0003_add_clerk_auth_helpers", + "breakpoints": true } ] -} \ No newline at end of file +} diff --git a/lib/auth/get-current-user.ts b/lib/auth/get-current-user.ts index 5ab5385f..8595c451 100644 --- a/lib/auth/get-current-user.ts +++ b/lib/auth/get-current-user.ts @@ -17,8 +17,13 @@ export async function getClerkUserIdOnServer(): Promise { if (AUTH_DISABLED_FLAG) { return MOCK_USER_ID; } - const { userId } = await auth(); - return userId; + try { + const { userId } = await auth(); + return userId; + } catch (error) { + console.error('[Auth] Error getting Clerk user ID:', error); + return null; + } } /** @@ -47,7 +52,10 @@ export async function resolveClerkUserToDbUser(clerkUserId: string): Promise