Files
euchre_camp/docs/AAA_DESIGN.md
david 123df671f5 nextjs-rewrite (#5)
Reviewed-on: #5
Co-authored-by: David Gwilliam <dhgwilliam@gmail.com>
Co-committed-by: David Gwilliam <dhgwilliam@gmail.com>
2026-03-30 02:30:13 +00:00

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)

  1. User enters email and password
  2. Better Auth verifies email exists
  3. Password verification using secure hashing (scrypt by default)
  4. If successful:
    • Create session token
    • Set secure HTTP-only cookie
    • Redirect to dashboard
  5. 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)

  1. User registers with email and password via /auth/register
  2. System creates user record with emailVerified: false
  3. Email confirmation flow (if enabled)
  4. User can log in after verification

Password Reset Flow

  1. User requests password reset via /auth/reset-password
  2. System generates unique token
  3. Email reset link to user
  4. User enters new password
  5. System validates and updates password hash
  6. 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

  1. Set up Better Auth with Prisma
  2. Create users table and related models
  3. Add foreign key from users to players
  4. Update navigation to show login/logout links
  5. Add authentication checks to existing routes
  6. Implement role assignment for existing admins
  7. Set up activity logging for new features