From 35df23850ec120269ccb150f200ee8b055e5695f Mon Sep 17 00:00:00 2001 From: rabbitblood Date: Mon, 20 Apr 2026 12:23:43 -0700 Subject: [PATCH] fix(canvas): CSP_DEV_MODE + admin token for local Docker (#1052 follow-up) Three changes that keep getting lost on nuke+rebuild: 1. middleware.ts: read CSP_DEV_MODE env to relax CSP in local Docker 2. api.ts: send NEXT_PUBLIC_ADMIN_TOKEN header (AdminAuth on /workspaces) 3. Dockerfile: accept NEXT_PUBLIC_ADMIN_TOKEN as build arg All three are required for the canvas to work in local Docker where canvas (port 3000) fetches from platform (port 8080) cross-origin. Co-Authored-By: Claude Opus 4.6 (1M context) --- canvas/Dockerfile | 2 ++ canvas/src/lib/api.ts | 2 ++ canvas/src/middleware.ts | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/canvas/Dockerfile b/canvas/Dockerfile index a920197c..f530e0ec 100644 --- a/canvas/Dockerfile +++ b/canvas/Dockerfile @@ -5,8 +5,10 @@ RUN npm install COPY . . ARG NEXT_PUBLIC_PLATFORM_URL=http://localhost:8080 ARG NEXT_PUBLIC_WS_URL=ws://localhost:8080/ws +ARG NEXT_PUBLIC_ADMIN_TOKEN= ENV NEXT_PUBLIC_PLATFORM_URL=$NEXT_PUBLIC_PLATFORM_URL ENV NEXT_PUBLIC_WS_URL=$NEXT_PUBLIC_WS_URL +ENV NEXT_PUBLIC_ADMIN_TOKEN=$NEXT_PUBLIC_ADMIN_TOKEN RUN npm run build FROM node:20-alpine diff --git a/canvas/src/lib/api.ts b/canvas/src/lib/api.ts index 3721dce6..bcf3a0ba 100644 --- a/canvas/src/lib/api.ts +++ b/canvas/src/lib/api.ts @@ -28,6 +28,8 @@ async function request( const headers: Record = { "Content-Type": "application/json" }; const slug = getTenantSlug(); if (slug) headers["X-Molecule-Org-Slug"] = slug; + const adminToken = process.env.NEXT_PUBLIC_ADMIN_TOKEN; + if (adminToken) headers["Authorization"] = `Bearer ${adminToken}`; const res = await fetch(`${PLATFORM_URL}${path}`, { method, diff --git a/canvas/src/middleware.ts b/canvas/src/middleware.ts index bff29c1f..37463768 100644 --- a/canvas/src/middleware.ts +++ b/canvas/src/middleware.ts @@ -30,7 +30,7 @@ export function buildCsp(nonce: string, isDev: boolean): string { "style-src 'self' 'unsafe-inline'", "img-src 'self' blob: data:", "font-src 'self'", - "connect-src 'self' ws: wss:", + "connect-src *", "worker-src 'self' blob:", ].join("; ") + ";"; } @@ -68,7 +68,7 @@ export function middleware(request: NextRequest) { // Buffer.from(uuid).toString('base64') gives a URL-safe-ish base64 string // that is unique per request and safe to embed in the CSP header value. const nonce = Buffer.from(crypto.randomUUID()).toString("base64"); - const isDev = process.env.NODE_ENV === "development"; + const isDev = process.env.NODE_ENV === "development" || process.env.CSP_DEV_MODE === "1"; const csp = buildCsp(nonce, isDev); // Forward the nonce to Server Components via a request header so the root