Skip to content

Auth - Module Specification

1. Overview

1.1 Purpose

Auth module handles user authentication, session management, and role-based access control for staff and admin users. Built on Better Auth for edge-compatible, modern authentication.

1.2 Scope

In Scope: - User account management (staff/admin) - Authentication (login, logout, password reset) - Session management - Role-based access control (RBAC) - Role definitions (Sales, Kennel Staff, Photographer, Admin)

Out of Scope: - Customer accounts (future consideration for ecommerce phase) - Social login (not needed for staff) - Multi-factor authentication (future consideration)


2. Dependencies

2.1 Upstream (What This Module Needs)

Module/Service What We Need Interface
Better Auth Authentication framework Library
Database User/session storage Drizzle ORM

2.2 Downstream (What Needs This Module)

Module/Service What They Need Interface
Inventory User ID for change tracking, role checks Server Actions
Media User ID for upload tracking, role checks Server Actions
Admin Settings Role checks for admin operations Server Actions
CMS User ID for content authorship, role checks Server Actions
Customer Website Session validation for protected routes Middleware

3. Data Ownership

3.1 Entities This Module Owns

Entity Description Key Fields
User Better Auth user + custom fields id, email, name, roles (extended)
Session Better Auth session id, userId, token, expiresAt

Note: Uses Better Auth's built-in user and session schema. We extend with custom fields only.

3.2 Entities This Module Uses (Read-Only)

Entity Owner How We Use It

3.3 Data Schema

// Extend Better Auth's built-in user with custom fields
// See: https://www.better-auth.com/docs/concepts/users-accounts

type UserRole = 'sales' | 'kennel_staff' | 'photographer' | 'admin';

// Custom fields added to Better Auth user
interface UserExtension {
  roles: UserRole[];              // User can have multiple roles
  phone?: string;
  isActive: boolean;
}

// Full user type (Better Auth base + our extensions)
// Better Auth provides: id, email, name, emailVerified, image, createdAt, updatedAt
type User = BetterAuthUser & UserExtension;

// Session managed entirely by Better Auth
// See: https://www.better-auth.com/docs/concepts/session-management
type Session = BetterAuthSession;

// Permission check helper
function hasRole(user: User, role: UserRole): boolean;
function hasAnyRole(user: User, roles: UserRole[]): boolean;

4. Service Level Objectives

Objective Target Measurement
Login Latency < 500ms Application monitoring
Session Validation < 50ms Application monitoring
Availability 99.9% Health checks
Password Reset Email < 30s delivery Email provider metrics

Note: Session validation is on the critical path for all authenticated requests. Must be fast.


5. Interface Contract

5.1 Better Auth Configuration

// Better Auth handles core auth flows out of the box
// See: https://www.better-auth.com/docs

import { betterAuth } from 'better-auth';

export const auth = betterAuth({
  database: drizzleAdapter(db),
  emailAndPassword: {
    enabled: true,
  },
  user: {
    additionalFields: {
      roles: {
        type: 'string[]',
        required: true,
        defaultValue: ['sales'],
      },
      phone: {
        type: 'string',
        required: false,
      },
      isActive: {
        type: 'boolean',
        required: true,
        defaultValue: true,
      },
    },
  },
});

5.2 Custom Service Interface

interface AuthService {
  // User management (Admin only)
  getUser(id: string): Promise<User>;
  listUsers(filters?: UserFilters): Promise<User[]>;
  createUser(data: CreateUserDTO): Promise<User>;
  updateUser(id: string, data: UpdateUserDTO): Promise<User>;
  deactivateUser(id: string): Promise<void>;
  reactivateUser(id: string): Promise<void>;

  // Role management (Admin only)
  assignRoles(userId: string, roles: UserRole[]): Promise<User>;
  removeRoles(userId: string, roles: UserRole[]): Promise<User>;

  // Permission helpers
  hasRole(user: User, role: UserRole): boolean;
  hasAnyRole(user: User, roles: UserRole[]): boolean;

  // Session helpers
  getCurrentUser(): Promise<User | null>;
  requireUser(): Promise<User>;
  requireRole(role: UserRole): Promise<User>;
  requireAnyRole(roles: UserRole[]): Promise<User>;
}

interface UserFilters {
  role?: UserRole;
  isActive?: boolean;
  search?: string;                // Search by name, email
}

interface CreateUserDTO {
  email: string;
  name: string;
  password: string;
  roles: UserRole[];
  phone?: string;
}

interface UpdateUserDTO {
  name?: string;
  email?: string;
  phone?: string;
  roles?: UserRole[];
}

5.3 Remote Procedure Interface

Communication Pattern: Next.js Server Actions

Procedure Input Output Auth Description
login { email, password } Session Public Better Auth built-in
logout void Authenticated Better Auth built-in
resetPassword { email } void Public Better Auth built-in
getUser { id } User Admin Get user details
listUsers UserFilters User[] Admin List all users
createUser CreateUserDTO User Admin Create new user
updateUser { id, data } User Admin Update user
deactivateUser { id } void Admin Deactivate user
assignRoles { userId, roles } User Admin Add roles to user
getCurrentUser User Authenticated Get current session user

6. Error Handling Strategy

Error Code Condition User Message
AUTH_INVALID_CREDENTIALS Wrong email/password "Invalid email or password"
AUTH_USER_NOT_FOUND User doesn't exist "User not found"
AUTH_USER_INACTIVE Account deactivated "Your account has been deactivated"
AUTH_SESSION_EXPIRED Session no longer valid "Your session has expired, please login again"
AUTH_UNAUTHORIZED Not logged in "Please login to continue"
AUTH_FORBIDDEN Insufficient role "You don't have permission to perform this action"
AUTH_EMAIL_EXISTS Email already registered "An account with this email already exists"
AUTH_WEAK_PASSWORD Password doesn't meet requirements "Password must be at least 8 characters"
AUTH_RESET_TOKEN_INVALID Invalid/expired reset token "Password reset link is invalid or expired"

Error Response Format

interface AuthErrorResponse {
  code: string;
  message: string;
  details?: Record<string, unknown>;
}

7. Observability

7.1 Logging

Event Level Fields to Include
User logged in INFO user_id, email, ip_address
User logged out INFO user_id
Login failed WARN email, ip_address, reason
Password reset requested INFO email
Password reset completed INFO user_id
User created INFO user_id, email, roles, created_by
User deactivated INFO user_id, deactivated_by
Roles changed INFO user_id, old_roles, new_roles, changed_by
Session expired DEBUG user_id, session_id

7.2 Metrics

Metric Type Description
auth_logins_total Counter Total login attempts by status (success/failure)
auth_active_sessions Gauge Current active sessions
auth_users_total Gauge Total users by role and status
auth_session_duration_ms Histogram Session lifetimes
auth_password_resets_total Counter Password reset requests

8. Testing Strategy

Test Type Coverage Target Tools Focus Areas
Unit 80%+ Vitest Role helpers, permission checks, validation
Integration Critical paths Vitest Login flow, session management, user CRUD
E2E Happy paths Playwright Login/logout, password reset, user management UI

9. Feature Inventory

Feature ID Name Status Priority
FEATURE-054 Authentication (Login/Logout/Reset) Draft Must Have
FEATURE-055 User Management Draft Must Have
FEATURE-056 Role-Based Access Control Draft Must Have

FEATURE-054 Note: Leverages Better Auth built-in flows for login, logout, password reset.

FEATURE-055 Note: Admin UI for creating, updating, deactivating users.

FEATURE-056 Note: Permission helpers, role assignment, middleware for protected routes.


10. Access Control

Operation Permissions

Operation Public Sales Kennel Staff Photographer Admin
Login/Logout Yes Yes Yes Yes Yes
Reset own password Yes Yes Yes Yes Yes
View own profile No Yes Yes Yes Yes
Update own profile No Yes Yes Yes Yes
View all users No No No No Yes
Create user No No No No Yes
Update user No No No No Yes
Deactivate user No No No No Yes
Assign roles No No No No Yes

Note: All user management operations are Admin-only. Users can only manage their own profile.


11. Decisions

D1: Better Auth Framework (2026-01-20)

Status: Accepted Context: Need edge-compatible, modern authentication for Cloudflare Workers Decision: Use Better Auth as the authentication framework Consequences: Built-in session management, password hashing, email flows; edge-compatible; extend with custom fields for roles

D2: Multi-Role Users (2026-01-20)

Status: Accepted Context: Staff may have overlapping responsibilities (e.g., kennel staff who also does photography) Decision: Users have an array of roles rather than a single role Consequences: More flexible permissions, check uses hasAnyRole() for access control

D3: Staff-Only Authentication (2026-01-20)

Status: Accepted Context: Phase 1 focuses on staff/admin access; customer accounts needed later for ecommerce Decision: Auth module only handles staff/admin users for now Consequences: Simpler implementation; customer accounts deferred to Phase 4 (Ecommerce)

D4: Email/Password Only (2026-01-20)

Status: Accepted Context: Staff users don't need social login options Decision: Support only email/password authentication, no OAuth providers Consequences: Simpler setup, no third-party dependencies for auth


12. References

  • Architecture: ARCH-001
  • Inventory Module: MODULE-002
  • Media Module: MODULE-003
  • Admin Settings Module: MODULE-004
  • Rich Text Editor Module: MODULE-005
  • Better Auth Documentation: https://www.better-auth.com/docs
  • Better Auth Drizzle Adapter: https://www.better-auth.com/docs/adapters/drizzle

Change Log

Version Date Author Changes
1.0 2026-01-20 Claude Initial module spec - Draft