Skip to content

feat: implement Supabase Auth, document isolation by user, and dynami…#3

Merged
ivancidev merged 1 commit into
mainfrom
feature/supabase-auth-integration
Jun 9, 2026
Merged

feat: implement Supabase Auth, document isolation by user, and dynami…#3
ivancidev merged 1 commit into
mainfrom
feature/supabase-auth-integration

Conversation

@ivancidev

@ivancidev ivancidev commented Jun 9, 2026

Copy link
Copy Markdown
Owner

PR Summary by Qodo

Add Supabase Auth, user-scoped documents, and auth-gated UI
✨ Enhancement ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

Walkthroughs

User Description

…c UI elements

AI Description
• Add Supabase email/password auth with callback and global AuthProvider.
• Require Bearer token on upload/chat APIs and scope document operations by user_id.
• Gate upload/chat/navigation CTAs based on auth state and show loading spinners.
Diagram
graph TD
  UI["Client UI"] --> AC["AuthContext"] --> SA{{"Supabase Auth"}}
  UI -- "Bearer token" --> AU["/api/upload"] --> DB[("documents")]
  UI -- "Bearer token" --> CH["/api/chat"] --> DB
  CH --> EMB{{"Cohere Embeddings"}}
  CH --> LLM{{"Groq Chat"}}

  subgraph Legend
    direction LR
    _db[("Database")] ~~~ _api["API/Component"] ~~~ _ext{{"External"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use Supabase SSR helpers + cookie sessions
  • ➕ Avoid manual Authorization header plumbing from client hooks
  • ➕ Server routes can rely on cookie-based session retrieval
  • ➕ Cleaner redirect/protection patterns with fewer client-side spinners
  • ➖ Requires adopting @supabase/ssr (or Next helpers) and wiring middleware/cookies
  • ➖ More moving parts and potential SSR hydration/session edge cases
2. Enforce isolation via RLS + non-service server client
  • ➕ Defense-in-depth: DB enforces user scoping even if app code regresses
  • ➕ Reduces risk of service-role misuse in future changes
  • ➖ Requires RLS policies + using user JWT/anon key for data access
  • ➖ May require reworking current RPC/table access patterns
3. Centralize API auth guard helper (shared function/middleware)
  • ➕ Removes duplicated token parsing/getUser logic across routes
  • ➕ Consistent error responses and easier future expansion
  • ➖ Still relies on service-role filtering unless combined with RLS
  • ➖ Small refactor overhead now

Recommendation: Current approach is reasonable for a first iteration: validate the Supabase JWT on the server, derive userId, and scope all DB operations by that value. For stronger long-term safety, consider migrating to RLS-enforced isolation (and/or Supabase SSR cookie sessions) so the database becomes the final enforcement layer and the client no longer needs to pass access tokens explicitly.

Grey Divider

File Changes

Enhancement (14)
route.ts Require auth token and scope vector search by user +19/-0

Require auth token and scope vector search by user

• Adds Bearer-token authentication and rejects unauthenticated/expired sessions. Passes the authenticated user's id into match_documents RPC via p_user_id to isolate retrieval results.

app/api/chat/route.ts


route.ts Require auth token and store/delete chunks per user +22/-2

Require auth token and store/delete chunks per user

• Adds Bearer-token authentication and derives userId from the session. Overwrite logic now deletes by (name, user_id) and inserts new document chunks with user_id set.

app/api/upload/route.ts


route.ts Add Supabase email confirmation callback handler +19/-0

Add Supabase email confirmation callback handler

• Implements an OAuth-style callback endpoint that exchanges the email confirmation code for a session using the anon key client, then redirects to /upload.

app/auth/callback/route.ts


page.tsx Add login/sign-up page with email/password flow +188/-0

Add login/sign-up page with email/password flow

• Introduces a dedicated auth page supporting sign-in and sign-up with email/password. Includes redirect behavior for authenticated users and UI states for loading, error, and email-confirmation success messaging.

app/auth/page.tsx


page.tsx Protect chat page behind authentication +20/-1

Protect chat page behind authentication

• Adds client-side redirect to /auth when unauthenticated. Shows a spinner while auth state is loading or when no user is present.

app/chat/page.tsx


layout.tsx Wrap app shell with AuthProvider +5/-2

Wrap app shell with AuthProvider

• Registers the global AuthProvider around the Navbar and page content so auth state is available throughout the app.

app/layout.tsx


page.tsx Make home CTAs conditional on auth state +46/-17

Make home CTAs conditional on auth state

• Converts the home page to a client component and renders different CTA buttons depending on whether a user is logged in. Displays a spinner while auth state is resolving.

app/page.tsx


page.tsx Protect upload page behind authentication +20/-0

Protect upload page behind authentication

• Adds client-side redirect to /auth when unauthenticated. Shows a spinner while auth state is loading or when no user is present.

app/upload/page.tsx


Navbar.tsx Show nav links only when logged in and add sign-out +60/-21

Show nav links only when logged in and add sign-out

• Integrates auth state into the Navbar to conditionally render Upload/Chat links for authenticated users. Adds user email display and a logout button, plus a login link when signed out.

components/layout/Navbar.tsx


AuthContext.tsx Introduce AuthContext provider for session/user state +73/-0

Introduce AuthContext provider for session/user state

• Adds a client-side context that loads the current Supabase session, subscribes to auth state changes, and exposes user/session/loading plus a signOut helper.

components/providers/AuthContext.tsx


useChat.ts Attach access token to chat requests +3/-1

Attach access token to chat requests

• Reads the current session from AuthContext and passes the access token to queryDocuments so /api/chat can authenticate the user.

features/chat/hooks/useChat.ts


useUpload.ts Attach access token to upload requests +4/-2

Attach access token to upload requests

• Reads the current session from AuthContext and passes the access token to uploadDocument/uploadText so /api/upload can authenticate the user.

features/upload/hooks/useUpload.ts


n8n.ts Support Bearer auth headers and propagate API errors +35/-12

Support Bearer auth headers and propagate API errors

• Extends uploadDocument/uploadText/queryDocuments to accept an optional token and send it via Authorization header. Improves error messaging by surfacing server error payloads when requests fail.

lib/n8n.ts


supabaseBrowser.ts Add browser Supabase client for auth +7/-0

Add browser Supabase client for auth

• Creates a client-side Supabase instance using NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY for authentication flows in the browser.

lib/supabaseBrowser.ts


Other (1)
.env.example Document Supabase anon key env var +1/-0

Document Supabase anon key env var

• Adds NEXT_PUBLIC_SUPABASE_ANON_KEY to the example environment configuration so browser auth can be initialized.

.env.example


Grey Divider

Qodo Logo

@ivancidev ivancidev self-assigned this Jun 9, 2026
@vercel

vercel Bot commented Jun 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
doqify Ready Ready Preview, Comment Jun 9, 2026 5:32am

@qodo-code-review

qodo-code-review Bot commented Jun 9, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (0)

Grey Divider


Action required

1. Callback session not stored 🐞 Bug ≡ Correctness
Description
/auth/callback exchanges the auth code in a server route handler but never persists the resulting
session to the browser (cookie/localStorage), so AuthContext will still see session=null and the
user remains logged out after the email confirmation redirect.
Code

app/auth/callback/route.ts[R10-18]

+  if (code) {
+    const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL || "https://placeholder-project.supabase.co";
+    const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "placeholder-anon-key";
+    
+    const supabase = createClient(supabaseUrl, supabaseAnonKey);
+    await supabase.auth.exchangeCodeForSession(code);
+  }
+
+  return NextResponse.redirect(`${requestUrl.origin}/upload`);
Evidence
Signup redirects to /auth/callback, but the callback logic runs on the server and only exchanges
the code without persisting a session in the browser; AuthContext reads session state from the
browser client (getSession()), so it will remain null after the redirect.

app/auth/page.tsx[46-52]
app/auth/callback/route.ts[6-19]
components/providers/AuthContext.tsx[21-42]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Supabase PKCE callback is handled by a server route (`app/auth/callback/route.ts`) that calls `exchangeCodeForSession(code)` but does not write the session into browser storage (or set auth cookies). The app’s auth state is client-driven via `supabaseBrowser.auth.getSession()`, so users will still appear logged out after confirming their email.

## Issue Context
- Signup sets `emailRedirectTo` to `/auth/callback`.
- Auth state is read in the browser via `supabaseBrowser`.
- A Next.js route handler cannot populate browser localStorage; cookie-based auth would require Supabase SSR helpers.

## Fix Focus Areas
- app/auth/callback/route.ts[1-19]
- app/auth/page.tsx[46-52]
- components/providers/AuthContext.tsx[21-42]

## Suggested fix
Option A (simplest for SPA-style auth):
1. Replace the route handler with a **client page** at `app/auth/callback/page.tsx` (and remove `route.ts` to avoid path conflicts).
2. In the client page, read `code` from `useSearchParams()`, call `supabaseBrowser.auth.exchangeCodeForSession(code)`, then `router.replace('/upload')`.
3. Show a spinner + error UI on failure.

Option B (cookie-based SSR auth):
1. Use `@supabase/ssr` (or official Next auth helpers) in the callback handler to exchange the code and **set response cookies** so the browser has a persisted session.
2. Update `AuthContext` to read session from cookies/SSR client appropriately.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. RPC parameter mismatch 🐞 Bug ≡ Correctness
Description
/api/chat passes p_user_id into the match_documents RPC call, but the repository’s documented
SQL defines match_documents(query_embedding, match_threshold, match_count) only; this will cause
the RPC call to fail at runtime against the documented setup.
Code

app/api/chat/route.ts[R66-70]

        query_embedding: queryEmbedding,
        match_threshold: 0.2, // cosine similarity threshold
        match_count: 5,        // retrieve top 5 matching blocks
+        p_user_id: userId,
      }
Evidence
The chat route now sends p_user_id, but the repo’s documented SQL function signature does not
accept it, indicating an integration mismatch that will break the RPC call unless the DB was updated
out-of-band.

app/api/chat/route.ts[63-71]
README.md[66-71]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `match_documents` RPC call now includes a named parameter `p_user_id`, but the repo’s Supabase setup SQL (README) defines `match_documents` with only three parameters. With Supabase/PostgREST RPC, providing an unexpected named argument causes a runtime error.

## Issue Context
- Chat API uses `supabase.rpc('match_documents', { ..., p_user_id })`.
- README provides the only in-repo definition of the function and does not include `p_user_id`.

## Fix Focus Areas
- app/api/chat/route.ts[63-71]
- README.md[66-90]

## Suggested fix
Choose one:
1. **Update the database function** to accept `p_user_id uuid` and filter rows by `documents.user_id = p_user_id`.
2. If DB changes are not intended, **remove `p_user_id`** from the RPC call and rely on RLS instead (but then ensure RLS is enabled and policies exist).

Also update the README SQL snippet to match the new function signature and behavior.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Missing documents.user_id 🐞 Bug ≡ Correctness
Description
/api/upload now inserts and filters on documents.user_id, but the repo’s documented Supabase
table definition for documents does not include that column; upload and overwrite-delete will fail
unless the DB schema is updated.
Code

app/api/upload/route.ts[R115-119]

      name,
      content: chunk,
      embedding: embeddings[index],
+      user_id: userId,
    }));
Evidence
The upload route now requires a user_id column, but the repository’s documented setup SQL does not
create it, so the new insert/delete logic won’t work unless the DB schema is updated accordingly.

app/api/upload/route.ts[101-124]
README.md[57-65]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The upload API writes `user_id` into the `documents` table and filters deletes by `user_id`. The in-repo Supabase setup SQL (README) does not create this column, so inserts/filters will error against the documented schema.

## Issue Context
- `/api/upload` builds `records` containing `user_id`.
- README SQL defines `documents` without `user_id`.

## Fix Focus Areas
- app/api/upload/route.ts[101-124]
- README.md[57-65]

## Suggested fix
1. Update the Supabase schema:
  - Add `user_id uuid not null references auth.users(id)` (or the appropriate auth schema).
  - Add an index on `(user_id, name)` for overwrite delete and retrieval.
2. Update/introduce RLS policies (recommended) so isolation is enforced at the DB layer.
3. Update README SQL to include the new column and any required policies.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Anon key not documented 🐞 Bug ☼ Reliability
Description
The client auth code requires NEXT_PUBLIC_SUPABASE_ANON_KEY, but the README’s environment
configuration section omits it; combined with placeholder fallbacks in the Supabase clients, this
will commonly fail with confusing auth errors instead of a clear configuration error.
Code

lib/supabaseBrowser.ts[R3-5]

+const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL || "https://placeholder-project.supabase.co";
+const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "placeholder-anon-key";
+
Evidence
The browser client initializes using NEXT_PUBLIC_SUPABASE_ANON_KEY, but README does not tell users
to set it; the code falls back to placeholder values, increasing the chance of opaque auth failures
when env is incomplete.

lib/supabaseBrowser.ts[1-7]
README.md[94-109]
.env.example[1-5]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Client auth depends on `NEXT_PUBLIC_SUPABASE_ANON_KEY`, but README env setup doesn’t include it, and the Supabase client modules fall back to placeholder values when env vars are missing. This makes misconfiguration likely and hard to diagnose.

## Issue Context
- `.env.example` includes `NEXT_PUBLIC_SUPABASE_ANON_KEY`.
- `README.md` env config omits it.
- `lib/supabaseBrowser.ts` and `lib/supabase.ts` use placeholder fallbacks.

## Fix Focus Areas
- lib/supabaseBrowser.ts[1-7]
- lib/supabase.ts[1-11]
- README.md[94-110]
- .env.example[1-5]

## Suggested fix
1. Update README env config snippet to include `NEXT_PUBLIC_SUPABASE_ANON_KEY`.
2. Replace placeholder fallbacks with explicit validation:
  - If required env vars are missing, throw a clear error (client) or return a clear 500 + log (server).
3. Optionally add a small runtime check in `AuthProvider` to render a helpful configuration error screen when env is missing.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@ivancidev
ivancidev merged commit d57486e into main Jun 9, 2026
4 checks passed
Comment on lines +10 to +18
if (code) {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL || "https://placeholder-project.supabase.co";
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "placeholder-anon-key";

const supabase = createClient(supabaseUrl, supabaseAnonKey);
await supabase.auth.exchangeCodeForSession(code);
}

return NextResponse.redirect(`${requestUrl.origin}/upload`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Callback session not stored 🐞 Bug ≡ Correctness

/auth/callback exchanges the auth code in a server route handler but never persists the resulting
session to the browser (cookie/localStorage), so AuthContext will still see session=null and the
user remains logged out after the email confirmation redirect.
Agent Prompt
## Issue description
The Supabase PKCE callback is handled by a server route (`app/auth/callback/route.ts`) that calls `exchangeCodeForSession(code)` but does not write the session into browser storage (or set auth cookies). The app’s auth state is client-driven via `supabaseBrowser.auth.getSession()`, so users will still appear logged out after confirming their email.

## Issue Context
- Signup sets `emailRedirectTo` to `/auth/callback`.
- Auth state is read in the browser via `supabaseBrowser`.
- A Next.js route handler cannot populate browser localStorage; cookie-based auth would require Supabase SSR helpers.

## Fix Focus Areas
- app/auth/callback/route.ts[1-19]
- app/auth/page.tsx[46-52]
- components/providers/AuthContext.tsx[21-42]

## Suggested fix
Option A (simplest for SPA-style auth):
1. Replace the route handler with a **client page** at `app/auth/callback/page.tsx` (and remove `route.ts` to avoid path conflicts).
2. In the client page, read `code` from `useSearchParams()`, call `supabaseBrowser.auth.exchangeCodeForSession(code)`, then `router.replace('/upload')`.
3. Show a spinner + error UI on failure.

Option B (cookie-based SSR auth):
1. Use `@supabase/ssr` (or official Next auth helpers) in the callback handler to exchange the code and **set response cookies** so the browser has a persisted session.
2. Update `AuthContext` to read session from cookies/SSR client appropriately.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread app/api/chat/route.ts
Comment on lines 66 to 70
query_embedding: queryEmbedding,
match_threshold: 0.2, // cosine similarity threshold
match_count: 5, // retrieve top 5 matching blocks
p_user_id: userId,
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. Rpc parameter mismatch 🐞 Bug ≡ Correctness

/api/chat passes p_user_id into the match_documents RPC call, but the repository’s documented
SQL defines match_documents(query_embedding, match_threshold, match_count) only; this will cause
the RPC call to fail at runtime against the documented setup.
Agent Prompt
## Issue description
The `match_documents` RPC call now includes a named parameter `p_user_id`, but the repo’s Supabase setup SQL (README) defines `match_documents` with only three parameters. With Supabase/PostgREST RPC, providing an unexpected named argument causes a runtime error.

## Issue Context
- Chat API uses `supabase.rpc('match_documents', { ..., p_user_id })`.
- README provides the only in-repo definition of the function and does not include `p_user_id`.

## Fix Focus Areas
- app/api/chat/route.ts[63-71]
- README.md[66-90]

## Suggested fix
Choose one:
1. **Update the database function** to accept `p_user_id uuid` and filter rows by `documents.user_id = p_user_id`.
2. If DB changes are not intended, **remove `p_user_id`** from the RPC call and rely on RLS instead (but then ensure RLS is enabled and policies exist).

Also update the README SQL snippet to match the new function signature and behavior.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread app/api/upload/route.ts
Comment on lines 115 to 119
name,
content: chunk,
embedding: embeddings[index],
user_id: userId,
}));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

3. Missing documents.user_id 🐞 Bug ≡ Correctness

/api/upload now inserts and filters on documents.user_id, but the repo’s documented Supabase
table definition for documents does not include that column; upload and overwrite-delete will fail
unless the DB schema is updated.
Agent Prompt
## Issue description
The upload API writes `user_id` into the `documents` table and filters deletes by `user_id`. The in-repo Supabase setup SQL (README) does not create this column, so inserts/filters will error against the documented schema.

## Issue Context
- `/api/upload` builds `records` containing `user_id`.
- README SQL defines `documents` without `user_id`.

## Fix Focus Areas
- app/api/upload/route.ts[101-124]
- README.md[57-65]

## Suggested fix
1. Update the Supabase schema:
   - Add `user_id uuid not null references auth.users(id)` (or the appropriate auth schema).
   - Add an index on `(user_id, name)` for overwrite delete and retrieval.
2. Update/introduce RLS policies (recommended) so isolation is enforced at the DB layer.
3. Update README SQL to include the new column and any required policies.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

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