Skip to content

Add Ory Auth.js integration#342

Draft
ben-fornefeld wants to merge 3 commits into
mainfrom
pr-2-dashboard-as-hydra-oidc-client-ory-elements-integration-eng-4125
Draft

Add Ory Auth.js integration#342
ben-fornefeld wants to merge 3 commits into
mainfrom
pr-2-dashboard-as-hydra-oidc-client-ory-elements-integration-eng-4125

Conversation

@ben-fornefeld
Copy link
Copy Markdown
Member

Summary

  • Add Auth.js/Ory Hydra OAuth integration behind the AUTH_PROVIDER switch.
  • Wire Ory session handling, hosted auth redirects, logout, proxy auth, and infra API headers while preserving Supabase mode.
  • Update dashboard-api types for admin OIDC user bootstrap and cover the Ory auth path with tests.

Test plan

  • bun run tsc --noEmit
  • bun test tests/unit/auth-headers.test.ts tests/unit/auth-ory-provider.test.ts tests/integration/auth-ory-bootstrap.test.ts

Copilot AI review requested due to automatic review settings May 25, 2026 01:04
@ben-fornefeld ben-fornefeld requested a review from drankou as a code owner May 25, 2026 01:04
@vercel
Copy link
Copy Markdown

vercel Bot commented May 25, 2026

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

Project Deployment Actions Updated (UTC)
web Ready Ready Preview, Comment May 25, 2026 2:30am
web-juliett Ready Ready Preview, Comment May 25, 2026 2:30am

Request Review

@linear-code
Copy link
Copy Markdown

linear-code Bot commented May 25, 2026

ENG-4125

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

cursor Bot commented May 25, 2026

PR Summary

High Risk
Touches authentication, session cookies, token refresh, logout, middleware, and how all dashboard/infra API calls are authorized; client terminal/inspect still read Supabase sessions and may break under full Ory-only usage.

Overview
Adds an Ory path behind AUTH_PROVIDER=ory using Auth.js (Ory Hydra at /api/auth/oauth) with JWT sessions, token refresh, and a sign-out flow that clears Auth.js and Ory via id_token_hint.

When Ory is on, the previous stub provider is replaced with real session resolution; sign-in / sign-up / forgot-password auto-redirect to hosted Ory instead of local forms. On first OAuth sign-in, JWT claims drive an admin /admin/users/bootstrap call to provision the E2B user/team.

API clients now use authHeaders() (Authorization: Bearer + X-Team-ID) instead of Supabase-only headers; the OpenAPI spec adds bearer/team security and admin profile/bootstrap routes. oryAuthAdmin uses Ory Identity API for lookups. Middleware wraps Auth.js for req.auth when Ory is enabled. Supabase mode is unchanged aside from signOut returning a redirectTo URL.

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

Comment thread src/proxy.ts
Comment thread src/core/server/auth/ory/admin.ts
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 an Auth.js + Ory Hydra authentication mode (behind AUTH_PROVIDER) alongside the existing Supabase auth path, and updates request authentication/header wiring plus supporting dashboard-api contract types and tests.

Changes:

  • Add Auth.js (NextAuth) Ory Hydra OAuth integration with session handling, sign-in bootstrap, and Ory sign-out flows.
  • Unify API auth header generation via authHeaders() and route server-side auth through a provider abstraction (auth / authAdmin).
  • Update OpenAPI spec + generated dashboard-api types and add unit/integration tests covering Ory auth paths.

Reviewed changes

Copilot reviewed 85 out of 86 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
vitest.config.ts Inline Auth.js deps for Vitest resolution.
tests/unit/teams-repository.test.ts Update mocks for new authAdmin dependency.
tests/unit/keys-repository.test.ts Update expected error string for provider-agnostic auth.
tests/unit/auth-supabase-provider.test.ts Add unit tests for SupabaseAuthProvider.
tests/unit/auth-supabase-admin.test.ts Add unit tests for supabaseAuthAdmin.
tests/unit/auth-ory-provider.test.ts Add unit tests for oryAuthProvider behavior.
tests/unit/auth-headers.test.ts Add unit tests for header switching.
tests/setup.ts Provide placeholder env for module-load clients.
tests/integration/proxy.test.ts Extend proxy integration mocks for getSession.
tests/integration/auth-ory-bootstrap.test.ts Add integration coverage for Ory bootstrap call.
src/proxy.ts Add Ory-aware middleware auth integration.
src/lib/utils/server.ts Switch token header generation to authHeaders().
src/lib/utils/auth.ts Remove Supabase-only provider extraction helper.
src/lib/env.ts Add Ory/Auth.js env variables to schema.
src/features/dashboard/terminal/sandbox-session.ts Use authHeaders() for E2B SDK calls.
src/features/dashboard/sandbox/inspect/context.tsx Use authHeaders() for inspect sandbox headers.
src/features/dashboard/context.tsx Use AuthUser instead of Supabase User.
src/features/dashboard/account/password-settings.tsx Use AuthUser.providers for email provider checks.
src/features/dashboard/account/name-settings.tsx Use AuthUser.name in forms and comparisons.
src/features/dashboard/account/email-settings.tsx Use AuthUser.providers for email provider checks.
src/features/auth/ory-hosted-auth-redirect.tsx Add client redirect component for hosted Ory auth.
src/core/shared/contracts/dashboard-api.types.ts Regenerate dashboard-api types with new admin endpoints.
src/core/server/trpc/init.ts Update TRPC context session/user types for AuthUser.
src/core/server/functions/sandboxes/get-team-metrics-max.ts Switch to authHeaders() for infra calls.
src/core/server/functions/sandboxes/get-team-metrics-core.ts Switch to authHeaders() for infra calls.
src/core/server/functions/auth/get-user-by-token.ts Remove Supabase-specific cached token lookup.
src/core/server/functions/auth/get-session.ts Remove Supabase getSessionInsecure helper.
src/core/server/auth/types.ts Introduce provider-agnostic auth types.
src/core/server/auth/supabase/user.ts Map Supabase User -> AuthUser.
src/core/server/auth/supabase/server-client.ts Add SSR client wrappers for proxy/headers contexts.
src/core/server/auth/supabase/provider.ts Add SupabaseAuthProvider implementing AuthProvider.
src/core/server/auth/supabase/flows.ts Centralize Supabase auth flows behind helpers.
src/core/server/auth/supabase/admin.ts Add supabaseAuthAdmin implementing AuthAdmin.
src/core/server/auth/provider.ts Define AuthProvider interface.
src/core/server/auth/ory/signout.ts Add signed state + redirect helpers for Ory logout.
src/core/server/auth/ory/provider.ts Add oryAuthProvider using Auth.js session.
src/core/server/auth/ory/identity.ts Map Auth.js session/Ory identity -> AuthUser.
src/core/server/auth/ory/client.ts Add cached Ory Identity API client.
src/core/server/auth/ory/bootstrap.ts Add dashboard-api bootstrap on Auth.js sign-in event.
src/core/server/auth/ory/admin.ts Add oryAuthAdmin for identity/email lookups.
src/core/server/auth/index.ts Wire auth/authAdmin selection + helpers.
src/core/server/auth/admin.ts Define AuthAdmin interface.
src/core/server/api/middlewares/telemetry.ts Update telemetry middleware to AuthUser.
src/core/server/api/middlewares/auth.ts Switch TRPC auth middleware to provider-based auth.
src/core/server/actions/user-actions.ts Use supabaseAuthFlows + provider signOut.
src/core/server/actions/sandbox-actions.ts Switch to authHeaders() for infra DELETE.
src/core/server/actions/ory-auth-actions.ts Add server action wrapper for Auth.js signIn.
src/core/server/actions/client.ts Switch safe-action auth context to provider-based.
src/core/server/actions/auth-actions.ts Route Supabase flows through supabaseAuthFlows; add Ory sign-out redirect.
src/core/modules/webhooks/repository.server.ts Replace SUPABASE_AUTH_HEADERS with authHeaders.
src/core/modules/users/auth-user-emails.server.ts Resolve creator emails via authAdmin (provider-agnostic).
src/core/modules/templates/repository.server.ts Replace SUPABASE_AUTH_HEADERS with authHeaders.
src/core/modules/teams/user-teams-repository.server.ts Replace SUPABASE_AUTH_HEADERS with authHeaders.
src/core/modules/teams/teams-repository.server.ts Replace Supabase admin lookups with authAdmin.getUserById.
src/core/modules/sandboxes/repository.server.ts Replace SUPABASE_AUTH_HEADERS with authHeaders.
src/core/modules/keys/repository.server.ts Replace SUPABASE_AUTH_HEADERS with authHeaders.
src/core/modules/builds/repository.server.ts Replace SUPABASE_AUTH_HEADERS with authHeaders.
src/core/modules/billing/repository.server.ts Replace SUPABASE_AUTH_HEADERS with authHeaders.
src/core/modules/auth/repository.server.ts Route verifyOtp via supabaseAuthFlows helper.
src/configs/flags.ts Add isOryAuthEnabled() feature switch.
src/configs/api.ts Introduce authHeaders() + Ory team header constant.
src/auth.ts Add Auth.js (NextAuth) Ory Hydra provider configuration + token refresh.
src/app/sbx/new/route.ts Switch route auth to provider-based auth + authHeaders.
src/app/dashboard/terminal/page.tsx Switch server auth to provider-based auth + authHeaders.
src/app/dashboard/route.ts Switch server auth to provider-based auth + Ory sign-out on missing team.
src/app/dashboard/account/route.ts Switch server auth to provider-based auth + Ory sign-out on missing team.
src/app/dashboard/[teamSlug]/team-gate.tsx Update prop type to AuthUser.
src/app/dashboard/[teamSlug]/layout.tsx Switch server auth to provider-based auth.
src/app/dashboard/(resolvers)/inspect/sandbox/[sandboxId]/route.ts Switch server auth to provider-based auth + authHeaders.
src/app/api/teams/[teamSlug]/metrics/route.ts Switch route auth to provider-based auth.
src/app/api/auth/oauth/signout-flow/route.ts Add Ory logout flow endpoint.
src/app/api/auth/oauth/signed-out/route.ts Add Ory post-logout callback endpoint.
src/app/api/auth/oauth/[...nextauth]/route.ts Add NextAuth handlers route.
src/app/api/auth/email-callback/route.tsx Route exchangeCodeForSession via supabaseAuthFlows and log errors.
src/app/api/auth/callback/route.ts Route exchangeCodeForSession via supabaseAuthFlows.
src/app/(auth)/sign-up/signup-form.tsx Extract existing signup form into client component.
src/app/(auth)/sign-up/page.tsx Redirect to hosted Ory auth when enabled.
src/app/(auth)/sign-in/page.tsx Redirect to hosted Ory auth when enabled.
src/app/(auth)/sign-in/login-form.tsx Extract existing login form into client component.
src/app/(auth)/forgot-password/page.tsx Redirect to hosted Ory auth when enabled.
src/app/(auth)/forgot-password/forgot-password-form.tsx Extract existing forgot-password form into client component.
src/app/(auth)/auth/cli/page.tsx Switch server auth to provider-based auth for CLI flow.
spec/openapi.dashboard-api.yaml Add auth provider security schemes + new admin endpoints.
package.json Add next-auth and @ory/client-fetch dependencies.
bun.lock Lockfile updates for new dependencies.
.env.example Document Ory/Auth.js env variables and usage.

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

Comment thread src/configs/api.ts Outdated
Comment on lines +11 to +22
export const authHeaders = (
token: string,
teamId?: string
): Record<string, string> => {
const isOry = isOryAuthEnabled()
const headers: Record<string, string> = isOry
? { Authorization: `Bearer ${token}` }
: { [SUPABASE_TOKEN_HEADER]: token }
if (teamId) {
headers[isOry ? AUTH_PROVIDER_TEAM_HEADER : SUPABASE_TEAM_HEADER] = teamId
}
return headers
Comment thread src/proxy.ts
Comment on lines +125 to +127
const proxyWithOryAuth = authjsMiddleware(async (req, _event: NextFetchEvent) =>
proxyCore(req, !!req.auth)
)
Comment on lines +7 to +13
export async function signInWithOryAction(formData: FormData) {
const returnTo = formData.get('returnTo')
const redirectTo =
typeof returnTo === 'string' && returnTo.length > 0
? returnTo
: '/dashboard'
await signIn('ory', { redirectTo })
Comment thread src/lib/env.ts
Comment on lines +17 to +25
AUTH_PROVIDER: z.enum(['supabase', 'ory']).optional(),
AUTH_SECRET: z.string().min(1).optional(),
AUTH_TRUST_HOST: z.string().optional(),
ORY_SDK_URL: z.url().optional(),
ORY_OAUTH2_CLIENT_ID: z.string().min(1).optional(),
ORY_OAUTH2_CLIENT_SECRET: z.string().min(1).optional(),
ORY_OAUTH2_AUDIENCE: z.string().min(1).optional(),
ORY_PROJECT_API_TOKEN: z.string().min(1).optional(),

Comment thread src/auth.ts Outdated
Comment on lines +91 to +99
try {
const credentials = btoa(`${clientId}:${clientSecret}`)
const tokenEndpoint = `${sdkUrl.replace(/\/$/, '')}/oauth2/token`
const response = await fetch(tokenEndpoint, {
method: 'POST',
headers: {
Authorization: `Basic ${credentials}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
Comment on lines +1 to +35
};
};
};
put?: never;
Wire the dashboard to Ory through Auth.js while preserving Supabase mode behind the auth provider switch.
@ben-fornefeld ben-fornefeld force-pushed the pr-2-dashboard-as-hydra-oidc-client-ory-elements-integration-eng-4125 branch from 0b18e9c to 77a5a85 Compare May 25, 2026 01:32
@ben-fornefeld ben-fornefeld marked this pull request as draft May 25, 2026 01:32

const userId = data.session.user.id
const headers = SUPABASE_AUTH_HEADERS(data.session.access_token, teamId)
const headers = authHeaders(data.session.access_token, teamId)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ory breaks browser terminal inspect

High Severity

With AUTH_PROVIDER=ory, browser terminal and sandbox inspect still call supabase.auth.getSession() for E2B SDK headers. There is no Supabase session in Ory mode, so connect/create fails or sends users to sign-in despite a valid Auth.js session.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0386667. Configure here.

Comment thread src/proxy.ts
}

const proxyWithOryAuth = authjsMiddleware(async (req, _event: NextFetchEvent) =>
proxyCore(req, !!req.auth)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Agentic Security Review
Severity: MEDIUM
The Ory middleware path marks a request authenticated using !!req.auth instead of the stricter provider validation used elsewhere (getAuthContext() rejects sessions with missing user.id, missing accessToken, or session.error). This creates an auth-context mismatch in proxy routing decisions.

Impact: Requests tied to invalid/errored sessions can be treated as authenticated for middleware redirect logic, weakening the intended authentication gate on protected navigation paths.

Move the sign-out redirect target into the AuthProvider contract so route
handlers drop their isOryAuthEnabled() branches. Delete the HMAC-state
sign-out machinery (we no longer thread a "you were signed out because X"
message through Hydra). Extract refreshOryToken into its own module and
convert the bootstrap import to a static one.
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.

There are 2 total unresolved issues (including 1 from previous review).

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 b07a33a. Configure here.


const userId = data.session.user.id
const headers = SUPABASE_AUTH_HEADERS(data.session.access_token, teamId)
const headers = authHeaders(data.session.access_token, teamId)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ory client sandbox auth broken

Medium Severity

In Ory mode the proxy treats req.auth as authenticated, but oryAuthProvider.getAuthContext() returns null when the Auth.js session carries session.error (for example after RefreshTokenError). Users can pass middleware yet server routes and actions see no auth context.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b07a33a. Configure here.

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.

2 participants