Authentication
Configure Better Auth, verified email, OAuth, two-factor authentication, API keys, and administration.
Authentication ownership
packages/auth/src/index.ts creates Better Auth with D1, mail, API keys, two-factor, and administration plugins. Requests under /api/auth/* go to Better Auth. Page checks live in apps/web/src/routes/$.tsx, while business APIs enforce authorization again in apps/web/src/server/api.ts.
Distinguish signed-out visitors, users, administrators, and banned or unverified accounts. Do not accept a role directly from an ordinary browser request, and do not rely on whether the sidebar displays an admin link.
Default email-code sign-in
At /auth/login, request a six-digit code and submit the newest code. Existing addresses sign in; new addresses can create accounts. Read local messages at /dev/mail and production messages in the real mailbox. Retain resend, change-address, and two-factor redirects. Select “Use password” for /auth/password.
The Better Auth endpoints are /api/auth/email-otp/send-verification-otp with type sign-in and /api/auth/sign-in/email-otp. Do not store codes independently or validate them only in the browser.
Password registration, verification, and recovery
- Register through
/auth/register. - Follow the verification email. The current configuration requires email verification and supports automatic sign-in after verification.
- Sign in with a password at
/auth/passwordand check that the workbench loads your own data. - Use
/auth/forgot-passwordand the emailed link to complete/auth/reset-password.
Development messages appear at /dev/mail. This inbox must remain unavailable in production. Treat reset links and verification codes as credentials, and keep them out of public screenshots and logs. Local mail capture does not prove production delivery.
Configure OAuth and Turnstile
Use local .dev.vars or production root .env as appropriate:
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret
GITHUB_CLIENT_ID=your-client-id
GITHUB_CLIENT_SECRET=your-client-secret
TURNSTILE_SITE_KEY=your-site-key
TURNSTILE_SECRET=your-secret-keyGoogle uses https://your-domain.com/api/auth/callback/google; GitHub uses the corresponding github callback. Register the exact local origin separately. Better Auth trustedOrigins come from APP_URL. Locale page prefixes do not change API callback paths.
The UI reads public provider settings from /api/public/auth. Fix an incomplete credential pair instead of forcing an unusable OAuth button to appear. Turnstile also requires the correct allowed domains; its secret remains server-side.
Sessions and two-factor authentication
/app/settings contains profile, session, API key, and two-factor controls. Profile updates use Better Auth update-user. Session listing/revocation and API key creation/deletion use their dedicated auth APIs.
After enabling TOTP, verify the code and retain recovery material before testing a new login through /auth/verify-two-factor. Revoke a test session and confirm that it can no longer request protected data. A failed security operation must show a meaningful error rather than merely changing the button state.
API key boundaries
Pass a file key in x-api-key. Current access covers file listing, uploads, downloads, and deletion. The server still verifies that the owner has verified email, is not banned, and owns the requested resource.
Retain the full key when it is first displayed. Lists and logs should not reveal it again. File keys do not grant chat, checkout, or administrative access. Cloudflare deployment tokens are separate credentials.
Administration and isolation
With ADMIN_EMAIL configured, the matching production user must still register and verify email. Production does not seed local_admin. The admin page is /app/admin.
Administrative APIs cover users, orders, tasks, webhooks, audit, refunds, and dead letters. Role changes accept admin/user and forbid self-role changes. Credit grants use a positive integer amount and a UUID requestId for idempotency. Retain server restrictions and object-specific confirmation for refunds and event replay.
Verify the complete flow
- Signed-out API access fails; normal users cannot access admin data.
- User A cannot read or mutate user B's files, orders, tasks, or sessions.
- Exercise OAuth, verification, recovery, TOTP, revoked sessions, and deleted keys.
- For login loops, compare APP_URL, cookie origin, HTTPS settings, and whether AUTH_SECRET changed.
- For missing verification mail, distinguish local capture from real delivery using the email guide.
Continue with API architecture and testing.