From 6e3aad9fdf8a1fe8e165eed0fbcae6d08baad996 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Wed, 26 Nov 2025 22:25:19 -0600 Subject: [PATCH] CLAUDE: Add developer utilities and access control documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added helper scripts and documentation for improved developer experience: 1. ACCESS_CONTROL.md - Comprehensive Discord whitelist documentation - Configuration examples and security best practices - Troubleshooting guide for common access issues - Details both OAuth and test token protection points 2. start-services.sh - One-command startup for backend + frontend - Logs to logs/ directory with PIDs tracked - Displays URLs and helpful information - 3-second backend initialization delay 3. stop-services.sh - Clean shutdown with process tree killing - Removes orphaned processes by pattern matching - Graceful TERM followed by KILL if needed - Cleans up PID files These utilities streamline local development by: - Reducing manual startup steps - Ensuring clean shutdown (no orphaned processes) - Providing clear access control guidance Scripts are now executable and ready to use: ./start-services.sh ./stop-services.sh 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- ACCESS_CONTROL.md | 215 ++++++++++++++++++++++++++++++++++++++++++++++ start-services.sh | 48 +++++++++++ stop-services.sh | 99 +++++++++++++++++++++ 3 files changed, 362 insertions(+) create mode 100644 ACCESS_CONTROL.md create mode 100755 start-services.sh create mode 100755 stop-services.sh diff --git a/ACCESS_CONTROL.md b/ACCESS_CONTROL.md new file mode 100644 index 0000000..4ac13af --- /dev/null +++ b/ACCESS_CONTROL.md @@ -0,0 +1,215 @@ +# Discord User Access Control + +## Overview + +The backend now includes Discord ID whitelist functionality to control who can access the system. This is useful for limiting access during testing/beta phases or for private leagues. + +## Configuration + +### Quick Start + +Add this line to your `backend/.env` file: + +```bash +# Allow specific Discord users (recommended) +ALLOWED_DISCORD_IDS=123456789012345678,987654321098765432 + +# OR allow everyone (not recommended for production) +ALLOWED_DISCORD_IDS=* + +# OR leave empty to allow everyone (default, not recommended) +ALLOWED_DISCORD_IDS= +``` + +### Finding Discord User IDs + +**Method 1: Enable Developer Mode in Discord** +1. Open Discord Settings → Advanced +2. Enable "Developer Mode" +3. Right-click on a user → "Copy User ID" + +**Method 2: From OAuth Response** +- Check backend logs after a user authenticates +- Look for: `Discord ID {id} verified in whitelist` + +## How It Works + +### Protection Points + +The whitelist is checked at **two** authentication points: + +1. **Discord OAuth Flow** (`/api/auth/discord/callback`) + - After Discord authenticates the user + - Before creating JWT token + - Returns HTTP 403 if user not in whitelist + +2. **Test Token Endpoint** (`/api/auth/token`) + - Development/testing endpoint + - Also checks whitelist + - Returns HTTP 403 if Discord ID not allowed + +### Error Response + +When access is denied, users see: +```json +{ + "detail": "Access denied. Your Discord account is not authorized to access this system." +} +``` + +## Usage Examples + +### Allow Only You and One Tester + +```bash +# In backend/.env +ALLOWED_DISCORD_IDS=123456789012345678,987654321098765432 +``` + +### Allow Everyone (Development) + +```bash +# Option 1: Use wildcard +ALLOWED_DISCORD_IDS=* + +# Option 2: Leave empty +ALLOWED_DISCORD_IDS= +``` + +### Production Recommendation + +```bash +# Explicitly list all authorized users +ALLOWED_DISCORD_IDS=user1_id,user2_id,user3_id +``` + +## Testing + +### Test With Authorized User + +```bash +# 1. Add your Discord ID to .env +ALLOWED_DISCORD_IDS=your_discord_id + +# 2. Restart backend +./stop-services.sh && ./start-services.sh + +# 3. Try to authenticate - should succeed +``` + +### Test With Unauthorized User + +```bash +# 1. Set whitelist to specific IDs (not yours) +ALLOWED_DISCORD_IDS=123456789012345678 + +# 2. Restart backend +./stop-services.sh && ./start-services.sh + +# 3. Try to authenticate - should get 403 error +``` + +## Monitoring + +### Backend Logs + +Whitelist checks are logged: + +**Allowed:** +``` +INFO - Discord ID 123456789012345678 verified in whitelist +INFO - User TestPlayer authenticated successfully +``` + +**Denied:** +``` +WARNING - Discord ID 999999999999999999 not in whitelist - access denied +``` + +**Whitelist Disabled:** +``` +WARNING - Discord whitelist disabled - allowing all users +``` + +## Security Best Practices + +1. ✅ **DO** use explicit user IDs in production +2. ✅ **DO** keep the whitelist in `.env` (gitignored) +3. ✅ **DO** restart backend after changing whitelist +4. ❌ **DON'T** use `*` or empty whitelist in production +5. ❌ **DON'T** commit Discord IDs to git +6. ❌ **DON'T** share your `.env` file + +## Troubleshooting + +### "Access denied" for authorized user + +**Check:** +1. Discord ID is correct (no typos) +2. No extra spaces in `.env` +3. Backend has been restarted +4. Check backend logs for whitelist warnings + +**Fix:** +```bash +# Verify your Discord ID +# Right-click your name in Discord → Copy User ID + +# Update .env with correct ID +ALLOWED_DISCORD_IDS=your_correct_discord_id + +# Restart +./stop-services.sh && ./start-services.sh +``` + +### Everyone getting access when they shouldn't + +**Check:** +1. `ALLOWED_DISCORD_IDS` is not `*` +2. `ALLOWED_DISCORD_IDS` is not empty +3. Backend logs show "verified in whitelist" + +**Fix:** +```bash +# Set explicit whitelist +ALLOWED_DISCORD_IDS=your_id,friend_id + +# Restart +./stop-services.sh && ./start-services.sh +``` + +### Test tokens not working + +**Remember:** Test token endpoint (`/api/auth/token`) also respects the whitelist! + +```bash +# Your test token request must use an allowed Discord ID +{ + "user_id": "test-user-1", + "username": "TestPlayer", + "discord_id": "your_whitelisted_discord_id" # Must be in whitelist! +} +``` + +## Implementation Details + +### Code Locations + +- **Config**: `backend/app/config.py` - Defines `allowed_discord_ids` setting +- **Auth Routes**: `backend/app/api/routes/auth.py` - Implements `is_discord_id_allowed()` check +- **Discord OAuth**: Line 198-203 - Checks whitelist after Discord auth +- **Test Tokens**: Line 376-380 - Checks whitelist for test tokens + +### Whitelist Format + +- **Comma-separated**: `id1,id2,id3` +- **Spaces ignored**: `id1, id2, id3` works too +- **Empty/missing**: Allows all users +- **Wildcard**: `*` allows all users + +--- + +**Created**: 2025-01-25 +**Author**: Jarvis +**Purpose**: Control testing access via Discord user whitelist + diff --git a/start-services.sh b/start-services.sh new file mode 100755 index 0000000..1e2c69c --- /dev/null +++ b/start-services.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# Start both backend and frontend services for SBA gameplay testing + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LOG_DIR="$SCRIPT_DIR/logs" + +# Create logs directory if it doesn't exist +mkdir -p "$LOG_DIR" + +echo "🚀 Starting SBA Gameplay Services..." +echo "" + +# Start Backend +echo "📡 Starting Backend (FastAPI + Socket.io)..." +cd "$SCRIPT_DIR/backend" +uv run python -m app.main > "$LOG_DIR/backend.log" 2>&1 & +BACKEND_PID=$! +echo " Backend started with PID: $BACKEND_PID" +echo " Logs: $LOG_DIR/backend.log" +echo " API: http://localhost:8000" +echo "" + +# Wait for backend to initialize +sleep 3 + +# Start Frontend +echo "🎨 Starting Frontend (Nuxt 3)..." +cd "$SCRIPT_DIR/frontend-sba" +npm run dev > "$LOG_DIR/frontend.log" 2>&1 & +FRONTEND_PID=$! +echo " Frontend started with PID: $FRONTEND_PID" +echo " Logs: $LOG_DIR/frontend.log" +echo " URL: http://localhost:3000" +echo "" + +# Save PIDs for stop script +echo "$BACKEND_PID" > "$LOG_DIR/backend.pid" +echo "$FRONTEND_PID" > "$LOG_DIR/frontend.pid" + +echo "✅ Services started successfully!" +echo "" +echo "📊 To view logs:" +echo " Backend: tail -f $LOG_DIR/backend.log" +echo " Frontend: tail -f $LOG_DIR/frontend.log" +echo "" +echo "🛑 To stop services: ./stop-services.sh" diff --git a/stop-services.sh b/stop-services.sh new file mode 100755 index 0000000..c29b515 --- /dev/null +++ b/stop-services.sh @@ -0,0 +1,99 @@ +#!/bin/bash +# Stop both backend and frontend services +# Enhanced to kill entire process trees and clean up orphans + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LOG_DIR="$SCRIPT_DIR/logs" + +echo "🛑 Stopping SBA Gameplay Services..." +echo "" + +# Function to kill process tree +kill_tree() { + local pid=$1 + local sig=${2:-TERM} + + # Get all child PIDs + local children=$(pgrep -P "$pid" 2>/dev/null || true) + + # Kill children first + for child in $children; do + kill_tree "$child" "$sig" + done + + # Kill the parent + if ps -p "$pid" > /dev/null 2>&1; then + kill -"$sig" "$pid" 2>/dev/null || true + fi +} + +# Stop Backend +if [ -f "$LOG_DIR/backend.pid" ]; then + BACKEND_PID=$(cat "$LOG_DIR/backend.pid") + if ps -p "$BACKEND_PID" > /dev/null 2>&1; then + echo "📡 Stopping Backend (PID: $BACKEND_PID)..." + kill_tree "$BACKEND_PID" TERM + sleep 1 + # Force kill if still running + if ps -p "$BACKEND_PID" > /dev/null 2>&1; then + kill_tree "$BACKEND_PID" KILL + fi + echo " ✓ Backend stopped" + else + echo " ⚠ Backend process not found" + fi + rm -f "$LOG_DIR/backend.pid" +else + echo " ⚠ No backend PID file found" +fi +echo "" + +# Stop Frontend +if [ -f "$LOG_DIR/frontend.pid" ]; then + FRONTEND_PID=$(cat "$LOG_DIR/frontend.pid") + if ps -p "$FRONTEND_PID" > /dev/null 2>&1; then + echo "🎨 Stopping Frontend (PID: $FRONTEND_PID)..." + kill_tree "$FRONTEND_PID" TERM + sleep 1 + # Force kill if still running + if ps -p "$FRONTEND_PID" > /dev/null 2>&1; then + kill_tree "$FRONTEND_PID" KILL + fi + echo " ✓ Frontend stopped" + else + echo " ⚠ Frontend process not found" + fi + rm -f "$LOG_DIR/frontend.pid" +else + echo " ⚠ No frontend PID file found" +fi +echo "" + +# Aggressive cleanup: kill ALL related processes by pattern matching +echo "🔍 Checking for any remaining processes..." + +# Kill all uvicorn processes for this app +BACKEND_PIDS=$(pgrep -f "uvicorn.*app\.main" 2>/dev/null || true) +if [ -n "$BACKEND_PIDS" ]; then + echo " Found orphaned backend processes: $BACKEND_PIDS" + for pid in $BACKEND_PIDS; do + kill_tree "$pid" KILL + done + echo " ✓ Killed orphaned backend processes" +fi + +# Kill all nuxt dev processes in this directory +FRONTEND_PIDS=$(pgrep -f "nuxt.*dev" | while read pid; do + if ps -p $pid -o args= | grep -q "$SCRIPT_DIR/frontend-sba"; then + echo $pid + fi +done) +if [ -n "$FRONTEND_PIDS" ]; then + echo " Found orphaned frontend processes: $FRONTEND_PIDS" + for pid in $FRONTEND_PIDS; do + kill_tree "$pid" KILL + done + echo " ✓ Killed orphaned frontend processes" +fi + +echo "✅ Services stopped successfully!"