9.3 KiB
9.3 KiB
Authentication, Authorization, and Accounting (AAA) Design
Overview
Implement a comprehensive AAA system for EuchreCamp with user authentication, role-based authorization, and activity logging using Next.js and Better Auth.
Authentication (Who are you?)
User Model (Prisma Schema)
// prisma/schema.prisma
model User {
id String @id @default(cuid())
email String @unique
emailVerified Boolean @default(false)
password String?
name String?
role Role @default(PLAYER)
// Relationships
player Player? @relation(fields: [playerId], references: [id])
playerId String?
// Better Auth fields
accounts Account[]
sessions Session[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Player {
id String @id @default(cuid())
name String
rating Int @default(1000)
user User?
userId String?
// Relationships
matches Match[]
partnerships Partnership[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum Role {
PLAYER
TOURNAMENT_ADMIN
CLUB_ADMIN
}
Authentication Flow (Better Auth)
- User enters email and password
- Better Auth verifies email exists
- Password verification using secure hashing (scrypt by default)
- If successful:
- Create session token
- Set secure HTTP-only cookie
- Redirect to dashboard
- If failed:
- Rate limiting applied (Better Auth built-in)
- Account lockout after multiple failed attempts
Session Management (Better Auth)
- Session Storage: Secure HTTP-only cookies
- Session Expiration: Configurable (default 30 days)
- Session Invalidation: On password change
- Concurrent Sessions: Supported with session tracking
Password Requirements
- Minimum 8 characters
- Configurable via Better Auth settings
- Hashed with scrypt (server-side)
Registration Flow (Next.js + Better Auth)
- User registers with email and password via
/auth/register - System creates user record with
emailVerified: false - Email confirmation flow (if enabled)
- User can log in after verification
Password Reset Flow
- User requests password reset via
/auth/reset-password - System generates unique token
- Email reset link to user
- User enters new password
- System validates and updates password hash
- Invalidate all existing sessions
Authorization (What can you do?)
Role-Based Access Control (RBAC)
Roles
// src/lib/auth.ts
enum Role {
PLAYER = 0, // Default user - can view rankings, own profile
TOURNAMENT_ADMIN = 1, // Can manage specific tournaments
CLUB_ADMIN = 2 // Superuser - full access
}
Permission Matrix
| Permission | Player | Tournament Admin | Club Admin |
|---|---|---|---|
| View rankings | ✅ | ✅ | ✅ |
| View own profile | ✅ | ✅ | ✅ |
| Edit own profile | ✅ | ✅ | ✅ |
| View other profiles | ✅ | ✅ | ✅ |
| Record own matches | ✅ | ✅ | ✅ |
| Create tournaments | ❌ | ✅ | ✅ |
| Manage own tournaments | ❌ | ✅ | ✅ |
| Manage all tournaments | ❌ | ❌ | ✅ |
| Manage all players | ❌ | ❌ | ✅ |
| Club settings | ❌ | ❌ | ✅ |
Implementation
// src/lib/auth.ts
import { betterAuth } from "better-auth";
import { prisma } from "./prisma";
export const auth = betterAuth({
database: prisma,
emailAndPassword: {
enabled: true,
},
plugins: [
// Additional plugins as needed
],
});
// Authorization helper
export function can(user: User, action: string, resource?: any): boolean {
switch (action) {
case "view_rankings":
return true; // All users can view rankings
case "edit_profile":
return !resource || resource.id === user.playerId;
case "create_tournament":
return user.role === "CLUB_ADMIN";
case "manage_tournament":
return user.role === "CLUB_ADMIN" ||
(user.role === "TOURNAMENT_ADMIN" && resource.adminId === user.playerId);
case "manage_players":
return user.role === "CLUB_ADMIN";
default:
return false;
}
}
Middleware
// src/middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { getSession } from "@/lib/auth";
export async function middleware(request: NextRequest) {
const session = await getSession(request);
// Protect admin routes
if (request.nextUrl.pathname.startsWith("/admin")) {
if (!session) {
return NextResponse.redirect(new URL("/auth/login", request.url));
}
if (session.user.role !== "CLUB_ADMIN") {
return NextResponse.redirect(new URL("/unauthorized", request.url));
}
}
return NextResponse.next();
}
export const config = {
matcher: ["/admin/:path*"],
};
Accounting (What did you do?)
Activity Logging (Prisma)
// prisma/schema.prisma
model ActivityLog {
id String @id @default(cuid())
userId String
action String // e.g., 'login', 'create_tournament', 'record_match'
resourceType String? // e.g., 'tournament', 'match', 'player'
resourceId String?
ipAddress String?
userAgent String?
details Json? // Additional context
user User @relation(fields: [userId], references: [id])
createdAt DateTime @default(now())
@@index([userId, createdAt])
@@index([resourceType, resourceId])
}
Tracked Events
- Authentication events (login, logout, password change)
- Tournament management (create, update, delete, schedule)
- Match recording (create, update, delete)
- Player management (create, update)
- Settings changes
Audit Reports
- User activity timeline
- Tournament lifecycle audit
- Match result verification log
- System changes overview
Implementation
// src/lib/activity.ts
import { prisma } from "./prisma";
export async function logActivity(
userId: string,
action: string,
resourceType?: string,
resourceId?: string,
details?: any
) {
await prisma.activityLog.create({
data: {
userId,
action,
resourceType,
resourceId,
details,
ipAddress: "0.0.0.0", // Get from request
userAgent: "unknown", // Get from request
},
});
}
Implementation Plan
Phase 1: Authentication (Week 1-2)
- Set up Better Auth with Prisma
- Create login page (
/auth/login) - Create registration page (
/auth/register) - Implement session management
- Add navigation for logged-in users
- Password reset functionality
- Email confirmation system
Phase 2: Authorization (Week 2-3)
- Define roles in Prisma schema
- Implement RBAC middleware
- Add role assignment UI (for club admins)
- Protect routes based on roles
- Add permission checks to API routes
Phase 3: Accounting (Week 3-4)
- Create activity logging system
- Track key events
- Build audit reports UI
- Add activity feed to dashboard
Phase 4: Security Hardening (Week 4-5)
- Rate limiting (Better Auth built-in)
- IP-based lockout
- Secure cookie settings
- CSRF protection (Next.js built-in)
- Security headers
- Session fixation prevention
Security Considerations
Password Security
- Use scrypt hashing (Better Auth default)
- Never store plaintext passwords
- Enforce strong password policy (via Better Auth)
- Rate limit password attempts
Session Security
- Use signed, encrypted cookies
- Regenerate session ID on privilege escalation
- Set appropriate expiration times
- Invalidate on password change
Access Control
- Principle of least privilege
- Defense in depth
- Fail securely (deny by default)
- Log all access attempts
Data Protection
- Secure transmission (HTTPS only)
- Regular security audits
- Compliance with privacy regulations
API Endpoints
Authentication (Better Auth)
POST /api/auth/sign-up/email # Register new account
POST /api/auth/sign-in/email # Login with email/password
POST /api/auth/sign-out # Logout current session
POST /api/auth/forgot-password # Request password reset
POST /api/auth/reset-password # Reset password with token
Users
GET /api/users/me # Get current user profile
PATCH /api/users/me # Update own profile
GET /api/users/:id # Get user profile (public)
Admin
GET /api/admin/users # List users (club admin only)
PATCH /api/admin/users/:id # Update user roles (club admin only)
GET /api/admin/activity # View audit logs (club admin only)
Testing Strategy
Unit Tests (Vitest)
- Password hashing verification
- Permission calculations
- Session management
- Activity logging
Integration Tests
- Login/logout flow
- Registration process
- Password reset
- Role-based access
Security Tests
- SQL injection prevention (Prisma)
- XSS prevention (Next.js)
- CSRF protection (Next.js)
- Rate limiting
- Session security
Migration Steps
- Set up Better Auth with Prisma
- Create users table and related models
- Add foreign key from users to players
- Update navigation to show login/logout links
- Add authentication checks to existing routes
- Implement role assignment for existing admins
- Set up activity logging for new features