Skip to content

fix(ai): use the shared requireUser auth check - #88

Merged
Programcode merged 1 commit into
mainfrom
fix/ai-shared-auth
Oct 2, 2026
Merged

Programcode merged 1 commit into
mainfrom
fix/ai-shared-auth

Conversation

@Programcode

@Programcode Programcode commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner

What changed

The ai Edge Function had its own copy of the Bearer parsing and user lookup that _shared/auth.ts already provides. It now calls requireUser and returns its HttpError through jsonError, the same way account, announcements and workspace-invites do. An ultrareview of the logic slice flagged the duplicate (supabase/functions/ai/index.ts:277-294).

The copy had already drifted:

  • It didn't trim the token, so Authorization: Bearer went to Supabase Auth instead of being rejected locally.
  • Its errors were "Unauthorized" and "Invalid token". They are now "Authentication required" and "Invalid authentication token", matching the other functions.

The client (mapAIError) maps auth failures by HTTP status (401), and the status is unchanged, so users see the same sign-in-again message.

_shared/auth.ts reads SUPABASE_ANON_KEY when it loads. Hosted Edge Functions get that variable by default, and account already depends on it in production. The four test files that import ai/index.ts now set it.

Verification (local, at 8f489a8)

  • New failing-first test in ai/handler-research.test.ts covers a missing header, an empty bearer token and a rejected token. It failed before the fix ("Unauthorized") and passes after.
  • npm run test:edge: 326 passed.
  • npm test: 1208 passed. npm run lint, npm run typecheck, npm run docs:check and npm run build all pass.
  • Real app, fake provider (verify-debatelab): recipes/ai-tools passed 12/12 on desktop-chromium and mobile-chromium.
  • Direct calls to the local ai function returned 401 with Authentication required (no header), Authentication required (empty token) and Invalid authentication token (bogus token).

Deploy note

The merge ships no frontend change. After merge, ai must be redeployed (supabase functions deploy ai) for the change to reach production.

Follow-up, not in this PR

admin, ai-stream, create-checkout, create-portal, export-gdoc, import-source, redeem-promo, share and voice still have their own auth copies. Moving them to requireUser changes nine functions' error contracts, so it should be a separate PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HyWuBiDEMvVnjmguH9CwCZ

Summary by CodeRabbit

  • Bug Fixes
    • Non-streaming AI requests without a valid authorization token now return a 401 response with a consistent authentication error.

The ai Edge Function parsed the Bearer token and looked up the user with
its own copy of the logic in _shared/auth.ts. The copy had drifted: it
did not trim the token, sent an empty token to Auth, and returned
different error strings from account, announcements and
workspace-invites.

ai now calls requireUser and returns its HttpError through jsonError.
Missing or empty tokens get 401 "Authentication required" without an
Auth round trip; rejected tokens get 401 "Invalid authentication token".
The client maps 401 by status, so its behavior is unchanged.

A new handler test covers the missing, empty and rejected cases. Tests
that import ai/index.ts now set SUPABASE_ANON_KEY, which _shared/auth.ts
requires at load.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HyWuBiDEMvVnjmguH9CwCZ
Copilot AI balanced review requested due to automatic review settings October 2, 2026 11:00
@vercel

vercel Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
debate-navigator Ready Ready Preview Oct 2, 2026 11:00am UTC

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 5d03c4ad-0c9b-486f-8b58-a5a35fd7e437

📥 Commits

Reviewing files that changed from the base of the PR and between 11c8bf5 and 8f489a8.

📒 Files selected for processing (5)
  • supabase/functions/ai/debate-context.test.ts
  • supabase/functions/ai/generate-honesty_test.ts
  • supabase/functions/ai/handler-research.test.ts
  • supabase/functions/ai/index.ts
  • supabase/functions/ai/voice.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The AI handler now uses shared authentication helpers to obtain the user ID and format authentication errors. Tests configure SUPABASE_ANON_KEY and check responses to missing, empty, and rejected credentials.

Changes

AI request authentication

Layer / File(s) Summary
Shared authentication in the AI handler
supabase/functions/ai/index.ts
The handler calls requireUser(req) to obtain the user ID. It logs HttpError failures and returns them through jsonError with CORS headers.
Authentication test coverage and setup
supabase/functions/ai/*test.ts
Tests set SUPABASE_ANON_KEY. The non-streaming auth test checks 401 responses for missing, whitespace-only, and rejected credentials, and verifies remote-call counts.

Estimated code review effort: 2 (Simple) | ~10 minutes

Security Architecture Review

Security architecture risk: 🔵 Low · up to 8f489

The change centralizes authentication without an observed bypass or broader access. Deployment configuration remains unverified, so the assessment retains some uncertainty.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • observed — The changed enforcement point gates AI requests whose downstream operations include personal-source access and workspace processing. Those inspected operations receive the verified user identity rather than an identity supplied in the request payload.

Trust Boundaries and Controls

  • observed — The attacker-controlled authorization header is parsed locally, then its token is validated by Supabase before a user identity is returned. Although requireUser returns administrative and user-scoped clients, the changed AI call site consumes only user.id.

Resilience and Maintainability Implications

  • observed — The added authentication test asserts that missing and empty credentials return 401 without remote calls, while a rejected token returns 401 after one Supabase authentication call.
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Essentials by visiting https://app.coderabbit.ai/settings/billing.

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

@Programcode
Programcode merged commit 8279a30 into main Oct 2, 2026
8 of 9 checks passed
@Programcode
Programcode deleted the fix/ai-shared-auth branch October 2, 2026 11:40

This branch was successfully deployed

1 active deployment
Preview — 8f489a80 Deployed Oct 2, 2026 by vercel[bot]
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