feat(broker): per-user credential injection + per-(user,server) connection keying (spec 074 T7)#691
feat(broker): per-user credential injection + per-(user,server) connection keying (spec 074 T7)#691Dumbris wants to merge 2 commits into
Conversation
…ction keying (spec 074, MCP-1040) Wire the resolved per-user upstream credential (T6 CredentialResolver) into the outbound request, replacing any inbound/configured auth and keying brokered connections per (user, server). - transport: edition-neutral BrokeredAuth + EffectiveHeaders primitive that injects the resolved credential into the configured header, dropping any same-named inbound/configured header (case-insensitive). The gateway/IdP token is never forwarded (FR-016/FR-017). Applied in both the HTTP and SSE client builders. - upstream/core: Client carries an optional per-user BrokeredAuth; the headers-auth strategy (HTTP + SSE) injects it and runs even when the upstream has no static headers. - serveredition/broker: HeaderInjector bridges CredentialResolver to the transport layer, rejects stdio brokering as defense-in-depth (FR-002), and ConnectionKey binds a brokered connection to one (user, server) so one user's credential is never reused for another (FR-018). - serveredition/multiuser: Router.BrokeredConnectionKey exposes the per-(user,server) pooling key for brokered upstream connections. - docs: header-injection + per-(user,server) connection keying sections. TDD: outbound carries the user-specific token; the inbound token is absent on the outbound request across both HTTP and SSE paths; two users -> two distinct tokens and connection keys; stdio brokering rejected. Related #688 FR-016, FR-017, FR-018. SC-002, SC-003.
Deploying mcpproxy-docs with
|
| Latest commit: |
deb8008
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://9600327c.mcpproxy-docs.pages.dev |
| Branch Preview URL: | https://074-t7-injection-connection.mcpproxy-docs.pages.dev |
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
Claude review → ACCEPT on the isolation logic (credential injection + (user,server) connection keying both verified, fail-closed at the broker boundary, -race green, on-the-wire HTTP+SSE tests prove A's token reaches upstream, inbound auth header dropped). One change requested before merge (per maintainer): close the fail-closed gap now rather than deferring it to the activating PR. [FIX] Brokered connect must fail CLOSED — no fallback to no-auth/OAuth. The HTTP auth-strategy chain ( |
📦 Build ArtifactsWorkflow Run: View Run Available Artifacts
How to DownloadOption 1: GitHub Web UI (easiest)
Option 2: GitHub CLI gh run download 27570086880 --repo smart-mcp-proxy/mcpproxy-go
|
CEO Code Review: PR #691 — spec 074 T7, per-user credential injection + connection keyingVerdict: ACCEPT (CodexReviewer was unavailable; CEO performed review directly) What I checked
Minor observation (not blocking)
All spec requirements verified
Gate 3 satisfied — ready to merge. |
…llback (spec 074, MCP-1040) Maintainer fix-before-merge on PR #691. When a per-user brokered credential is set on a connection (brokeredAuth != nil), the ONLY permitted auth strategy is the brokered headers. Previously the headers -> no-auth -> OAuth fallback chain meant a failure of the per-user headers auth (e.g. 403 on the user's token) would fall through to no-auth (connect unauthenticated) or shared OAuth (borrow another identity) — defeating per-user isolation at the on-the-wire point. Extract httpAuthStrategies()/sseAuthStrategies(): a brokered connection returns ONLY the headers strategy, so a failure refuses the connection (no fallback). Non-brokered connections keep the historical headers -> no-auth -> OAuth chain unchanged. Test: TestClient_BrokeredConnection_FailsClosed_OnlyHeadersStrategy asserts the brokered strategy list is [headers] (no no-auth, no OAuth) for both HTTP and SSE, while non-brokered keeps the full chain. Affected packages green under -tags server -race; golangci-lint v2 clean. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Fail-closed fix applied —
|
|
ACCEPT — spec 074 T7 (MCP-1040). Reviewed at head Verification (clean worktree, not trusting the PR description)
Security review — the invariants that matter here all hold
Notes (non-blocking)
No blocking findings. Clean to merge per the gate. |
Summary
Spec 074 T7 (MCP-1040) — wires the per-user upstream credential resolved by T6's
CredentialResolverinto the outbound proxied request, and keys brokered connections per(user, server). Builds on the merged T6 (#688/#690).Gate 3: open for review — I do not merge.
What it does
internal/transport: a new edition-neutralBrokeredAuth+EffectiveHeadersprimitive injects the resolved credential into the configured header (defaultAuthorization: Bearer {token}), dropping any same-named inbound/configured header (case-insensitive) first. The inbound gateway/IdP token is never forwarded. Applied in both the HTTP and SSE client builders.internal/upstream/core:Clientcarries an optional per-userBrokeredAuth; the headers-auth strategy (HTTP + SSE) injects it and runs even when the upstream has no static headers.internal/serveredition/broker:HeaderInjectorturnsCredentialResolver.Resolve(user, server)into atransport.BrokeredAuth, rejects stdio brokering as defense-in-depth, andConnectionKey(user, server)binds a brokered connection to one(user, server)so one user's credential is never reused for another.internal/serveredition/multiuser:Router.BrokeredConnectionKeyexposes the per-(user, server)pooling key for the brokered connection pool.docs/features/auth-broker.md: header-injection + per-(user, server)connection keying sections.Tests (TDD)
httptestcapture)ErrBrokerStdioUnsupported)Verification
go build ./cmd/mcpproxy(personal) +go build -tags server ./cmd/mcpproxy(server) — both OKgo test -race ./internal/transport/ ./internal/upstream/core/— passgo test -tags server -race ./internal/serveredition/... ./internal/auth/...— passgolangci-lint v2.5.0 --config .github/.golangci.yml(incl.--build-tags server) — 0 issuesgofmt -l— cleanScope note
The workspace today is a config container (no live MCP connection pool yet). This PR delivers the injection + per-
(user,server)keying contract (ConnectionKey/Router.BrokeredConnectionKey) the pool must use; wiring an actual per-user live-connection pool belongs to the REST/runtime layer (T8) that consumes this key.Related #688
FR-016, FR-017, FR-018. SC-002, SC-003.