fix(canvas): consolidate platform-auth headers via shared helper (#178) #54

Merged
claude-ceo-assistant merged 6 commits from fix/178-canvas-shared-auth-headers into main 2026-05-08 18:35:59 +00:00

Summary

  • Adds platformAuthHeaders() to canvas/src/lib/api.ts as the single source of truth for the bearer-token + tenant-slug header pair every authenticated canvas fetch needs.
  • Routes 7 previously-duplicated raw-fetch callsites through it: request() itself, uploadChatFiles, downloadChatFile, and the 5 Attachment* components (Image/Video/Audio/PDF/TextPreview).
  • Removes 4 duplicate local getTenantSlug() copies that inline-redefined what @/lib/tenant already exports.
  • Preserves the off-platform branch in AttachmentTextPreview (no bearer leakage to third-party URLs).

Why

Pre-fix, the 7 callsites each read NEXT_PUBLIC_ADMIN_TOKEN, manually attached the bearer, called a local-or-imported getTenantSlug(), and attached the slug header. A new poller / raw-fetch added without going through this exact recipe silently 401s against workspace-server when ADMIN_TOKEN is set on the server side. Closes the bug-shape gap called out in #178.

Test plan

  • 6 unit tests in platform-auth-headers.test.ts — empty, bearer-only, slug-only, both, empty-string-as-unset, fresh-object-per-call.
  • Mutation-test: removing the bearer attach inside the helper fails 2 of 6 immediately.
  • All 1389 existing canvas vitest tests pass unchanged (no regression).
  • npx tsc --noEmit clean.
  • npm run build succeeds.

Hostile self-review

  1. Behavior preservation in request() — the helper returns {} instead of { "Content-Type": "application/json" }; we spread the helper's result over an explicit Content-Type line so JSON requests still set it. FormData uploads (uploadChatFiles) deliberately do NOT set Content-Type so the browser writes the multipart boundary; comment added explaining this.
  2. AttachmentTextPreview off-platform branch — the original code only attached auth headers when isPlatformAttachment() was true. The new code preserves this with isPlatformAttachment(uri) ? platformAuthHeaders() : {}. Without that branch, off-platform fetches would leak the admin token to third-party origins.
  3. uploads.ts import surface change — removed import { getTenantSlug } from "@/lib/tenant" because it's no longer referenced. If a future caller in uploads.ts adds a slug-dependent code path, they'll either re-import or (more likely) reach for platformAuthHeaders() — the right path.

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

## Summary - Adds `platformAuthHeaders()` to `canvas/src/lib/api.ts` as the single source of truth for the bearer-token + tenant-slug header pair every authenticated canvas fetch needs. - Routes 7 previously-duplicated raw-fetch callsites through it: `request()` itself, `uploadChatFiles`, `downloadChatFile`, and the 5 `Attachment*` components (Image/Video/Audio/PDF/TextPreview). - Removes 4 duplicate local `getTenantSlug()` copies that inline-redefined what `@/lib/tenant` already exports. - Preserves the off-platform branch in `AttachmentTextPreview` (no bearer leakage to third-party URLs). ## Why Pre-fix, the 7 callsites each read `NEXT_PUBLIC_ADMIN_TOKEN`, manually attached the bearer, called a local-or-imported `getTenantSlug()`, and attached the slug header. A new poller / raw-fetch added without going through this exact recipe silently 401s against workspace-server when `ADMIN_TOKEN` is set on the server side. Closes the bug-shape gap called out in #178. ## Test plan - [x] 6 unit tests in `platform-auth-headers.test.ts` — empty, bearer-only, slug-only, both, empty-string-as-unset, fresh-object-per-call. - [x] Mutation-test: removing the bearer attach inside the helper fails 2 of 6 immediately. - [x] All 1389 existing canvas vitest tests pass unchanged (no regression). - [x] `npx tsc --noEmit` clean. - [x] `npm run build` succeeds. ## Hostile self-review 1. **Behavior preservation in `request()`** — the helper returns `{}` instead of `{ "Content-Type": "application/json" }`; we spread the helper's result over an explicit `Content-Type` line so JSON requests still set it. FormData uploads (`uploadChatFiles`) deliberately do NOT set Content-Type so the browser writes the multipart boundary; comment added explaining this. 2. **`AttachmentTextPreview` off-platform branch** — the original code only attached auth headers when `isPlatformAttachment()` was true. The new code preserves this with `isPlatformAttachment(uri) ? platformAuthHeaders() : {}`. Without that branch, off-platform fetches would leak the admin token to third-party origins. 3. **`uploads.ts` import surface change** — removed `import { getTenantSlug } from "@/lib/tenant"` because it's no longer referenced. If a future caller in `uploads.ts` adds a slug-dependent code path, they'll either re-import or (more likely) reach for `platformAuthHeaders()` — the right path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude-ceo-assistant added 1 commit 2026-05-07 21:36:25 +00:00
fix(canvas): consolidate platform-auth headers via shared helper (#178)
Some checks failed
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 6s
CI / Detect changes (pull_request) Successful in 8s
Retarget main PRs to staging / Retarget to staging (pull_request) Has been skipped
E2E API Smoke Test / detect-changes (pull_request) Successful in 7s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 7s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 7s
Harness Replays / detect-changes (pull_request) Successful in 7s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 7s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 7s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 3s
CI / Platform (Go) (pull_request) Successful in 4s
CI / Python Lint & Test (pull_request) Successful in 4s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 5s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 5s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 5s
Harness Replays / Harness Replays (pull_request) Failing after 36s
CodeQL / Analyze (${{ matrix.language }}) (go) (pull_request) Failing after 1m22s
CodeQL / Analyze (${{ matrix.language }}) (javascript-typescript) (pull_request) Failing after 1m22s
CodeQL / Analyze (${{ matrix.language }}) (python) (pull_request) Failing after 1m23s
CI / Canvas (Next.js) (pull_request) Failing after 1m39s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 3m55s
501d07b0f2
Closes the post-Task-#176 self-review gap: the bearer-token + tenant-
slug header construction was duplicated across 7 raw-fetch callsites
in the canvas (lib/api.ts request(), uploads.ts × 2, and 5 Attachment*
components). Each callsite read NEXT_PUBLIC_ADMIN_TOKEN, attached
Authorization: Bearer manually, computed getTenantSlug locally
(three of them inline-redefined it from /lib/tenant!), and attached
X-Molecule-Org-Slug. A new poller / raw-fetch added without going
through this exact recipe silently 401s against workspace-server when
ADMIN_TOKEN is set on the server side — the bug shape called out in
the original task.

Adds platformAuthHeaders() to lib/api.ts as the single source of truth
and routes all 7 raw-fetch callsites through it. Removes 4 duplicate
local getTenantSlug() copies (Image, Video, Audio, PDF, TextPreview)
that were inline-redefining what /lib/tenant.ts already exports.

Also preserves the AttachmentTextPreview off-platform branch — when
isPlatformAttachment() is false, headers is {} (no bearer leakage to
third-party URLs).

Tests:
- 6 unit tests in platform-auth-headers.test.ts covering: empty,
  bearer-only, slug-only, both, empty-string-as-unset, fresh-object-
  per-call. Mutation-tested: removing the bearer attach inside the
  helper fails 2 of 6 tests immediately.
- All 1389 existing canvas vitest tests pass unchanged.
- npx tsc --noEmit clean.
- npm run build succeeds (canvas Next.js build).

Per feedback_assert_exact_not_substring: tests use exact toEqual()
equality, not substring/contains, so an extra-header bug also fails
the assertion. Per feedback_oss_design_philosophy: this is the
"plugin/abstract/modular/SSOT" move applied to the auth-header
construction surface — one helper, six call sites, no duplication.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude-ceo-assistant added 1 commit 2026-05-07 22:24:46 +00:00
Merge branch 'main' into fix/178-canvas-shared-auth-headers
Some checks failed
CodeQL / Analyze (${{ matrix.language }}) (go) (pull_request) Successful in 5s
CodeQL / Analyze (${{ matrix.language }}) (javascript-typescript) (pull_request) Successful in 5s
CodeQL / Analyze (${{ matrix.language }}) (python) (pull_request) Successful in 5s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 12s
pr-guards / disable-auto-merge-on-push (pull_request) Failing after 4s
CI / Detect changes (pull_request) Successful in 12s
E2E API Smoke Test / detect-changes (pull_request) Successful in 12s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 13s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 13s
Harness Replays / detect-changes (pull_request) Successful in 14s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 13s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 13s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 4s
CI / Platform (Go) (pull_request) Successful in 6s
CI / Python Lint & Test (pull_request) Successful in 6s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 7s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 8s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 11s
Harness Replays / Harness Replays (pull_request) Failing after 37s
CI / Canvas (Next.js) (pull_request) Failing after 2m54s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 4m25s
e4b1248f47
claude-ceo-assistant added 1 commit 2026-05-08 00:20:49 +00:00
Merge branch 'main' into fix/178-canvas-shared-auth-headers
Some checks failed
CodeQL / Analyze (${{ matrix.language }}) (go) (pull_request) Successful in 8s
CodeQL / Analyze (${{ matrix.language }}) (javascript-typescript) (pull_request) Successful in 8s
CodeQL / Analyze (${{ matrix.language }}) (python) (pull_request) Successful in 8s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 16s
pr-guards / disable-auto-merge-on-push (pull_request) Failing after 8s
CI / Detect changes (pull_request) Successful in 19s
E2E API Smoke Test / detect-changes (pull_request) Successful in 17s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 17s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 16s
Harness Replays / detect-changes (pull_request) Successful in 16s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 15s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 16s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 7s
CI / Platform (Go) (pull_request) Successful in 9s
CI / Python Lint & Test (pull_request) Successful in 8s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 11s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 12s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 9s
Harness Replays / Harness Replays (pull_request) Failing after 1m20s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 6m26s
CI / Canvas (Next.js) (pull_request) Failing after 8m35s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
9c82b2a61c
claude-ceo-assistant added 1 commit 2026-05-08 00:54:20 +00:00
Merge branch 'main' into fix/178-canvas-shared-auth-headers
Some checks failed
CodeQL / Analyze (${{ matrix.language }}) (go) (pull_request) Successful in 6s
CodeQL / Analyze (${{ matrix.language }}) (javascript-typescript) (pull_request) Successful in 6s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 17s
CodeQL / Analyze (${{ matrix.language }}) (python) (pull_request) Successful in 6s
CI / Detect changes (pull_request) Successful in 17s
pr-guards / disable-auto-merge-on-push (pull_request) Failing after 7s
E2E API Smoke Test / detect-changes (pull_request) Successful in 14s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 14s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 16s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 14s
Harness Replays / detect-changes (pull_request) Successful in 16s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 15s
CI / Platform (Go) (pull_request) Successful in 10s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 9s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 11s
CI / Python Lint & Test (pull_request) Successful in 12s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 11s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 16s
Harness Replays / Harness Replays (pull_request) Failing after 1m13s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 6m40s
CI / Canvas (Next.js) (pull_request) Failing after 8m13s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
7f86a245bf
claude-ceo-assistant added 1 commit 2026-05-08 01:27:49 +00:00
Merge branch 'main' into fix/178-canvas-shared-auth-headers
Some checks failed
CodeQL / Analyze (${{ matrix.language }}) (go) (pull_request) Successful in 9s
CodeQL / Analyze (${{ matrix.language }}) (javascript-typescript) (pull_request) Successful in 9s
CodeQL / Analyze (${{ matrix.language }}) (python) (pull_request) Successful in 9s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 14s
pr-guards / disable-auto-merge-on-push (pull_request) Failing after 7s
CI / Detect changes (pull_request) Successful in 17s
E2E API Smoke Test / detect-changes (pull_request) Successful in 16s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 16s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 16s
Harness Replays / detect-changes (pull_request) Successful in 16s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 16s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 16s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 7s
CI / Platform (Go) (pull_request) Successful in 8s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 9s
CI / Python Lint & Test (pull_request) Successful in 10s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 11s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 8s
Harness Replays / Harness Replays (pull_request) Failing after 1m59s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 5m54s
CI / Canvas (Next.js) (pull_request) Failing after 8m34s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
5c62f172f0
claude-ceo-assistant added 1 commit 2026-05-08 02:46:43 +00:00
Merge branch 'main' into fix/178-canvas-shared-auth-headers
All checks were successful
Harness Replays / Harness Replays (pull_request) Successful in 2m8s
CI / Canvas (Next.js) (pull_request) Successful in 5m49s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 6m19s
CodeQL / Analyze (${{ matrix.language }}) (go) (pull_request) Successful in 6s
CodeQL / Analyze (${{ matrix.language }}) (javascript-typescript) (pull_request) Successful in 7s
CodeQL / Analyze (${{ matrix.language }}) (python) (pull_request) Successful in 8s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 14s
CI / Detect changes (pull_request) Successful in 16s
pr-guards / disable-auto-merge-on-push (pull_request) Successful in 8s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 15s
E2E API Smoke Test / detect-changes (pull_request) Successful in 17s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 16s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 15s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 18s
Harness Replays / detect-changes (pull_request) Successful in 19s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 8s
CI / Platform (Go) (pull_request) Successful in 10s
CI / Python Lint & Test (pull_request) Successful in 8s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 11s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 9s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 11s
b398667fce
claude-ceo-assistant merged commit bce60f1b22 into main 2026-05-08 18:35:59 +00:00
claude-ceo-assistant deleted branch fix/178-canvas-shared-auth-headers 2026-05-08 18:35:59 +00:00
Sign in to join this conversation.
No reviewers
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#54
No description provided.