Authentication across the entire BrokerOS monorepo is powered exclusively by Better Auth. Custom JWT minting, custom session tables, and custom password hashing algorithms are strictly prohibited by system architecture law.Every client (web, mobile, background workers) authenticates through the unified Better Auth server instance mounted in apps/api/src/auth/.
Endpoints enforce role-based access control using the @Roles() decorator:
import { Controller, Get, UseGuards } from '@nestjs/common';import { AuthGuard, Roles } from '@thallesp/nestjs-better-auth';@Controller('api/dashboard/sales')@UseGuards(AuthGuard)export class SalesDashboardController { @Get() @Roles(['SALES_EXECUTIVE', 'SALES_MANAGER', 'DIRECTOR', 'ADMIN']) getSalesData() { return { success: true }; }}
Role Security Invariant: The user role is always read server-side from session.user.role. The API must never accept a role parameter from request bodies, headers, or query parameters.
In accordance with Next.js 16 conventions, route protection and workspace redirects are handled in apps/web/proxy.ts. The proxy intercepts requests, verifies session cookies, and redirects unauthenticated users to /login before rendering protected /dashboard workspaces.
The mobile application integrates @better-auth/expo with expo-secure-store:
Keystore Storage: On login, session tokens are stored inside expo-secure-store, protected by Android Keystore hardware-backed encryption.
Authorization Headers: The token is attached as an Authorization: Bearer <token> header to all API requests and WebSocket handshakes.
Native Background Persistence: The session token is copied to native SharedPreferences via setAuthTokenForBackground. This allows the Kotlin background worker (CallUploadWorker.kt) to upload audio files even when the React Native JavaScript thread is paused or the app is closed.
Database Model: Stored in the dedicated TwoFactor table (packages/prisma/schema.prisma), referencing User.id with cascade deletion. Contains encrypted secrets, hashed recovery codes, verification flags, and temporary lock counters.
Server Plugin: Configured in apps/api/src/lib/auth.ts with twoFactor({ issuer: 'BrokerOS' }). Automatically intercepts /sign-in/email, /sign-in/username, and /sign-in/phone-number.