Authentication
Authentication (@repo/auth)
This package handles all authentication concerns for the project, providing both server-side logic and client-side utilities. It leverages the better-auth library, configuring it with project-specific settings.
Why You Need This
Reliable authentication is crucial for any SaaS product. This package offers secure defaults and a plug-in system so you can enable features like organization management or API keys without starting from scratch. For setup instructions see Getting Started.
About better-auth
better-auth is a modern, modular, and type-safe authentication library designed for TypeScript applications. It aims to simplify the implementation of complex authentication flows while adhering to security best practices.
Why We Chose better-auth
The decision to use better-auth was based on several key factors that align with our project's goals and principles:
- Modularity and Extensibility: Its plugin-based architecture allows us to easily enable or disable features (like Organization Management, API Keys, Email OTP, Expo support) as needed. This fits perfectly with our modular monorepo structure.
- Type Safety:
better-authis built with TypeScript first, offering strong type safety and inference capabilities (e.g.,inferAdditionalFieldsfor the client). This aligns with our core principle of using TypeScript rigorously. - Database Agnostic (with Drizzle Adapter): The library supports various database adapters, including a first-party Drizzle adapter (
better-auth/adapters/drizzle), which integrates seamlessly with our@repo/databasepackage. - Modern Practices & Security: It handles many underlying complexities of authentication (session management, secure token handling, etc.), helping us avoid common pitfalls associated with implementing custom authentication schemes [[Cite: https://blog.gitguardian.com/authentication-and-authorization/]].
- Framework Integration: While the core is flexible, it provides convenient wrappers and utilities specifically for Hono (server-side) and React (client-side), matching our technology stack.
- Comprehensive Feature Set: It provides built-in support or plugins for essential features required by modern applications, including email/password, OTP flows, organization/team management, API key handling, and admin capabilities.
By using better-auth, we benefit from a robust, well-structured, and secure foundation for authentication, allowing us to focus more on application-specific features.
Core Concepts
- Foundation: Built on
better-auth. - Configuration: Uses environment variables (
process.env) and shared configuration from@repo/config. - Database Integration: Connects to the database using
@repo/databasevia thedrizzleAdapterfrombetter-auth/adapters/drizzle. - Email Integration: Sends emails (verification, password reset, invitations) using the
@repo/emailpackage. - Extensibility: Utilizes
better-authplugins for various features. - Server & Client: Provides both server-side initialization (
auth.server.ts) and a React client (client.ts).
Key Files
src/auth.server.ts: Initializes the mainbetterAuthinstance (auth) with server-side configuration, including database adapter, plugins, and email sending logic.src/client.ts: Creates and exports theauthClientinstance usingcreateAuthClientfrombetter-auth/react, configured with corresponding client plugins for use in frontend applications.src/index.ts: Re-exports essential types frombetter-authand the configuredauthClient.
Server-Side Setup (auth.server.ts)
This file is the heart of the authentication setup on the backend.
- Initialization: Calls
betterAuth(authConfig)to create the coreauthservice. - Database Adapter: Uses
drizzleAdapter(db)to connectbetter-authto our Drizzle ORM setup defined in@repo/database. - Email Service: Integrates with
@repo/emailto send various transactional emails (verification, password reset, OTPs, organization invitations) using predefined templates. - Configuration (
authConfig): Defines core settings likesecret,baseURL,basePath, trusted origins, cookie attributes, and enables/disables features based on@repo/config. - Plugins: Configures and enables various
better-authplugins:apiKey: For handling API key authentication.organization: Manages multi-tenancy, teams, and invitations.anonymous: Allows anonymous user sessions.admin: Provides utilities for admin actions.openAPI: Integrates with OpenAPI documentation generation.expo: Support for React Native (Expo) environments.emailOTP: Handles one-time password logic for sign-in, verification, and password reset via email.emailAndPassword(conditional): Standard email/password login, enabled via@repo/config.
// Simplified snippet from packages/auth/src/auth.server.ts
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "@repo/database";
import { EmailService /* ...email templates */ } from "@repo/email";
import { apiKey, organization /* ...other plugins */ } from "better-auth/plugins";
const authConfig: CustomAuthOptions = {
secret: process.env.BETTER_AUTH_SECRET,
baseURL: process.env.BETTER_AUTH_URL,
database: drizzleAdapter(db, { provider: "pg" }),
emailVerification: { /* ... email sending logic ... */ },
plugins: [ apiKey(), organization({ /* ... org config ... */ }), /* ... */ ],
// ... other configurations ...
};
const authService = betterAuth(authConfig);
export const auth = authService; // Export the configured serviceClient-Side Setup (client.ts)
This file prepares the client-side utilities for use in React applications.
- Initialization: Uses
createAuthClientfrombetter-auth/react. - Plugins: Includes client-side counterparts for the enabled server plugins (
organizationClient,anonymousClient,apiKeyClient,adminClient,emailOTPClient). - Export: Exports the configured
authClient.
// Snippet from packages/auth/src/client.ts
import { createAuthClient } from "better-auth/react";
import { organizationClient, /* ...other client plugins */ } from "better-auth/client/plugins";
import type { auth } from "./auth.server";
export const authClient = createAuthClient({
basePath: "/api/v1/auth", // Matches server basePath
plugins: [
inferAdditionalFields<typeof auth>(), // Important for type inference
organizationClient(),
// ... other client plugins
],
});Usage
API Server (@repo/api)
- Middleware: The API server uses
authMiddleware(created usingauth.api.getSessionand DB lookups) to authenticate requests and attach user/session info to the Hono context (c.get('user')). - Protected Routes: The
requireAuthmiddleware checks forc.get('user')to protect endpoints. - Auth Endpoints: Mounts the
auth.handler(viatoHonoHandler) at/api/v1/auth/*to handle sign-in, sign-up, etc. - Accessing User: Route handlers access the authenticated user via
c.get('user').
(See the API Usage Guide for detailed examples)
Web Application (apps/web)
- Import: Import
authClientdirectly:import { authClient } from "@repo/auth"; - Hooks: Use React hooks provided by
better-auth/react(often re-exported or used viaauthClient), such asuseSession. - Actions: Call methods on
authClientfor actions like sign-in, sign-out, sign-up, password reset, social login, etc. (e.g.,authClient.signIn({ email, password })). - Protected Routes: Use the
useSessionhook to check authentication status and redirect unauthenticated users. - Server-Side: In server loaders/actions (if applicable), use
auth.api.getSession({ headers })to get the session.
(See the Web App Usage Guide for detailed examples)