Skip to main content

Overview

All authenticated API routes in Brainstormer V2 follow a specific JWT pattern:
  1. JWT Authentication Middleware extracts user info from Bearer token
  2. Middleware sets request.user with userId, organizationId, email
  3. Route handlers access request.user.organizationId instead of reading headers directly
  4. The gateway verifies the JWT signature with verifyJwt(token, JWT_SECRET); downstream services (4001–4006, not publicly exposed) trust gateway-forwarded headers and do not re-verify

What NOT to Do

These anti-patterns will cause authentication failures. Never use them.

Never Read Headers Directly

Why this fails: The frontend never sets organization-id or user-id headers. These headers don’t exist in the request, so authentication will always fail with 401 errors.

Gateway Verifies, Downstream Services Trust Headers

Why: The gateway MUST verify the JWT signature with verifyJwt(token, JWT_SECRET) (HMAC-SHA256). Only downstream services (ports 4001–4006) skip re-verification — they trust the auth context the gateway forwards as headers, and must never be exposed to the public internet.

Correct Pattern

Step 1: Add JWT Authentication Middleware

Step 2: Access User Info via request.user

Step 3: Skip Authentication for Public Routes

Why the Gateway Verifies and Downstream Services Trust Headers

API Gateway (Port 4000) is the only public ingress and verifies JWT signatures with verifyJwt(token, JWT_SECRET) before routing to services.
Services trust requests from the gateway because service ports (4001–4006) are not exposed to the public internet — only the gateway is.
Avoids repeated signature verification across services once the gateway has verified.
Downstream services only read the gateway-forwarded headers, not manage secrets.

Organization Selection

Users can belong to multiple organizations. The primary org is always first in the JWT payload.

Common Errors and Solutions

Reference Implementation

Testing Checklist

  • Added JWT authentication middleware with fastify.addHook("preHandler", ...)
  • Defined AuthenticatedRequest and JWTPayload interfaces
  • Using request.user!.organizationId instead of reading headers
  • Public routes properly skip authentication
  • Tested with valid JWT token (should succeed)
  • Tested with missing Bearer token (should return 401)
  • Tested with invalid JWT format (should return 401)
Canonical reference implementation: /services/bot/src/routes/bots.routes.ts