Nextbase Docs
Nextbase Docs
HomeBlogWelcome to Nextbase v5!Choose your starterGetting startedLocal Supabase per agent
TanStack + ConvexInstallationEnvironment variablesAuthenticationAgent connectionsProject structureDeployment
AuthenticationWorkspaces and the admin area
Building with agentsEmails with Resend
TroubleshootingNextbase v5
The 8 starter kitsTanStack + Convex

Authentication

How Convex Auth works in the TanStack + Convex starter, including verification emails, social login, sessions, the separate admin app, the admin claim flow and impersonation.

This kit uses Convex Auth. The same backend login serves the web app, the admin app, the Convex functions and the agent connections, but the web and admin apps hold separate sessions on their own origins. For the concepts shared across all kits, read the v5 authentication guide.

Where the configuration lives

PiecePath
Providersapps/database/convex/auth.ts
Redirect allowlistapps/database/convex/lib/auth_redirect.ts, called from the redirect callback in auth.ts with SITE_URL and ADMIN_SITE_URL
JWT domain for Convexapps/database/convex/auth.config.ts (uses CONVEX_SITE_URL)
Auth HTTP routesapps/database/convex/http.ts via auth.addHttpRoutes(http)
Client providerapps/web/src/router.tsx and apps/admin/src/router.tsx wrap each router in ConvexAuthProvider
Route guardsapps/web/src/routes/_authenticated.tsx and apps/admin/src/routes/_authenticated.tsx
Web login, sign-up, reset pagesapps/web/src/routes/login.tsx, sign-up.tsx, forgot-password.tsx, update-password.tsx, logout.tsx
Admin login and reset pagesapps/admin/src/routes/login.tsx, forgot-password.tsx, update-password.tsx (no sign-up page)
Social buttons and magic link formcomponents/auth/oauth-buttons.tsx and components/auth/magic-link-form.tsx in both apps
Impersonationapps/database/convex/impersonation.ts, apps/admin/src/lib/impersonation.ts, apps/web/src/routes/auth.impersonate.tsx, apps/web/src/components/impersonation-banner.tsx

Auth tables come from authTables in @convex-dev/auth/server and are spread into apps/database/convex/schema.ts. The kit adds indexes on authVerifiers so account deletion stays bounded.

Signing keys (JWT_PRIVATE_KEY, JWKS) and SITE_URL are written to the selected deployment by pnpm auth:setup. Run it once per deployment. ADMIN_SITE_URL is set separately with pnpm convex env set.

Providers

auth.ts registers six providers:

  • Password with verify set to the Resend provider, so sign-up sends a verification code, and reset set to a dedicated password-reset Email provider that sends a 15 minute link to /update-password?mode=reset built from SITE_URL.
  • Resend (magic link). The login and sign-up pages include a magic link form that calls signIn("resend", ...).
  • Google, GitHub and Twitter from @auth/core/providers.
  • admin-impersonation, a ConvexCredentials provider that exchanges a one-use impersonation token for a session (see below). It is not a login method users see.

All of them read their credentials from the Convex deployment environment: AUTH_RESEND_KEY, AUTH_EMAIL_FROM, AUTH_GOOGLE_ID, AUTH_GOOGLE_SECRET, AUTH_GITHUB_ID, AUTH_GITHUB_SECRET, AUTH_TWITTER_ID and AUTH_TWITTER_SECRET. The environment variables page lists them.

Social buttons are hidden unless the provider id appears in the public VITE_OAUTH_PROVIDERS list of the app you are looking at (the web app and the admin app each have their own env file). Listing a provider does not configure it; set its AUTH_* credentials and register the callback with the provider too. The repository does not document the exact callback path; take it from the Convex Auth documentation for your provider.

Email verification and the local inbox

Sign-up posts flow=signUp to the password provider. If the result is not already signed in, the page navigates to /update-password?mode=verify&email=... where you enter the code from the email.

There is no local inbox in this kit. Verification, reset and magic link emails are sent by Resend from the Convex deployment, even locally. To test these flows, set AUTH_RESEND_KEY and AUTH_EMAIL_FROM in the local deployment:

pnpm convex env set AUTH_RESEND_KEY re_...
pnpm convex env set AUTH_EMAIL_FROM "Nextbase <auth@example.com>"

The apps/email dev server on port 3001 previews the React Email templates for app emails (invitation, email change, account deletion). It is not an inbox and does not render the Convex Auth emails. The shared emails guide covers Resend for the Convex kits.

Password sign-in without email

The README says password sign-in works right after pnpm auth:setup, and it does for an existing account. Creating a new account at /sign-up still needs Resend, because the sign-up UI routes to the verification step when the provider does not return a signed-in session. Configure Resend before testing a complete sign-up locally.

Sessions and route protection

Each browser origin holds its own Convex Auth session; Convex functions read it with getAuthUserId. The web app's _authenticated layout route is client-only (ssr: false). It shows a loader while auth resolves, redirects unauthenticated visitors to /login?returnTo=..., and sends users who have not finished onboarding to /onboarding.

The admin app's _authenticated layout does the same on the admin origin, then checks admin.accessStatus: administrators see the admin pages, the account matching NEXTBASE_ADMIN_EMAIL is sent to /app-admin to claim access, and everyone else sees "Administrator access required" with links to open the web app or sign out.

The redirect callback in auth.ts only allows redirects to SITE_URL (the web origin) and ADMIN_SITE_URL (the admin origin). OAuth and magic link callbacks started from the admin login return to the admin origin, so ADMIN_SITE_URL must be set in every deployment where the admin app signs in.

Router guards are navigation UX only. Every public Convex function performs its own checks through the wrappers in apps/database/convex/lib/functions.ts:

WrapperCheck
authenticatedQuery / authenticatedMutationA valid user; mutations also refuse accounts with a running or failed deletion job
workspaceMemberQuery / workspaceMemberMutationMembership in workspaceId; mutations also refuse workspaces being deleted
workspaceManagerMutationMembership with role owner or admin
platformAdminQuery / platformAdminMutationA profiles row with platformRole: "admin"

Workspace roles are owner, admin, member and readonly (apps/database/convex/lib/validators.ts). Mutations run through convex-helpers triggers that keep the user, workspace, project and invitation aggregates in sync. Elevated work is internal-only, and external effects (emails, Stripe, deletions) are recorded as durable externalJobs before a workflow runs them.

Sign out of the web app at /logout. Signing out ends any agent connection that was approved in that session.

Becoming an application admin

Admin status is a platformRole on the user's profile, separate from workspace roles. The admin area lives in the separate admin app (apps/admin, http://localhost:3002 locally), not in the web app. The first admin claims it:

Set the trusted email and the admin origin in the Convex deployment (local or production):

pnpm convex env set NEXTBASE_ADMIN_EMAIL admin@example.com
pnpm convex env set ADMIN_SITE_URL http://localhost:3002

Create the account with exactly that address in the web app (the admin app has no sign-up page), then open the admin app and sign in with it. The admin app redirects / to /app-admin.

Click Claim admin access. admin.claim in apps/database/convex/admin.ts compares the signed-in email with NEXTBASE_ADMIN_EMAIL, sets platformRole: "admin" and writes a platform_admin.claimed entry to adminAuditLogs.

The button only appears when the emails match (admin.accessStatus.canClaim). Other accounts see "Administrator access required". The repository does not include a UI for promoting further admins; the adminUsers.ts functions are the place to look if you need one.

The admin app serves /app-admin with users (list and detail), workspaces, payment gateway (customers, subscriptions, invoices), feedback, marketing, audit, settings and /app-admin/connections for the Admin MCP. The repository's docs/separate-admin-app.md describes the split.

Impersonation

From a user's detail page in the admin app, Impersonate in web app asks for a support reason. impersonation.create requires an active platform administrator session, creates a random one-use token, stores only its SHA-256 hash and writes the reason to the audit log. The admin app builds https://<web origin>/auth/impersonate#token=... from VITE_WEB_APP_URL (HTTPS is required outside localhost) and opens it.

The web app's /auth/impersonate route removes the fragment, signs in with the admin-impersonation provider, which runs impersonation.consume, and navigates to the customer app. Handoffs expire after 60 seconds; the resulting support session expires after 15 minutes. Expired or replayed links, ended admin sessions, demoted administrators, deleted or ineligible users and administrator targets are rejected. An impersonation banner in the web app offers End support session, which signs out the temporary session and returns to the admin app's user list through VITE_ADMIN_APP_URL. The administrator's own session on the admin origin is untouched.

HTTP API keys

Separately from sessions, users can create API keys at /user/settings/developer in the web app. Keys are shown once and stored as a SHA-256 hash; they carry scopes and authorize GET /api/v1/me, GET /api/v1/workspaces and POST /api/ai/stream on the Convex site URL with a Bearer nb_live_... header. These keys are not MCP credentials.

Environment variables

Every variable in the starter's two example env files, which runtime reads it, and where its value comes from.

Agent connections

User MCP and Admin MCP in the TanStack + Convex starter: endpoints, tools, consent and how to extend them.

On this page

Where the configuration livesProvidersEmail verification and the local inboxSessions and route protectionBecoming an application adminImpersonationHTTP API keys