From 1e30386aeccd2ee83eb6dece96a545f93ec94635 Mon Sep 17 00:00:00 2001 From: rabbitblood Date: Mon, 20 Apr 2026 12:40:20 -0700 Subject: [PATCH] fix(auth): accept admin token in WorkspaceAuth for canvas dashboard The canvas sends NEXT_PUBLIC_ADMIN_TOKEN on all API calls but per-workspace routes (/activity, /delegations, /traces) use WorkspaceAuth which only accepts per-workspace bearer tokens. This made the canvas dashboard 401 on every workspace detail view. Fix: WorkspaceAuth now accepts the admin token as a fallback after workspace token validation fails. This lets the canvas read all workspace data with a single admin credential. Co-Authored-By: Claude Opus 4.6 (1M context) --- workspace-server/internal/middleware/wsauth_middleware.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/workspace-server/internal/middleware/wsauth_middleware.go b/workspace-server/internal/middleware/wsauth_middleware.go index 2759586d..d8175c36 100644 --- a/workspace-server/internal/middleware/wsauth_middleware.go +++ b/workspace-server/internal/middleware/wsauth_middleware.go @@ -45,6 +45,14 @@ func WorkspaceAuth(database *sql.DB) gin.HandlerFunc { tok := wsauth.BearerTokenFromHeader(c.GetHeader("Authorization")) if tok != "" { + // Admin token fallback — lets the canvas dashboard read workspace + // activity, traces, delegations with a single admin credential. + adminSecret := os.Getenv("ADMIN_TOKEN") + if adminSecret != "" && subtle.ConstantTimeCompare([]byte(tok), []byte(adminSecret)) == 1 { + c.Next() + return + } + // Per-workspace token if err := wsauth.ValidateToken(ctx, database, workspaceID, tok); err != nil { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid workspace auth token"}) return