Spacerr uses Better Auth as the authentication layer. The app includes Google OAuth, email and password sign in, magic links, password reset, passkeys, two factor authentication, admin roles, sessions, and account deactivation.
What It Includes
Authentication is built as a real product flow, not a placeholder login screen.
- Google OAuth sign in and sign up.
- Email and password sign in and sign up.
- Magic link sign in.
- Password reset.
- Passkey sign in.
- Two factor authentication with TOTP and backup codes.
- Last used login method hints.
- Session cookies with cached session reads.
- Admin, moderator, and user roles.
- Account deactivation and reactivation.
- Stripe customer creation on signup through Better Auth Stripe integration.
- Welcome, verification, magic link, and password reset emails.
Source Files
The main server configuration lives in src/features/auth/lib/auth.ts.
export const auth = betterAuth({
plugins: [
admin({
defaultRole: "user",
adminRole: "admin",
}),
passkey({
rpName: SiteConfig.name,
rpID: ServerEnv.BETTER_AUTH_PASSKEY_RP_ID,
origin: ServerEnv.NEXT_PUBLIC_DOMAIN,
}),
magicLink({
sendMagicLink: async ({ email, url }) => {
await sendMagicLinkEmail({ to: email, url })
},
}),
twoFactor({
issuer: SiteConfig.name,
}),
lastLoginMethod(),
stripe({
stripeClient,
stripeWebhookSecret: ServerEnv.STRIPE_WEBHOOK_SECRET,
createCustomerOnSignUp: true,
}),
nextCookies(),
],
})The browser client lives in src/features/auth/lib/auth-client.ts.
export const authClient = createAuthClient({
baseURL: ClientEnv.NEXT_PUBLIC_DOMAIN,
plugins: [
adminClient(),
passkeyClient(),
twoFactorClient({
twoFactorPage: WebRoutes.signIn.path,
}),
lastLoginMethodClient(),
],
})Database Backing
Better Auth stores sessions, OAuth accounts, verification tokens, passkeys, and two factor state in the database. The product adds user fields for roles, account state, Stripe customer data, and preferences. See the Database section for database details.
Routes
Auth routes are centralized in WebRoutes.
export const WebRoutes = {
signIn: createRoute("routes.signIn", "/sign-in"),
signUp: createRoute("routes.signUp", "/sign-up"),
forgotPassword: createRoute("routes.resetPassword", "/forgot-password"),
resetPassword: createRoute("routes.resetPassword", "/reset-password"),
verifyEmail: createRoute("routes.verifyEmail", "/verify-email"),
}The Better Auth API route is handled by src/app/api/auth/[...all]/route.ts. UI pages are thin route files that render the feature owned auth components.
Session Behavior
Sessions last 30 days and use cookie caching for fast reads.
session: {
expiresIn: 60 * 60 * 24 * 30,
updateAge: 60 * 60 * 24,
cookieCache: {
enabled: true,
maxAge: 60 * 15,
},
}When a session is created, the app clears deactivatedAt and updates lastActiveAt. This lets normal sign in reactivate accounts after the user confirms the reactivation flow.
Where To Customize
Use these files first:
src/features/auth/lib/auth.tsfor server side Better Auth configuration.src/features/auth/lib/auth-client.tsfor browser auth plugins.src/features/auth/lib/auth.actions.tsfor sign in, sign up, magic link, reset password, and reactivation actions.src/features/auth/lib/auth.schema.tsfor auth form validation.src/features/auth/componentsfor auth UI.src/features/settings/components/settings-account-section/settings-account-section.tsxfor passkeys, password, and two factor account settings.
Keep provider setup values in .env.local, but keep app behavior inside the auth feature.