Skip to content

refactor(auth): split session and admin contracts behind neutral types#338

Merged
ben-fornefeld merged 17 commits into
mainfrom
pr-1-dashboard-auth-session-boundary-eng-3881
May 22, 2026
Merged

refactor(auth): split session and admin contracts behind neutral types#338
ben-fornefeld merged 17 commits into
mainfrom
pr-1-dashboard-auth-session-boundary-eng-3881

Conversation

@ben-fornefeld
Copy link
Copy Markdown
Member

@ben-fornefeld ben-fornefeld commented May 21, 2026

Summary

  • Introduce AuthProvider (session) and AuthAdmin (lookups) contracts with a neutral AuthUser type.
  • Move all Supabase SDK calls behind supabase/ providers and flows.
  • Add ory/ stubs and an AUTH_PROVIDER flag in index.ts so the provider is swapped in one place.

Introduce a provider abstraction for server auth reads so the dashboard can move off direct Supabase session access ahead of the Ory migration.
@ben-fornefeld ben-fornefeld requested a review from drankou as a code owner May 21, 2026 05:46
Copilot AI review requested due to automatic review settings May 21, 2026 05:46
@vercel
Copy link
Copy Markdown

vercel Bot commented May 21, 2026

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

Project Deployment Actions Updated (UTC)
web Ready Ready Preview, Comment May 22, 2026 8:12pm
web-juliett Ready Ready Preview, Comment May 22, 2026 8:12pm

Request Review

@linear-code
Copy link
Copy Markdown

linear-code Bot commented May 21, 2026

ENG-3881

@cla-bot cla-bot Bot added the cla-signed label May 21, 2026
@cursor
Copy link
Copy Markdown

cursor Bot commented May 21, 2026

PR Summary

High Risk
High risk because it rewires authentication/session handling across routes, middleware, and actions; enabling AUTH_PROVIDER=ory will currently force all requests to be treated as unauthenticated due to stubbed implementations.

Overview
This changes auth/session resolution across routes, tRPC middleware, server actions, and the proxy to use a new AuthProvider/AuthAdmin abstraction and a normalized AuthUser shape, removing direct createClient()/admin usage and deleting getSessionInsecure/getUserByToken.

What looks wrong: setting AUTH_PROVIDER=ory will effectively disable authentication because the Ory provider/admin are stubs that always return unauthenticated/null and signOut is not implemented. checkAuthProviderHealth now fails closed when Supabase env vars are missing (breaking sign-in/up flows in misconfigured environments), and the new header-based Supabase SSR client appends Set-Cookie to the passed Headers, which may not behave as expected depending on how those headers are used downstream.

Reviewed by Cursor Bugbot for commit 97f45f0. Bugbot is set up for automated code reviews on this repo. Configure here.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new “session boundary” for dashboard auth by adding an AuthSessionProvider abstraction (with a Supabase-backed implementation) and routing key server-side session/auth reads through a small facade. This sets the codebase up to swap the underlying auth provider later (e.g., Ory) while keeping current behavior mostly consistent.

Changes:

  • Added an AuthSessionProvider abstraction plus a Supabase implementation (SupabaseAuthSessionProvider) and a selector + facade API (getAuthContext, signOut, etc.).
  • Removed getSessionInsecure and updated middleware/routes/server actions to use the new auth facade/provider.
  • Updated proxy middleware-like auth detection to use the new provider and added safer env var handling.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/proxy.ts Uses the new session provider to determine auth state; adds env var guards.
src/core/server/trpc/init.ts Replaces Supabase Session typing with a minimal session shape in TRPC context.
src/core/server/functions/auth/get-session.ts Deletes getSessionInsecure helper.
src/core/server/auth/session.ts Adds facade functions for auth context/access token/sign-out.
src/core/server/auth/session.supabase.ts Adds Supabase-backed AuthSessionProvider implementation.
src/core/server/auth/session-selector.ts Adds provider selection entrypoint (currently always Supabase).
src/core/server/auth/session-provider.ts Adds base abstraction + MissingAuthSessionError.
src/core/server/api/middlewares/auth.ts Switches TRPC auth middleware to use the new provider.
src/core/server/actions/client.ts Switches safe-action auth to use getAuthContext and a minimal session shape.
src/core/server/actions/auth-actions.ts Routes sign-out through facade; tweaks auth provider health check env usage.
src/app/sbx/new/route.ts Uses getAuthContext() instead of direct Supabase calls + insecure session reads.
src/app/dashboard/route.ts Uses getAuthContext() + signOut() facade for dashboard redirect logic.
src/app/dashboard/account/route.ts Uses getAuthContext() + signOut() facade for account redirect logic.
src/app/dashboard/[teamSlug]/layout.tsx Uses getAuthContext() to obtain token for user resolution.
src/app/dashboard/(resolvers)/inspect/sandbox/[sandboxId]/route.ts Uses getAuthContext() for auth gating and token sourcing.
src/app/api/teams/[teamSlug]/metrics/route.ts Uses getAuthContext() for auth gating and token sourcing.
src/app/(auth)/auth/cli/page.tsx Uses getAuthContext() to supply email + token for CLI callback.
Comments suppressed due to low confidence (1)

src/core/server/auth/session.supabase.ts:30

  • client.auth.getSession() is called without the previous warning suppression that existed in getSessionInsecure, so Supabase's “getSession() is potentially insecure” warnings will likely reappear in logs. If the goal is still to avoid noisy production logs, consider reintroducing a narrowly-scoped suppression here (or switching to an approach that doesn’t call getSession() when only presence/identity is needed).
    const {
      data: { session },
    } = await client.auth.getSession()

    if (!session?.access_token) {
      return null

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/core/server/auth/session.supabase.ts Outdated
Comment thread src/core/server/actions/client.ts
Comment thread src/proxy.ts Outdated
Comment thread src/core/server/actions/auth-actions.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b67f816e08

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/core/server/actions/client.ts Outdated
Comment thread src/core/server/actions/client.ts Outdated
Match the new session provider behavior in proxy integration tests by mocking both user and session reads.
Comment thread src/core/server/auth/session.supabase.ts Outdated
Comment thread src/proxy.ts
Avoid silently bypassing proxy auth when Supabase env validation should already guarantee required values.
Use the existing process env typing directly in proxy Supabase client setup.
Comment thread src/core/server/actions/client.ts
Replace standalone session helper exports with an authProvider facade and factory for injected auth clients.
Use an authContext getter on auth providers so call sites read from the selected provider object directly.
Align the provider API by using async getters for both auth context and access token.
Introduce typed ManagedSupabase and HostedOry provider adapters, and route Supabase auth SDK usage through the provider boundary.
Comment thread src/core/server/auth/auth-provider.ts Outdated
Comment thread src/core/server/auth/managed-supabase-auth-provider.ts Outdated
ben-fornefeld and others added 3 commits May 21, 2026 16:33
Restructure the auth module into a flag-switchable provider contract that
no longer leaks Supabase types into the app. Introduces an `AuthProvider`
contract for session reads, an `AuthAdmin` contract for user lookups, and
a neutral `AuthUser` model so consumers stop depending on
`@supabase/supabase-js` shapes. Provider and admin implementations live
under `supabase/` and `ory/` (stub) and are selected via the
`AUTH_PROVIDER` flag in a single place.
Removes the leftover supabase/client.ts that was superseded by
server-client.ts, deletes the unused createdAt field on AuthUser,
collapses flags.ts to just isOryAuthEnabled(), and surfaces the
Ory stubs as rejected promises so callers see consistent failure
shapes instead of synchronous throws.
Comment thread src/proxy.ts
Comment thread src/proxy.ts
@ben-fornefeld ben-fornefeld changed the title refactor(auth): add dashboard session boundary refactor(auth): split session and admin contracts behind neutral types May 22, 2026
- Bake the resolved AuthUser into AuthContext so middleware/layouts no
  longer need a second admin getUser round-trip per authenticated request.
- Make Ory stubs return null/empty instead of throwing so flag-flips can't
  fall through proxy's catch-all into protected routes.
- Validate NEXT_PUBLIC_SUPABASE_URL alongside the anon key in the auth
  health probe.
Removes the empty ory/urls.ts stub and demotes type-only re-exports and
internal builders that nothing imports today. Will be reintroduced when
Ory hosted UI is wired.
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 54af3c7. Configure here.

Comment thread src/core/server/auth/admin.ts Outdated
…ession-boundary-eng-3881

# Conflicts:
#	src/core/server/api/middlewares/auth.ts
…ession-boundary-eng-3881

# Conflicts:
#	src/core/server/actions/user-actions.ts
The method had three definition sites (interface + supabase + ory) and
zero callers since AuthContext started carrying the resolved AuthUser
directly. Cuts the per-provider implementation surface and removes a
trap for the next contributor adding an Ory provider.
The new auth boundary (PR #338) swallowed Supabase failures and returned
null, leaving the proxy, tRPC middleware, and route handlers unable to
distinguish a real outage from a legitimate logged-out user.

- Log getUser / getSession / signOut / health-check failures with stable
  keys (auth_provider:*) so prod issues are immediately visible.
- Switch AuthProvider.signOut to return { error } instead of throwing,
  matching Supabase's value-style errors; updateUserAction now logs
  update_user_action:sign_out_others_failed when post-password-change
  session invalidation fails.
- Log auth_admin:get_user_by_id:error and auth_admin:get_emails_by_ids:error
  inside supabaseAuthAdmin so degraded member listing is no longer silent.
- Add email_callback:supabase_error so failed email changes are traceable.
- Add trpc_auth_middleware:no_session warn alongside the existing span
  status, restoring boundary-level logging dropped during the refactor.
- Make the Ory stub provider loud per request and return a stub error
  value instead of rejecting.
- Add unit tests for the provider and admin logging paths.
ben-fornefeld added a commit that referenced this pull request May 22, 2026
Squashed baseline of ENG-3881 work to start ENG-4125 (Auth.js + hosted
Ory) from a clean history. Will rebase onto main once PR #338 lands.

Behaviorally identical to PR #338 / ENG-3881 latest:
- AuthProvider (session) and AuthAdmin (lookups) interfaces with neutral
  AuthUser / AuthContext / SignOutOptions types
- supabase/ implementation under provider, admin, flows, server-client,
  user converter; logging via auth_provider:* / auth_admin:* keys
- ory/ stubs that fail-closed (return null / stub error)
- AUTH_PROVIDER flag selects provider in src/core/server/auth/index.ts
- All ~30 consumers migrated to auth.* / authAdmin.* / supabaseAuthFlows.*
- User type from @supabase/supabase-js no longer leaks outside supabase/
- Single auth roundtrip per authenticated request (AuthContext carries user)
- signOut returns { error } shape; observability hooks wired across
  middleware, actions, and route handlers
@ben-fornefeld ben-fornefeld merged commit e308594 into main May 22, 2026
15 checks passed
@ben-fornefeld ben-fornefeld deleted the pr-1-dashboard-auth-session-boundary-eng-3881 branch May 22, 2026 22:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants