fix: update documentation and configuration files
This commit is contained in:
+239
-151
@@ -1,73 +1,95 @@
|
||||
# Authentication, Authorization, and Accounting (AAA) Design
|
||||
|
||||
## Overview
|
||||
Implement a comprehensive AAA system for EuchreCamp with user authentication, role-based authorization, and activity logging.
|
||||
|
||||
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
|
||||
```ruby
|
||||
# Database Schema
|
||||
table :users do
|
||||
primary_key :id
|
||||
foreign_key :player_id, :players # Links to existing player record
|
||||
column :email, String, null: false, unique: true
|
||||
column :password_digest, String, null: false
|
||||
column :confirmed, Boolean, default: false
|
||||
column :confirmation_token, String
|
||||
column :confirmation_sent_at, DateTime
|
||||
column :reset_password_token, String
|
||||
column :reset_password_sent_at, DateTime
|
||||
column :failed_login_attempts, Integer, default: 0
|
||||
column :locked_until, DateTime
|
||||
column :last_login_at, DateTime
|
||||
column :last_login_ip, String
|
||||
column :created_at, DateTime, null: false
|
||||
column :updated_at, DateTime, null: false
|
||||
end
|
||||
### User Model (Prisma Schema)
|
||||
|
||||
# Indexes
|
||||
add_index :users, :email, unique: true
|
||||
add_index :users, :confirmation_token
|
||||
add_index :users, :reset_password_token
|
||||
```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
|
||||
1. User enters email and password
|
||||
2. System verifies email exists and account is not locked
|
||||
3. BCrypt verifies password hash
|
||||
4. If successful:
|
||||
- Update `last_login_at` and `last_login_ip`
|
||||
- Reset `failed_login_attempts`
|
||||
- Create session/token
|
||||
5. If failed:
|
||||
- Increment `failed_login_attempts`
|
||||
- Lock account after 5 failed attempts (15 minutes)
|
||||
### Authentication Flow (Better Auth)
|
||||
|
||||
### Session Management
|
||||
- Use signed cookies for session storage
|
||||
- Session expires after 30 days of inactivity
|
||||
- Session invalidation on password change
|
||||
- Concurrent session limits (optional)
|
||||
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
|
||||
- At least one uppercase letter
|
||||
- At least one lowercase letter
|
||||
- At least one number
|
||||
- At least one special character
|
||||
|
||||
### Registration Flow
|
||||
1. User registers with email and password
|
||||
2. System creates user record with `confirmed: false`
|
||||
3. Send confirmation email with unique token
|
||||
4. User clicks link to confirm account
|
||||
5. Account activated and ready for login
|
||||
- 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
|
||||
2. System generates unique token (expires in 1 hour)
|
||||
3. Send reset email with token link
|
||||
|
||||
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
|
||||
@@ -77,85 +99,124 @@ add_index :users, :reset_password_token
|
||||
### Role-Based Access Control (RBAC)
|
||||
|
||||
#### Roles
|
||||
```ruby
|
||||
ROLES = {
|
||||
player: 0, # Default user - can view rankings, own profile
|
||||
tournament_admin: 1, # Can manage specific tournaments
|
||||
club_admin: 2, # Superuser - full access
|
||||
system_admin: 3 # Technical admin access
|
||||
|
||||
```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 | System 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 | ❌ | ❌ | ✅ | ✅ |
|
||||
| System settings | ❌ | ❌ | ❌ | ✅ |
|
||||
| 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
|
||||
```ruby
|
||||
module EuchreCamp
|
||||
module Auth
|
||||
class Authorization
|
||||
def self.can?(user, action, resource = nil)
|
||||
case action
|
||||
when :view_rankings
|
||||
true # All users can view rankings
|
||||
when :edit_profile
|
||||
resource.nil? || resource.id == user.player_id
|
||||
when :create_tournament
|
||||
user.club_admin? || user.system_admin?
|
||||
when :manage_tournament
|
||||
user.club_admin? || user.system_admin? ||
|
||||
(user.tournament_admin? && resource.admin_id == user.player_id)
|
||||
when :manage_players
|
||||
user.club_admin? || user.system_admin?
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
```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
|
||||
- Authentication middleware checks for valid session
|
||||
- Authorization middleware verifies permissions
|
||||
- Redirect unauthenticated users to login
|
||||
- Show 403 Forbidden for unauthorized actions
|
||||
|
||||
```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
|
||||
```ruby
|
||||
table :activity_logs do
|
||||
primary_key :id
|
||||
foreign_key :user_id, :users
|
||||
column :action, String, null: false # e.g., 'login', 'create_tournament', 'record_match'
|
||||
column :resource_type, String # e.g., 'tournament', 'match', 'player'
|
||||
column :resource_id, Integer
|
||||
column :ip_address, String
|
||||
column :user_agent, String
|
||||
column :details, JSON # Additional context
|
||||
column :created_at, DateTime, null: false
|
||||
end
|
||||
### Activity Logging (Prisma)
|
||||
|
||||
add_index :activity_logs, [:user_id, :created_at]
|
||||
add_index :activity_logs, [:resource_type, :resource_id]
|
||||
```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)
|
||||
@@ -163,27 +224,56 @@ add_index :activity_logs, [:resource_type, :resource_id]
|
||||
- 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)
|
||||
- [ ] Create users table and model
|
||||
- [ ] Implement BCrypt password hashing
|
||||
- [ ] Build login/logout actions
|
||||
- [ ] Create registration flow
|
||||
- [ ] Add session management
|
||||
- [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)
|
||||
- [ ] Define roles and permissions
|
||||
- [ ] Implement RBAC middleware
|
||||
- [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 actions
|
||||
- [ ] Add permission checks to API routes
|
||||
|
||||
### Phase 3: Accounting (Week 3-4)
|
||||
- [ ] Create activity logging system
|
||||
@@ -192,19 +282,19 @@ add_index :activity_logs, [:resource_type, :resource_id]
|
||||
- [ ] Add activity feed to dashboard
|
||||
|
||||
### Phase 4: Security Hardening (Week 4-5)
|
||||
- [ ] Rate limiting on login attempts
|
||||
- [x] Rate limiting (Better Auth built-in)
|
||||
- [ ] IP-based lockout
|
||||
- [ ] Secure cookie settings
|
||||
- [ ] CSRF protection
|
||||
- [ ] CSRF protection (Next.js built-in)
|
||||
- [ ] Security headers
|
||||
- [ ] Session fixation prevention
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Password Security
|
||||
- Use BCrypt with cost factor 12+
|
||||
- Use scrypt hashing (Better Auth default)
|
||||
- Never store plaintext passwords
|
||||
- Enforce strong password policy
|
||||
- Enforce strong password policy (via Better Auth)
|
||||
- Rate limit password attempts
|
||||
|
||||
### Session Security
|
||||
@@ -220,40 +310,38 @@ add_index :activity_logs, [:resource_type, :resource_id]
|
||||
- Log all access attempts
|
||||
|
||||
### Data Protection
|
||||
- Encrypt sensitive data at rest
|
||||
- Secure transmission (HTTPS only)
|
||||
- Regular security audits
|
||||
- Compliance with privacy regulations
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Authentication
|
||||
### Authentication (Better Auth)
|
||||
```
|
||||
POST /auth/login # Login with email/password
|
||||
POST /auth/logout # Logout current session
|
||||
POST /auth/register # Create new account
|
||||
POST /auth/confirm # Confirm email address
|
||||
POST /auth/forgot # Request password reset
|
||||
POST /auth/reset # Reset password with token
|
||||
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 /users/me # Get current user profile
|
||||
PATCH /users/me # Update own profile
|
||||
GET /users/:id # Get user profile (public)
|
||||
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 /admin/users # List users (club admin only)
|
||||
PATCH /admin/users/:id # Update user roles (club admin only)
|
||||
GET /admin/activity # View audit logs (club admin only)
|
||||
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
|
||||
### Unit Tests (Vitest)
|
||||
- Password hashing verification
|
||||
- Permission calculations
|
||||
- Session management
|
||||
@@ -266,18 +354,18 @@ GET /admin/activity # View audit logs (club admin only)
|
||||
- Role-based access
|
||||
|
||||
### Security Tests
|
||||
- SQL injection prevention
|
||||
- XSS prevention
|
||||
- CSRF protection
|
||||
- SQL injection prevention (Prisma)
|
||||
- XSS prevention (Next.js)
|
||||
- CSRF protection (Next.js)
|
||||
- Rate limiting
|
||||
- Session security
|
||||
|
||||
## Migration Steps
|
||||
|
||||
1. Create users table and related migrations
|
||||
2. Add foreign key from users to players
|
||||
3. Migrate existing player data to users (optional)
|
||||
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 actions
|
||||
5. Add authentication checks to existing routes
|
||||
6. Implement role assignment for existing admins
|
||||
7. Set up activity logging for new features
|
||||
|
||||
Reference in New Issue
Block a user