From 1fb0eb94c2c24bc94e8b5e32c58bc92c637588a7 Mon Sep 17 00:00:00 2001 From: Kelly Date: Wed, 10 Dec 2025 19:01:44 -0700 Subject: [PATCH] security: Add authMiddleware to analytics-v2 routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add authMiddleware to analytics-v2.ts to require authentication - Add permanent rule #6 to CLAUDE.md: "ALL API ROUTES REQUIRE AUTHENTICATION" - Add forbidden action #19: "Creating API routes without authMiddleware" - Document authentication flow and trusted origins 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 38 +++++++++++++++++++++++++++++- backend/src/routes/analytics-v2.ts | 11 +++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 0ff24bfe..508e267b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -119,7 +119,42 @@ npx tsx src/db/migrate.ts - Importing it at runtime causes startup crashes if env vars aren't perfect - `pool.ts` uses lazy initialization - only validates when first query is made -### 6. LOCAL DEVELOPMENT BY DEFAULT +### 6. ALL API ROUTES REQUIRE AUTHENTICATION — NO EXCEPTIONS + +**Every API router MUST apply `authMiddleware` at the router level.** + +```typescript +import { authMiddleware } from '../auth/middleware'; + +const router = Router(); +router.use(authMiddleware); // REQUIRED - first line after router creation +``` + +**Authentication flow (see `src/auth/middleware.ts`):** +1. Check Bearer token (JWT or API token) → grant access if valid +2. Check trusted origins (cannaiq.co, findadispo.com, localhost, etc.) → grant access +3. Check trusted IPs (127.0.0.1, ::1, internal pod IPs) → grant access +4. **Return 401 Unauthorized** if none of the above + +**NEVER create API routes without auth middleware:** +- No "public" endpoints that bypass authentication +- No "read-only" exceptions +- No "analytics-only" exceptions +- If an endpoint exists under `/api/*`, it MUST be protected + +**When creating new route files:** +1. Import `authMiddleware` from `../auth/middleware` +2. Add `router.use(authMiddleware)` immediately after creating the router +3. Document security requirements in file header comments + +**Trusted origins (defined in middleware):** +- `https://cannaiq.co` +- `https://findadispo.com` +- `https://findagram.co` +- `*.cannabrands.app` domains +- `localhost:*` for development + +### 7. LOCAL DEVELOPMENT BY DEFAULT **Quick Start:** ```bash @@ -452,6 +487,7 @@ const result = await pool.query(` 16. **Running `lsof -ti:PORT | xargs kill`** or similar process-killing commands 17. **Using hardcoded database names** in code or comments 18. **Creating or connecting to a second database** +19. **Creating API routes without authMiddleware** (all `/api/*` routes MUST be protected) --- diff --git a/backend/src/routes/analytics-v2.ts b/backend/src/routes/analytics-v2.ts index 0b33ca79..45933184 100644 --- a/backend/src/routes/analytics-v2.ts +++ b/backend/src/routes/analytics-v2.ts @@ -7,10 +7,17 @@ * Routes are prefixed with /api/analytics/v2 * * Phase 3: Analytics Engine + Rec/Med by State + * + * SECURITY: All routes require authentication via authMiddleware. + * Access is granted to: + * - Trusted origins (cannaiq.co, findadispo.com, etc.) + * - Trusted IPs (localhost, internal pods) + * - Valid JWT or API tokens */ import { Router, Request, Response } from 'express'; import { Pool } from 'pg'; +import { authMiddleware } from '../auth/middleware'; import { PriceAnalyticsService } from '../services/analytics/PriceAnalyticsService'; import { BrandPenetrationService } from '../services/analytics/BrandPenetrationService'; import { CategoryAnalyticsService } from '../services/analytics/CategoryAnalyticsService'; @@ -36,6 +43,10 @@ function parseLegalType(legalType?: string): LegalType { export function createAnalyticsV2Router(pool: Pool): Router { const router = Router(); + // SECURITY: Apply auth middleware to ALL routes + // This gate ensures only authenticated requests can access analytics data + router.use(authMiddleware); + // Initialize services const priceService = new PriceAnalyticsService(pool); const brandService = new BrandPenetrationService(pool);