Server-Side Authentication (@repo/auth)
Relevant source files
The following files were used as context for generating this wiki page:
The @repo/auth package is a centralized module responsible for all authentication, session management, and authorization logic within the Adfluens application. It is built upon the better-auth library, providing a robust and extensible foundation for user identity. The package handles various authentication strategies, including email/password and social OAuth (Google, Facebook), and integrates an organization/multi-tenancy system.
This module exposes its functionality through a dedicated API endpoint (/api/auth/*) handled by an Express middleware. It also integrates deeply with the tRPC backend by providing the necessary context for creating authenticated and protected procedures, ensuring that sensitive data and operations are accessible only to authorized users.
Core Architecture
The authentication system is designed as a self-contained package within the monorepo. It leverages better-auth for the core logic, drizzle-orm for database interaction via a Drizzle adapter, and exposes both a server-side configuration and a client-side SDK. The api application consumes this package to secure its endpoints.
This diagram shows the high-level architecture of the authentication flow.
Authentication Configuration
All server-side authentication behavior is defined in a single configuration object. This object initializes betterAuth with a database adapter, authentication providers, session policies, and plugins.
Sources: packages/auth/src/auth.config.ts
Main Configuration Options
| Option | Description | Configuration Snippet |
|---|---|---|
database | Connects better-auth to the PostgreSQL database using the drizzleAdapter. It specifies the Drizzle client instance and the auth schema tables. | drizzleAdapter(db, { provider: "pg", schema: authSchema }) |
baseURL | The explicit URL where the auth API is hosted, read from environment variables. | env.BETTER_AUTH_URL |
trustedOrigins | A list of frontend origins allowed to make cross-origin requests, read from environment variables. | [env.APP_URL] |
emailAndPassword | Enables and configures email/password authentication, including password length constraints and auto sign-in on registration. | { enabled: true, minPasswordLength: 8, ... } |
socialProviders | Configures OAuth providers like Google and Facebook using client IDs and secrets from environment variables. | google: { clientId: env.GOOGLE_CLIENT_ID, ... } |
session | Defines session lifetime (expiresIn) and how often the session data is updated (updateAge). | expiresIn: 60 * 60 * 24 * 7 (7 days) |
plugins | Extends core functionality. The organization plugin is used to add multi-tenancy features. | [organization({ ac, roles, ... })] |
Sources: packages/auth/src/auth.config.ts:12-78
Automatic Organization Creation
A key feature is the automatic creation of a personal workspace for every new user upon sign-up. This is achieved using a databaseHooks on the user:create:after event. If the organization creation fails, the error is logged, but the user sign-up process is not blocked.
This flowchart illustrates the new user registration and workspace creation process.
Database Schema
The @repo/auth package defines its own database schema using Drizzle ORM, which is then used by the better-auth Drizzle adapter. The schema includes tables for users, accounts (credentials), sessions, and the entities required by the organization plugin.
Sources: packages/auth/src/schema/auth.table.ts, packages/db/drizzle/meta/0002_snapshot.json
This ER diagram shows the relationships between the core authentication and organization tables.
Key Table Descriptions
| Table | Description | File |
|---|---|---|
user | Stores core user profile information like name, email, and image. | auth.table.ts |
account | Stores authentication credentials. For email/password, it holds the hashed password. For OAuth, it stores provider-specific IDs and tokens. | auth.table.ts |
session | Stores active user sessions, linking a session token to a user ID. | auth.table.ts |
organization | Stores organization/workspace details, including name, slug, and creator. | auth.table.ts |
member | A join table linking users to organizations with a specific role. | auth.table.ts |
API & tRPC Integration
The authentication system is exposed and consumed within the apps/api backend in two primary ways: as a general-purpose auth middleware and as a context provider for tRPC.
Express Middleware
The api server uses an Express middleware to handle all authentication-related routes. The toNodeHandler(auth) function from better-auth/node creates a handler that is mounted at /api/auth/*splat. This single endpoint manages all auth operations like login, logout, registration, and OAuth callbacks.
It is critical that this middleware is mounted before express.json() to ensure it can correctly process request bodies.
// File: apps/api/src/index.ts:25-26
app.all("/api/auth/*splat", toNodeHandler(auth));
Sources: apps/api/src/index.ts:25-26
tRPC Context Creation
For type-safe API procedures, tRPC needs a context object for each request, which includes session and user information. The createContext function in apps/api/src/trpc/init.ts is responsible for this. It uses auth.api.getSession to validate the session token from the request headers and retrieve the associated user, session, and active organization data.
This sequence diagram illustrates how the tRPC context is created on each API call.
Sources: apps/api/src/trpc/init.ts:48-64
Protected Procedures
Building on the tRPC context, the system defines reusable procedure helpers to enforce authorization:
publicProcedure: For endpoints that do not require authentication.protectedProcedure: A middleware that checks for the existence ofctx.sessionandctx.user. If they are not present, it throws aTRPCErrorwith codeUNAUTHORIZED.organizationProcedure: A middleware that first runs theprotectedProcedurecheck and then verifies thatctx.organizationis also present, ensuring the user has an active workspace selected.
These helpers simplify the process of securing tRPC endpoints.
Sources: apps/api/src/trpc/init.ts:81-125
Client-Side Usage
The server-side setup enables a corresponding client-side library, exported via @repo/auth/client. This library provides functions like signIn.email, signIn.social, and signUp.email that the frontend application (apps/web) uses to interact with the /api/auth/* endpoints.
// Example from sign-up.view.tsx
import { signUp } from "@repo/auth/client";
const result = await signUp.email({
email: data.email,
password: data.password,
name: data.name || "",
});
Sources: apps/web/src/features/auth/views/sign-up.view.tsx:21-25, packages/auth/package.json:8-11