Files
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

372 lines
9.3 KiB
Markdown

# 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
// 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
```typescript
// 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
```typescript
// 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
```typescript
// 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
// 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
```typescript
// 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)
- [x] Set up Better Auth with Prisma
- [x] Create login page (`/auth/login`)
- [x] Create registration page (`/auth/register`)
- [x] Implement session management
- [x] Add navigation for logged-in users
- [ ] Password reset functionality
- [ ] Email confirmation system
### Phase 2: Authorization (Week 2-3)
- [x] Define roles in Prisma schema
- [x] 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)
- [x] 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