feat(auth): OAuth session persistence + user auto-creation - #8
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Reviewer's GuideImplements explicit Supabase OAuth session cookie persistence in the Next.js auth callback route and adds database-side (and temporary route-side) user auto-creation logic via new Supabase trigger migrations while removing old migrations. Sequence diagram for OAuth callback session persistence and user auto-creationsequenceDiagram
actor User
participant Browser
participant AuthCallbackRoute as AuthCallbackRoute_GET
participant SupabaseAuth as Supabase_auth
participant PublicUsers as public_users_table
User->>Browser: Click OAuth provider
Browser->>AuthCallbackRoute: GET /auth/callback?code&next
AuthCallbackRoute->>SupabaseAuth: exchangeCodeForSession(code)
SupabaseAuth-->>AuthCallbackRoute: data.session, data.user
AuthCallbackRoute->>PublicUsers: insert({ id, email, created_at, updated_at })
PublicUsers-->>AuthCallbackRoute: insert result (ignore duplicate)
AuthCallbackRoute->>AuthCallbackRoute: NextResponse.redirect(safeNext)
AuthCallbackRoute->>Browser: response.cookies.set(auth_token)
AuthCallbackRoute->>Browser: response.cookies.set(sb-refresh-token)
Browser-->>User: Redirect to safeNext with persisted session
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughMigrations overhaul auto-creates user records when new Auth users sign up via a database trigger, and the auth callback route now validates sessions, syncs user records, and explicitly sets authentication cookies on responses. ChangesAuth User Creation on Signup
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
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. Comment |
|
Updates to Preview Branch (pr8a-oauth-session-fixes) ↗︎
Tasks are run on every commit but only new migration files are pushed.
❌ Branch Error • Sat, 16 May 2026 08:33:42 UTC View logs for this Workflow Run ↗︎. |
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- The user auto-creation logic is now implemented both in the OAuth callback (manual insert) and via the new auth.users trigger, which is redundant and means the callback does extra work on every login—consider relying on one mechanism (ideally the DB trigger) for consistency.
- There are three separate migrations that all define a handle_new_user() function and on_auth_user_created trigger with very similar bodies; consolidating this into a single migration (and removing duplicates) will make the migration history clearer and easier to maintain.
- The explicit Supabase cookie name derivation (using NEXT_PUBLIC_SUPABASE_URL!.split('.')[0]) is brittle and may not match the cookie naming expected by supabase-js or across environments—consider aligning with Supabase’s documented cookie names or delegating cookie management back to the Supabase client if possible.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The user auto-creation logic is now implemented both in the OAuth callback (manual insert) and via the new auth.users trigger, which is redundant and means the callback does extra work on every login—consider relying on one mechanism (ideally the DB trigger) for consistency.
- There are three separate migrations that all define a handle_new_user() function and on_auth_user_created trigger with very similar bodies; consolidating this into a single migration (and removing duplicates) will make the migration history clearer and easier to maintain.
- The explicit Supabase cookie name derivation (using NEXT_PUBLIC_SUPABASE_URL!.split('.')[0]) is brittle and may not match the cookie naming expected by supabase-js or across environments—consider aligning with Supabase’s documented cookie names or delegating cookie management back to the Supabase client if possible.
## Individual Comments
### Comment 1
<location path="supabase/migrations/20260515_auto_create_user_on_signup.sql" line_range="10" />
<code_context>
+ ON CONFLICT (id) DO NOTHING;
+ RETURN new;
+END;
+$$ LANGUAGE plpgsql SECURITY DEFINER;
+
+DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users;
</code_context>
<issue_to_address>
**🚨 suggestion (security):** `SECURITY DEFINER` without an explicit `search_path` can be risky; consider aligning with the version that sets `search_path = public`.
In `20260515_user_auth_trigger.sql` this function is defined as `LANGUAGE plpgsql SECURITY DEFINER SET search_path = public`, but here it relies on the default `search_path`. For `SECURITY DEFINER` functions that can be invoked by other roles, the default `search_path` can be exploited or cause unexpected object resolution. Please update this definition (or the canonical migration) to explicitly set the `search_path` to the intended schema.
```suggestion
$$ LANGUAGE plpgsql SECURITY DEFINER SET search_path = public;
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| ON CONFLICT (id) DO NOTHING; | ||
| RETURN new; | ||
| END; | ||
| $$ LANGUAGE plpgsql SECURITY DEFINER; |
There was a problem hiding this comment.
🚨 suggestion (security): SECURITY DEFINER without an explicit search_path can be risky; consider aligning with the version that sets search_path = public.
In 20260515_user_auth_trigger.sql this function is defined as LANGUAGE plpgsql SECURITY DEFINER SET search_path = public, but here it relies on the default search_path. For SECURITY DEFINER functions that can be invoked by other roles, the default search_path can be exploited or cause unexpected object resolution. Please update this definition (or the canonical migration) to explicitly set the search_path to the intended schema.
| $$ LANGUAGE plpgsql SECURITY DEFINER; | |
| $$ LANGUAGE plpgsql SECURITY DEFINER SET search_path = public; |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@supabase/migrations/20260515_auto_create_user_on_signup.sql`:
- Around line 1-15: This migration duplicates existing migrations that create
the same function and trigger (handle_new_user and on_auth_user_created); delete
this file and the redundant 20260515_create_user_trigger.sql and keep only the
secure migration (20260515_user_auth_trigger.sql) that includes SET search_path
= public in the SECURITY DEFINER function to prevent search_path manipulation;
ensure the retained file defines function handle_new_user() and trigger
on_auth_user_created and remove the duplicates so only the secure version runs.
In `@supabase/migrations/20260515_create_user_trigger.sql`:
- Around line 1-18: This migration is a duplicate lacking the required
search_path and should be removed: delete this file (which defines function
public.handle_new_user and trigger on_auth_user_created) and keep only the
intended migration 20260515_user_auth_trigger.sql that includes the proper SET
search_path = public configuration; ensure no other migrations redeclare
public.handle_new_user or on_auth_user_created to avoid duplicate objects.
In `@web/app/auth/callback/route.ts`:
- Around line 49-62: The manual insert block that checks data?.user and calls
supabase.from('users').insert(...) is redundant because the on_auth_user_created
DB trigger already creates public.users rows; remove that entire block (the
data?.user conditional and insert/error handling) to eliminate unnecessary
latency, or if you want defensive behavior instead replace the insert with a
Supabase upsert using onConflict: 'id' to handle duplicates explicitly (refer to
the data?.user check and the supabase.from('users').insert call to locate the
code).
- Around line 72-83: The setAll callback used in exchangeCodeForSession isn't
applying cookies and cache headers to the outgoing response and there are manual
cookie writes that build malformed names; update the setAll callback (the one
passed into exchangeCodeForSession) to iterate over the provided cookiesToSet
and headers and call response.cookies.set(...) for each cookie and
response.headers.set(...) (or equivalent) for the cache headers so cookies and
Cache-Control/Expires/Pragma are applied to the Route Handler response, and
remove the manual response.cookies.set(...) calls that construct cookie names
using process.env.NEXT_PUBLIC_SUPABASE_URL!.split('.')[0] (delete the two lines
setting 'sb-...' and 'sb-refresh-token') so Supabase SSR's setAll handles
correct cookie naming and propagation.
🪄 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: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 98b3b375-eee5-40c4-888d-107464768830
📒 Files selected for processing (10)
supabase/migrations/001_initial_schema.sqlsupabase/migrations/002_schedule_cleanup.sqlsupabase/migrations/003_add_embeddings.sqlsupabase/migrations/004_add_stripe_integration.sqlsupabase/migrations/005_add_sharing.sqlsupabase/migrations/20260513000000_initial_schema.sqlsupabase/migrations/20260515_auto_create_user_on_signup.sqlsupabase/migrations/20260515_create_user_trigger.sqlsupabase/migrations/20260515_user_auth_trigger.sqlweb/app/auth/callback/route.ts
💤 Files with no reviewable changes (6)
- supabase/migrations/004_add_stripe_integration.sql
- supabase/migrations/003_add_embeddings.sql
- supabase/migrations/005_add_sharing.sql
- supabase/migrations/20260513000000_initial_schema.sql
- supabase/migrations/002_schedule_cleanup.sql
- supabase/migrations/001_initial_schema.sql
| -- Auto-create user records when new auth users sign up via OAuth | ||
| CREATE OR REPLACE FUNCTION public.handle_new_user() | ||
| RETURNS TRIGGER AS $$ | ||
| BEGIN | ||
| INSERT INTO public.users (id, email, created_at, updated_at) | ||
| VALUES (new.id, new.email, NOW(), NOW()) | ||
| ON CONFLICT (id) DO NOTHING; | ||
| RETURN new; | ||
| END; | ||
| $$ LANGUAGE plpgsql SECURITY DEFINER; | ||
|
|
||
| DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users; | ||
| CREATE TRIGGER on_auth_user_created | ||
| AFTER INSERT ON auth.users | ||
| FOR EACH ROW EXECUTE FUNCTION public.handle_new_user(); |
There was a problem hiding this comment.
Duplicate migration files detected.
This migration is functionally identical to 20260515_create_user_trigger.sql and 20260515_user_auth_trigger.sql. All three create the same handle_new_user() function and on_auth_user_created trigger. Since migrations run sequentially, only the last one takes effect. Consolidate into a single migration file.
Additionally, this file is missing SET search_path = public on the SECURITY DEFINER function (present only in 20260515_user_auth_trigger.sql). Without it, the function is vulnerable to search_path manipulation attacks.
🛠️ Recommended: Delete this file and keep only `20260515_user_auth_trigger.sql`
The third migration file already includes the secure SET search_path = public clause. Remove this file and 20260515_create_user_trigger.sql to avoid confusion and ensure the secure version is applied.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@supabase/migrations/20260515_auto_create_user_on_signup.sql` around lines 1 -
15, This migration duplicates existing migrations that create the same function
and trigger (handle_new_user and on_auth_user_created); delete this file and the
redundant 20260515_create_user_trigger.sql and keep only the secure migration
(20260515_user_auth_trigger.sql) that includes SET search_path = public in the
SECURITY DEFINER function to prevent search_path manipulation; ensure the
retained file defines function handle_new_user() and trigger
on_auth_user_created and remove the duplicates so only the secure version runs.
| -- Create function to handle new user signup | ||
| CREATE OR REPLACE FUNCTION public.handle_new_user() | ||
| RETURNS TRIGGER AS $$ | ||
| BEGIN | ||
| INSERT INTO public.users (id, email, created_at, updated_at) | ||
| VALUES (new.id, new.email, NOW(), NOW()) | ||
| ON CONFLICT (id) DO NOTHING; | ||
| RETURN new; | ||
| END; | ||
| $$ LANGUAGE plpgsql SECURITY DEFINER; | ||
|
|
||
| -- Drop existing trigger if it exists | ||
| DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users; | ||
|
|
||
| -- Create trigger on auth.users | ||
| CREATE TRIGGER on_auth_user_created | ||
| AFTER INSERT ON auth.users | ||
| FOR EACH ROW EXECUTE FUNCTION public.handle_new_user(); |
There was a problem hiding this comment.
Duplicate migration with missing security configuration.
Same issue as 20260515_auto_create_user_on_signup.sql: this is a duplicate migration file missing SET search_path = public. Delete this file and retain only 20260515_user_auth_trigger.sql.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@supabase/migrations/20260515_create_user_trigger.sql` around lines 1 - 18,
This migration is a duplicate lacking the required search_path and should be
removed: delete this file (which defines function public.handle_new_user and
trigger on_auth_user_created) and keep only the intended migration
20260515_user_auth_trigger.sql that includes the proper SET search_path = public
configuration; ensure no other migrations redeclare public.handle_new_user or
on_auth_user_created to avoid duplicate objects.
| const cookieOptions = { | ||
| httpOnly: true, | ||
| secure: true, | ||
| path: '/', | ||
| sameSite: 'lax' as const, | ||
| maxAge: 60 * 60 * 24 * 365, // 1 year | ||
| }; | ||
|
|
||
| response.cookies.set('sb-' + process.env.NEXT_PUBLIC_SUPABASE_URL!.split('.')[0] + '-auth-token', data.session.access_token, cookieOptions); | ||
| response.cookies.set('sb-refresh-token', data.session.refresh_token, cookieOptions); | ||
|
|
||
| return response; |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Supabase SSR createServerClient cookies route handler Next.js 15
💡 Result:
In Next.js 15, the cookies() API from next/headers is asynchronous [1]. When using @supabase/ssr to create a server-side Supabase client, you must await the cookies() function to ensure compatibility with this change [2][3]. ### Implementation for Route Handlers and Server Components To create a Supabase client that works in Route Handlers and Server Components, define a utility function (typically in lib/supabase/server.ts) that awaits the cookie store and passes it to createServerClient [3][4]. typescript import { createServerClient } from '`@supabase/ssr`'; import { cookies } from 'next/headers'; export async function createClient() { const cookieStore = await cookies(); return createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { cookies: { getAll() { return cookieStore.getAll(); }, setAll(cookiesToSet) { try { cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options) ); } catch { // The setAll method was called from a Server Component. // This can be ignored if you have middleware refreshing user sessions. } }, }, } ); } ### Key Considerations 1. Asynchronous Cookies: Because cookies() is now asynchronous, any code calling createClient() must also be async and await the result [2][3]. 2. Error Handling in setAll: In Server Components, attempting to set cookies will throw an error because the response headers have already been sent [3]. Wrapping the cookieStore.set calls in a try/catch block is the standard pattern to allow the render to proceed while delegating session maintenance to your Middleware [3][4]. 3. Middleware Strategy: The Middleware is responsible for refreshing the session and writing cookies to the response [5][6]. Ensure your middleware uses createServerClient with request.cookies and response.cookies to properly handle session persistence [2][6]. 4. Migration: If you are migrating from the deprecated @supabase/auth-helpers-nextjs package, you must uninstall it and use @supabase/ssr directly [7][8]. The new package provides a framework-agnostic way to manage sessions via cookies [8].
Citations:
- 1: https://nextjs.org/docs/app/api-reference/functions/cookies
- 2: new asynchronous
cookies()innextjs 15supabase/supabase#30021 - 3: https://coldfusion-example.blogspot.com/2026/02/fixing-cookies-should-be-awaited-auth.html
- 4: https://codershandbook.com/supabase-with-nextjs-15-complete-integration-guide
- 5: https://supabase.com/docs/guides/auth/server-side/creating-a-client?environment=route-handler
- 6: https://coldfusion-example.blogspot.com/2026/02/fixing-authsessionmissingerror-in.html
- 7: https://supabase.com/docs/guides/auth/auth-helpers/nextjs
- 8: https://github.com/supabase/ssr/
🏁 Script executed:
cat -n web/app/auth/callback/route.ts | head -100Repository: Hex-Tech-Lab/hex-yt-intel
Length of output: 3769
🏁 Script executed:
web_search
Supabase Route Handler cookie names sb- formatRepository: Hex-Tech-Lab/hex-yt-intel
Length of output: 167
🌐 Web query:
Supabase SSR Route Handler cookie naming convention sb- prefix
💡 Result:
Supabase SSR (Route Handler) auth cookies use an sb- prefix by default; the cookie is named sb-<auth-token> (example shown as sb- -auth-token, i.e., the library inserts the auth token type) [1][2]. In the recommended @supabase/ssr setup, cookies are read from the incoming Cookie header and written via your setAll callback using the cookie name returned by the library (so you generally don’t hardcode the sb- names) [1][2]. If you need a different naming convention (e.g., multiple auth sessions), Supabase’s @supabase/ssr supports customizing the cookie name via cookieOptions.name (so you can override the default sb-... pattern) [3].
Citations:
- 1: https://supabase.com/docs/guides/auth/server-side/creating-a-client?environment=route-handler
- 2: https://supabase.com/docs/guides/auth/server-side/creating-a-client
- 3: https://dev.to/artl13/two-supabase-sessions-one-browser-cookie-partitioning-for-admin-and-customer-auth-327
🌐 Web query:
Supabase SSR Route Handler setAll callback cookies response propagation Next.js
💡 Result:
In Supabase + Next.js, you generally don’t “manually propagate” cookies in a Supabase SSR route handler beyond implementing the @supabase/ssr cookie adapter correctly. The key is to wire @supabase/ssr’s setAll callback to write Set-Cookie headers onto the Next.js HTTP response, and to apply the cache-busting headers when token refreshes happen. 1) What setAll is for (and when it’s called) - In @supabase/ssr, setAll is called whenever the library needs to write cookies, for example after a token refresh.[1][2] - When it calls setAll, it passes (a) the cookies to set and (b) a headers object containing cache headers (Cache-Control, Expires, Pragma) that must be applied to the HTTP response to prevent CDNs from caching and leaking sessions.[1][2] 2) Correct cookie propagation in a Next.js Route Handler (app/router) - In a Next.js Route Handler, your setAll implementation should: a) write the provided cookiesToSet as Set-Cookie headers on the Next.js response b) apply the provided headers (Cache-Control/Expires/Pragma) to the same response - Supabase’s docs describe this pattern generically for SSR route handlers/proxies: apply cookiesToSet to the HTTP response, and then apply each header from the second argument to the response.[1][2] If you’re using the Next.js-specific environment where cookies are managed via NextResponse, the approach looks like: - create a NextResponse instance (e.g., response = NextResponse.next) - configure @supabase/ssr createServerClient with cookies.getAll and cookies.setAll - in cookies.setAll(cookiesToSet, headers): - iterate cookiesToSet and call response.cookies.set(...) / or append Set-Cookie equivalents - iterate headers and set them on response.headers - return that response from the route handler so the Set-Cookie header is actually sent to the browser. Why this matters: during SSR, if the refreshed session cookie isn’t written into the HTTP response via Set-Cookie, the browser won’t get the updated JWT and auth can appear to randomly fail.[3] 3) Response propagation gotcha: where the token refresh writes - Supabase docs explicitly note that when @supabase/ssr refreshes a session token server-side, it writes the updated JWT to the HTTP response via a Set-Cookie header.[4][5] - Therefore, your setAll must affect the outgoing response for the request being handled, not just internal/server cookie storage.[1][2] 4) CDN / caching requirement (very commonly missed) - As of @supabase/ssr v0.10.0, the library automatically passes the necessary cache headers to your setAll callback as the second argument whenever a token refresh occurs; if your setAll applies those headers to the response, most CDNs need no extra manual configuration.[4][5] - If you don’t apply them, Supabase recommends setting Cache-Control: private, no-store on auth-handling responses to prevent cached token leakage.[4][5] 5) Next.js Server Components vs Route Handlers / Proxies - Supabase docs explain that in Server Components, you can’t set headers, so setAll may be wrapped in try/catch and errors ignored; a Proxy (middleware/route/proxy layer) is used to actually write cookies + cache headers on every request.[1][2] - For Route Handlers, you do have an outgoing response to mutate, so setAll should not be ignored—you should implement it so cookies and headers are written to the response.[1][2] 6) Related practical symptoms - Supabase community reports attribute missing cookies to cases where setAll was effectively “called too late” (after the HTTP response was already generated), causing cookies not to be included; newer versions reduced this risk by deferring initialization (skipAutoInitialize).[6] 7) “Supabase SSR Route Handler setAll callback cookies response propagation Next.js” checklist - Ensure your @supabase/ssr cookie adapter implements setAll.[3][7] - In setAll(cookiesToSet, headers): - write cookiesToSet to the Next.js response as Set-Cookie - apply headers (Cache-Control/Expires/Pragma) from the second argument to the same response.[1][2] - Return that response from your route handler so the browser receives Set-Cookie.[1][2] If you paste your current Next.js route handler code (the createServerClient setup + cookies.getAll/setAll), I can point out exactly what’s missing/miswired in your specific setAll propagation.
Citations:
- 1: https://supabase.com/docs/guides/auth/server-side/creating-a-client?environment=route-handler
- 2: https://supabase.com/docs/guides/auth/server-side/creating-a-client
- 3: https://github.com/supabase/ssr/blob/main/src/createServerClient.ts
- 4: https://supabase.com/docs/guides/auth/server-side-rendering
- 5: https://supabase.com/docs/guides/auth/server-side/advanced-guide
- 6: Cookies not setting properly supabase ssr supabase/ssr#36
- 7: Warning: @supabase/ssr: createServerClient was configured without the setAll cookie method - but it's definitely defined supabase/ssr#110
🏁 Script executed:
rg -A 5 "setAll.*headers" --type typescriptRepository: Hex-Tech-Lab/hex-yt-intel
Length of output: 104
🏁 Script executed:
rg "setAll" -A 3 --type tsRepository: Hex-Tech-Lab/hex-yt-intel
Length of output: 1534
🏁 Script executed:
cat -n web/middleware.tsRepository: Hex-Tech-Lab/hex-yt-intel
Length of output: 2275
🌐 Web query:
Supabase auth cookie names sb-auth-token refresh token exact naming
💡 Result:
Supabase does not have a single, hardcoded, global cookie name for authentication tokens. Instead, the naming of authentication cookies is configurable and depends on the implementation, particularly when using server-side rendering (SSR) or frameworks like Next.js [1][2]. Commonly used naming conventions in the Supabase ecosystem, particularly within community modules and SSR implementations, are: - Access Token: sb-access-token [3] - Refresh Token: sb-refresh-token [3] However, these are not enforced by the core Supabase Auth service itself. When using the @supabase/ssr package or similar server-side integrations, developers are typically responsible for defining the cookie name via the cookieOptions configuration [1]. This allows for flexibility, such as: - Customizing names to avoid conflicts (e.g., sb-admin-auth-token or sb-portal-auth-token) [1]. - Supporting multiple, parallel sessions in the same browser by using different cookie names for different areas of an application [1]. If you are implementing custom cookie storage, you must ensure that your client-side and server-side code are configured to read and write to the same cookie names [2]. If you are using pre-built auth helpers or framework-specific modules, check the documentation for that specific package to see its default naming convention, as it may differ from the common examples above [1].
Citations:
- 1: https://dev.to/artl13/two-supabase-sessions-one-browser-cookie-partitioning-for-admin-and-customer-auth-327
- 2: https://supabase.com/docs/guides/auth/server-side/advanced-guide
- 3:
sb-access-tokenandsb-refresh-tokencookie doesn't get set on login nuxt-modules/supabase#300
🏁 Script executed:
python3 << 'EOF'
# Test the split logic to confirm the issue
url = "https://xyz.supabase.co"
result = url.split('.')[0]
print(f"Original URL: {url}")
print(f"After split('.')[0]: {result}")
print(f"Cookie name would be: sb-{result}-auth-token")
# What it probably should be
parts = url.replace('https://', '').replace('http://', '').split('.')[0]
print(f"\nCorrected (removing protocol): {parts}")
print(f"Corrected cookie name: sb-{parts}-auth-token")
EOFRepository: Hex-Tech-Lab/hex-yt-intel
Length of output: 265
🏁 Script executed:
rg "sb-.*auth-token|sb-refresh-token" --type ts --type tsxRepository: Hex-Tech-Lab/hex-yt-intel
Length of output: 97
🏁 Script executed:
rg "sb-.*auth-token|sb-refresh-token" --type tsRepository: Hex-Tech-Lab/hex-yt-intel
Length of output: 365
Fix setAll callback to properly write cookies and cache headers to the response in Route Handler.
The current implementation has two critical issues:
-
setAll callback doesn't write to response: Lines 24-32 only set cookies on
cookieStore(request), not on theresponseobject. In a Route Handler, Supabase SSR'ssetAllmust apply bothcookiesToSetAND theheadersargument (Cache-Control, Expires, Pragma) to the HTTP response for session persistence. Without this, token refreshes won't propagate to the browser. -
Manual cookie names are malformed: Lines 80-81 construct incorrect cookie names.
process.env.NEXT_PUBLIC_SUPABASE_URL!.split('.')[0]onhttps://xyz.supabase.coyieldshttps://xyz, creating a cookie namedsb-https://xyz-auth-tokeninstead ofsb-xyz-auth-token. This name won't be recognized by Supabase client on subsequent requests.
Remove the manual cookie setting and fix setAll to handle response cookies and cache headers:
Fix setAll and remove manual cookies
const cookieStore = await cookies();
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll();
},
- setAll(cookiesToSet) {
+ setAll(cookiesToSet, headers) {
try {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options as CookieOptions)
);
+ // Apply cache headers for CDN safety during token refresh
+ headers.forEach(([key, value]) => {
+ response.headers.set(key, value);
+ });
} catch {
// Can be safely ignored during middleware/API route execution
}
},
},
}
);
// ... exchange code and validation ...
- // Create response with explicit cookie setting
- const response = NextResponse.redirect(new URL(safeNext, request.url));
-
- // Set Supabase session cookies explicitly on the response
- const cookieOptions = {
- httpOnly: true,
- secure: true,
- path: '/',
- sameSite: 'lax' as const,
- maxAge: 60 * 60 * 24 * 365, // 1 year
- };
-
- response.cookies.set('sb-' + process.env.NEXT_PUBLIC_SUPABASE_URL!.split('.')[0] + '-auth-token', data.session.access_token, cookieOptions);
- response.cookies.set('sb-refresh-token', data.session.refresh_token, cookieOptions);
-
+ const response = NextResponse.redirect(new URL(safeNext, request.url));
return response;The setAll callback is called when exchangeCodeForSession returns, and it's the proper place to propagate session cookies to the response. Let Supabase SSR handle cookie naming via the setAll interface rather than hardcoding names.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/app/auth/callback/route.ts` around lines 72 - 83, The setAll callback
used in exchangeCodeForSession isn't applying cookies and cache headers to the
outgoing response and there are manual cookie writes that build malformed names;
update the setAll callback (the one passed into exchangeCodeForSession) to
iterate over the provided cookiesToSet and headers and call
response.cookies.set(...) for each cookie and response.headers.set(...) (or
equivalent) for the cache headers so cookies and Cache-Control/Expires/Pragma
are applied to the Route Handler response, and remove the manual
response.cookies.set(...) calls that construct cookie names using
process.env.NEXT_PUBLIC_SUPABASE_URL!.split('.')[0] (delete the two lines
setting 'sb-...' and 'sb-refresh-token') so Supabase SSR's setAll handles
correct cookie naming and propagation.
There was a problem hiding this comment.
4 issues found across 10 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="supabase/migrations/20260515_auto_create_user_on_signup.sql">
<violation number="1" location="supabase/migrations/20260515_auto_create_user_on_signup.sql:10">
P1: `SECURITY DEFINER` is used without a fixed `search_path`, which can expose search-path hijacking risk. Pin the function search path explicitly.</violation>
<violation number="2" location="supabase/migrations/20260515_auto_create_user_on_signup.sql:10">
P1: Duplicate migration with insecure `SECURITY DEFINER` — missing `SET search_path = public`. This is the second of three identical migration files creating the same `handle_new_user()` function and trigger. Consolidate into a single migration (keep `20260515_user_auth_trigger.sql` which has the secure `SET search_path = public`) and delete this file.</violation>
</file>
<file name="web/app/auth/callback/route.ts">
<violation number="1" location="web/app/auth/callback/route.ts:49">
P2: This insert path runs on every OAuth callback, causing repeated writes and duplicate-error-driven control flow instead of true “new user” detection.</violation>
<violation number="2" location="web/app/auth/callback/route.ts:80">
P1: These cookies are written in a non-`@supabase/ssr` format, so auth session restoration/refresh can fail even after OAuth callback.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Re-trigger cubic
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Added JSDoc for GET handler in /auth/callback route: - Describes OAuth callback flow and session token exchange - Documents parameters, return value, and execution steps - Satisfies CodeRabbit docstring coverage threshold (80%) Verification: - pnpm type-check: ✅ PASS - pnpm lint: ✅ PASS - pnpm build: ✅ PASS (verified earlier) CodeRabbit Issue #8 Resolution: Docstring Coverage Warning ✅ Co-Authored-By: Claude Code <noreply@anthropic.com>
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
# Conflicts: # web/app/auth/callback/route.ts
* fix(auth): Auto-create user record on OAuth callback to fix session persistence * fix: Remove invalid .on() call from user insert * fix(critical): Set Supabase session cookies explicitly in callback response to fix auth persistence * docs(auth): Add comprehensive JSDoc docstring to OAuth callback handler Added JSDoc for GET handler in /auth/callback route: - Describes OAuth callback flow and session token exchange - Documents parameters, return value, and execution steps - Satisfies CodeRabbit docstring coverage threshold (80%) Verification: - pnpm type-check: ✅ PASS - pnpm lint: ✅ PASS - pnpm build: ✅ PASS (verified earlier) CodeRabbit Issue #8 Resolution: Docstring Coverage Warning ✅ Co-Authored-By: Claude Code <noreply@anthropic.com> --------- Co-authored-by: Kelly Bakri <noreply@github.com> Co-authored-by: Claude Code <noreply@anthropic.com>
Summary
Key Changes
Test Plan
🤖 Generated with Claude Code
Summary by Sourcery
Persist OAuth sessions reliably and ensure user records are created automatically on authentication callbacks and signups.
New Features:
Enhancements:
Deployment:
Summary by cubic
Fixes OAuth session persistence and prevents “user not found” errors by setting Supabase auth cookies in the callback and auto-creating user records on signup. Also adds a clear JSDoc to the OAuth callback for easier maintenance.
Bug Fixes
response.cookies.set(HttpOnly, Secure, SameSite=Lax).public.userson signup via DB trigger, with a safe insert fallback in the callback; removed the invalid.on()call.Migration
public.usersonauth.usersinsert; run the latest Supabase migrations.Written for commit ee08db8. Summary will update on new commits. Review in cubic
Summary by CodeRabbit
New Features
Refactor