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-auth
is built with TypeScript first, offering strong type safety and inference capabilities (e.g.,inferAdditionalFields
for 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/database
package. - 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/database
via thedrizzleAdapter
frombetter-auth/adapters/drizzle
. - Email Integration: Sends emails (verification, password reset, invitations) using the
@repo/email
package. - Extensibility: Utilizes
better-auth
plugins 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 mainbetterAuth
instance (auth
) with server-side configuration, including database adapter, plugins, and email sending logic.src/client.ts
: Creates and exports theauthClient
instance usingcreateAuthClient
frombetter-auth/react
, configured with corresponding client plugins for use in frontend applications.src/index.ts
: Re-exports essential types frombetter-auth
and 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 coreauth
service. - Database Adapter: Uses
drizzleAdapter(db)
to connectbetter-auth
to our Drizzle ORM setup defined in@repo/database
. - Email Service: Integrates with
@repo/email
to 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-auth
plugins: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 service
Client-Side Setup (client.ts
)
This file prepares the client-side utilities for use in React applications.
- Initialization: Uses
createAuthClient
frombetter-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.getSession
and DB lookups) to authenticate requests and attach user/session info to the Hono context (c.get('user')
). - Protected Routes: The
requireAuth
middleware 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
authClient
directly: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
authClient
for actions like sign-in, sign-out, sign-up, password reset, social login, etc. (e.g.,authClient.signIn({ email, password })
). - Protected Routes: Use the
useSession
hook 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)