Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions forge-core/mcp/platform_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,19 @@ func (p *platformTokenSource) fetch(ctx context.Context) (string, time.Duration,
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "Bearer "+identity)
// Tenancy headers, exactly as the admission + remote-session-store
// clients send them: the platform verifies a PER-ORG HS256 token, so it
// needs Org-Id to select the signing secret BEFORE it can validate the
// bearer (without this the endpoint 401s "missing org-id header"), and
// Workspace-Id to authorize the request against the entry (entitlement).
// Read from the standard FORGE_ORG_ID / FORGE_WORKSPACE_ID env the
// platform always injects; omitted when empty (standalone/dev).
if org := os.Getenv("FORGE_ORG_ID"); org != "" {
req.Header.Set("Org-Id", org)
}
if ws := os.Getenv("FORGE_WORKSPACE_ID"); ws != "" {
req.Header.Set("Workspace-Id", ws)
}

client := p.client
if client == nil {
Expand Down
10 changes: 10 additions & 0 deletions forge-core/mcp/platform_token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,19 @@ import (
// server ref, and can emit a (forbidden) refresh_token.
func newFakeResolver(t *testing.T, expiresIn int, includeRefresh bool) (*httptest.Server, *int) {
t.Helper()
t.Setenv("FORGE_ORG_ID", "org-42")
t.Setenv("FORGE_WORKSPACE_ID", "ws-7")
hits := 0
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hits++
// Tenancy headers must ride along so the platform can select the
// per-org HS256 secret + authorize the workspace (the "missing
// org-id header" 401 fix).
if r.Header.Get("Org-Id") != "org-42" || r.Header.Get("Workspace-Id") != "ws-7" {
w.WriteHeader(http.StatusUnauthorized)
_ = json.NewEncoder(w).Encode(map[string]string{"error": "missing org-id header"})
return
}
if r.Header.Get("Authorization") != "Bearer agent-cred-1" {
w.WriteHeader(http.StatusUnauthorized)
return
Expand Down
Loading