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¶
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 | |
| 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 |