Skip to content

chore: migrate frontend auth from Clerk to Better Auth#4568

Draft
NathanFlurry wants to merge 8 commits intomainfrom
04-04-chore_migrate_to_better-auth
Draft

chore: migrate frontend auth from Clerk to Better Auth#4568
NathanFlurry wants to merge 8 commits intomainfrom
04-04-chore_migrate_to_better-auth

Conversation

@NathanFlurry
Copy link
Copy Markdown
Member

Summary

  • Replace ClerkProvider with Better Auth client in the frontend
  • Rewrite sign-in, sign-up, and onboarding pages to use Better Auth
  • Update org switching, context providers, and E2E test auth setup
  • Remove all Clerk dependencies and references

Test plan

  • Sign in / sign up flows work end-to-end
  • Google OAuth flow works
  • Org creation and switching works
  • E2E tests pass with new auth setup

🤖 Generated with Claude Code

@railway-app
Copy link
Copy Markdown

railway-app bot commented Apr 6, 2026

🚅 Deployed to the rivet-pr-4568 environment in rivet-frontend

Service Status Web Updated (UTC)
kitchen-sink ❌ Build Failed (View Logs) Web Apr 14, 2026 at 7:45 pm
frontend-inspector ❌ Build Failed (View Logs) Web Apr 6, 2026 at 10:05 pm
frontend-cloud ❌ Build Failed (View Logs) Web Apr 6, 2026 at 10:01 pm
website ❌ Build Failed (View Logs) Web Apr 6, 2026 at 10:01 pm
ladle ❌ Build Failed (View Logs) Web Apr 6, 2026 at 10:00 pm
mcp-hub ✅ Success (View Logs) Web Apr 6, 2026 at 9:56 pm

Copy link
Copy Markdown
Contributor

jog1t commented Apr 6, 2026

@claude
Copy link
Copy Markdown

claude bot commented Apr 6, 2026

PR Review: Migrate Frontend Auth from Clerk to Better Auth

Overview

This PR replaces Clerk with Better Auth across the frontend. It removes ~2,500 lines of Clerk-specific code (ClerkProvider, Clerk Elements sign-in/sign-up flows, SSO callback routes, invitation acceptance, waitForClerk utility) and replaces them with Better Auth's createAuthClient plus cookie-based sessions. The overall direction is correct and the diff is clean, but there are several issues worth addressing before merging.


Issues

E2E test auth flow is likely broken (e2e/auth.setup.ts)

The setup now calls request.post to sign in via the Better Auth API, then navigates to /. Playwright's request fixture uses a separate network context from page - cookies set via request.post are not automatically shared with the browser page. The session cookie won't be present when page.goto("/") runs, so the auth state will never be saved. The test should sign in through the browser UI (fill the form on /login) or use Playwright's APIRequestContext attached to the browser context.

Hardcoded base URL in E2E test

const baseURL = "http://localhost:43710" is hardcoded in auth.setup.ts. This will fail in any environment that doesn't use that exact port. Should use process.env.BASE_URL or the Playwright config's base URL.

Sentry/PostHog user identification silently removed

waitForClerk.ts included Sentry.setUser and posthog.setPersonProperties calls. Deleting it without a replacement means user identification for error tracking and analytics is gone. This is an observability regression.

Unconditional setActive on every org navigation (routes/_context/_cloud/orgs.$organization.tsx)

The old code only called clerk.setActive when the org ID changed. The new code calls authClient.organization.setActive unconditionally on every navigation to an org route. This makes a network request on every route load and could cause unnecessary session updates or flicker.

Duplicated slug generation logic

Slug generation with a random suffix appears in both create-organization-frame.tsx and choose-organization.tsx. Extract it to a shared utility (e.g. lib/slug.ts).

Redundant inner try/catch in login handler (src/app/login.tsx)

The inner try/catch in handleSubmit that immediately re-throws adds no value; remove it and let the outer catch handle redirect errors.

Profile/Settings/Members menu items removed without replacement (user-dropdown.tsx)

The user profile, org settings, and org members DropdownMenuItems that called Clerk's openUserProfile / openOrganizationProfile were removed. If these features are intended to still exist (e.g. via a dedicated settings route), they need to be wired up; otherwise this is a user-visible regression.

Missing email verification flow

Clerk handled email verification as part of sign-up. It's not clear whether Better Auth's email verification is configured or whether new sign-ups are automatically considered verified. If email verification is required, the sign-up success path should handle the unverified state.


Observations (non-blocking)

  • Cookie-based auth plus CORS: withCredentials: true is added to the fetcher and credentials: "include" to the SSE stream, which is the right pattern. Confirm the server sets SameSite / Domain appropriately for the expected deployment topology.
  • token: async () => "" in createClient: Fine given auth is cookie-based, but worth a comment so future readers understand it's intentional (cookies carry auth, not Bearer tokens).
  • setActive before redirect: authClient.organization.setActive is called before throwing router redirects in several places. Confirm it actually persists before the redirect fires, or the active org may not reflect correctly on the next page load.
  • Null cast for non-cloud mode: The APP_TYPE guard with null cast is carried over from the Clerk pattern. Calling any authClient method in non-cloud mode throws at runtime with no useful error. A thin assertion wrapper would surface this more clearly.

Summary

The migration is well-scoped and removes a large external dependency cleanly. The main blocker is the E2E test setup (cookies won't transfer between Playwright's request context and page context), and the missing Sentry/PostHog identification is a silent observability regression. The other items above are worth fixing before shipping.

@jog1t jog1t mentioned this pull request Apr 8, 2026
11 tasks
NathanFlurry and others added 8 commits April 14, 2026 20:10
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ropdown

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ences

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

Preview packages published to npm

Install with:

npm install rivetkit@pr-4568

All packages published as 0.0.0-pr.4568.54ca6fd with tag pr-4568.

Engine binary is shipped via @rivetkit/engine-cli on linux-x64-musl, linux-arm64-musl, darwin-x64, and darwin-arm64. Windows users should use the release installer or set RIVET_ENGINE_BINARY.

Docker images:

docker pull rivetdev/engine:slim-54ca6fd
docker pull rivetdev/engine:full-54ca6fd
Individual packages
npm install rivetkit@pr-4568
npm install @rivetkit/react@pr-4568
npm install @rivetkit/rivetkit-native@pr-4568
npm install @rivetkit/sqlite-wasm@pr-4568
npm install @rivetkit/workflow-engine@pr-4568

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