Better Auth
How Better Auth is used for authentication.
This project uses Better Auth as its core authentication library. It provides a flexible foundation for handling user sessions, credentials, social logins, and more.
Core Concepts
- Adapter Pattern: Better Auth uses adapters to connect to databases. This project utilizes the
prismaAdapterconfigured inpackages/auth/src/index.ts. - Plugins: Functionality like admin features, username handling, and third-party integrations (like Polar) are added via plugins. This project uses:
adminplugin: For user management, impersonation, etc.usernameplugin: Allows sign-in/sign-up using a username in addition to email.openAPIplugin: Generates an OpenAPI reference for auth routes at/api/auth/reference.polarplugin (via@polar-sh/better-auth): Integrates Polar for payments and subscriptions.
- API keys: The
@better-auth/api-keyplugin issueszts_-prefixed keys for REST and MCP (x-api-keyheader). See Public API. - Procedures: tRPC procedures (
publicProcedure,protectedProcedure,adminProcedureinpackages/trpc/src/trpc.ts) usectx.sessionand optionalctx.apiKey. - Configuration: The main
betterAuth({...})call is inpackages/auth/src/index.ts.
Configuration via Environment Variables
Several aspects of Better Auth's behavior can be controlled via environment variables:
Required Core Variables:
BETTER_AUTH_SECRET: A secret string used for signing session tokens. Must be set for production.NEXT_PUBLIC_APP_URL: The absolute URL of your application (e.g.,https://yourapp.com). This is used by Better Auth for constructing callback URLs. Defaults tohttp://localhost:3000if not set explicitly, but should be set for production.
Optional Feature Toggles (Defaults Shown):
NEXT_PUBLIC_AUTH_ENABLE_EMAIL_PASSWORD_AUTHENTICATION=true: Enables or disables the standard email/password login and signup functionality.AUTH_ENABLE_CHANGE_EMAIL=false: Enables or disables the built-in (but currently basic) flow for users to change their email address.AUTH_AUTO_SIGN_IN_AFTER_VERIFICATION=true: If email verification is enabled (NEXT_PUBLIC_AUTH_ENABLE_EMAIL_VERIFICATION=true), setting this totruewill automatically sign the user in after they successfully click the verification link. If set tofalse, they will need to sign in manually after verifying their email.NEXT_PUBLIC_AUTH_ENABLE_EMAIL_VERIFICATION=true: Toggles whether email verification is required after signing up with email/password.
These variables are defined in packages/env/src/schemas and should be set in root .env.
Server-Side Usage (tRPC)
Better Auth integrates tightly with tRPC to provide authenticated procedures.
Public vs. Protected Procedures (packages/trpc/src/trpc.ts)
-
publicProcedure: Use this for API endpoints accessible to anyone (logged in or not). Inside a public procedure,ctx.sessionmight benull. Always check forctx.session?.userbefore accessing user data. -
protectedProcedure: Use this for endpoints that require a logged-in user. This procedure automatically verifies the session.- If no valid session exists, it throws an
UNAUTHORIZEDTRPCError. - Inside a protected procedure,
ctx.session.useris guaranteed to exist and be non-nullable. - Accessing User ID: Get the current user's ID directly via
ctx.session.user.id.
- If no valid session exists, it throws an
-
adminProcedure: ExtendsprotectedProcedureand adds a check to ensurectx.session.user.role === 'admin'. Use this for admin-only actions.
Server-Side Session Helper (packages/auth/src/index.ts)
-
getServerSession: A cached helper function (cachefrom React) to securely get the current session within Server Components or traditional Next.js API routes (outside of tRPC).
Client-Side Usage
Fetching User Data (apps/web/src/hooks/useCurrentUser.ts)
-
useCurrentUser()Hook: This is the recommended way to get the current user's full data object (including preferences, role, custom fields) in client components.- It internally calls the
api.user.getCurrentUsertRPC query. - It uses
authClient.useSession()only to enable/disable the query (won't run if not logged in). - Advantage: Leverages React Query for caching, background updates, and cache invalidation. If you update user data with a tRPC mutation, invalidating the
getCurrentUserquery will automatically update the data provided by this hook throughout your app. - Returns
undefinedduring initial loading, error states, or if the user is not logged in.
- It internally calls the
Basic Auth Actions (packages/auth/src/client.ts)
authClient: Provides client-side functions for common authentication actions.authClient.useSession(): Hook to get basic session status (logged in/out) and minimal user data (e.g., email). Use this primarily for quickly checking authentication state, not for detailed user profile data (useuseCurrentUserfor that).authClient.signIn.email({...}): Sign in using email and password.authClient.signUp.email({...}): Sign up using email and password (and username if the plugin is active).authClient.signOut(): Log the user out.- These are typically used within forms or UI components (e.g.,
apps/web/src/app/(landing)/(auth)/signin/signin-form.tsx).
Key files
packages/auth/src/index.ts— Better Auth server config (admin, username, apiKey, mcp, polar plugins)packages/auth/src/client.ts— Browser auth clientpackages/trpc/src/trpc.ts— Procedures and context (session,apiKey)packages/env/src/schemas/auth.ts— Auth-related env vars.env.example— Root template
Further Reading
For more in-depth information on Better Auth's capabilities and configuration options, refer to the official Better Auth documentation.