Context
Follow-up to PR #10768 (feat(auth): add proxy identity injection via X-PREFERRED-USERNAME (#10727)). Surfaced during cross-family review by @neo-opus-4-7 (review comment) — the trust-boundary code in TransportService.mjs ships without unit-test coverage. Substantively correct (verified end-to-end in review), but security-relevant trust-boundary code without tests is a structural risk: regressions to OIDC precedence or source-tag propagation would be caught only at integration time.
The Problem
The proxy-identity injection logic added in PR #10768 (TransportService.mjs:142-170) handles three distinct branches:
- OIDC-active path:
req.auth populated → proxy header IGNORED, OIDC takes precedence
- Proxy-active path:
req.auth undefined + trustProxyIdentity=true + header present → identity injected with source: 'proxy-header'
- Gate-disabled path: header present +
trustProxyIdentity=false → no identity injected, single-tenant fallthrough
Each branch has trust-boundary semantics. Without tests, future refactors could silently flip precedence, drop the source-tag, or leak proxy-header values into OIDC-active deployments.
The Architectural Reality
ai/mcp/server/shared/services/TransportService.mjs:142-170 — the branch logic introduced by PR #10768
aiConfig.auth.trustProxyIdentity — the gate-config flag (default false)
- Header-name conventions:
x-preferred-username (canonical) + x-auth-request-preferred-username (oauth2-proxy variant)
Server.mjs#buildRequestContext:259 — downstream consumer that propagates source: reqAuth.source || 'oidc'
Existing test infrastructure: test/playwright/unit/ai/mcp/server/memory-core/services/HealthService.spec.mjs provides the pattern (module-scope pure-function unit tests with test.beforeAll import). TransportService is testable similarly if the branch logic is extractable into a pure function, OR via mock req shape with the existing class instance.
The Fix
Add a unit-test spec at test/playwright/unit/ai/mcp/server/shared/services/TransportService.spec.mjs covering the three branches:
- OIDC precedence test:
req.auth populated + trustProxyIdentity=true + proxy header set → identity comes from req.auth, NOT proxy header. source is whatever req.auth.source was (typically 'oidc').
- Proxy-active test:
req.auth undefined + trustProxyIdentity=true + x-preferred-username header set → identity injected with userId matching header, source: 'proxy-header'.
- Proxy oauth2-proxy variant test: same as #2 but with
x-auth-request-preferred-username header instead — both header conventions yield the same result.
- Gate-disabled test:
req.auth undefined + trustProxyIdentity=false + header present → no identity injected, requestContext is single-tenant fallthrough shape.
- Both-headers test: both
x-preferred-username AND x-auth-request-preferred-username present → first non-empty wins (canonical name preferred, currently).
- Header-spoof test (load-bearing security):
req.auth undefined + trustProxyIdentity=false + adversarial proxy headers → identity NOT injected. Pins the gate behavior so a future refactor can't accidentally enable header reading without the explicit opt-in flag.
Acceptance Criteria
Out of Scope
- Integration tests against a live oauth2-proxy deployment (operator-territory L3)
- Per-request audit-log assertions (separate observability concern)
- Header-name configurability beyond the two currently-supported variants
Related
- Adjacent shipped: PR #10768 (the code under test)
- Adjacent doc: PR #10769 (operator-doc + threat-model statement)
- Parent epic: #10721 (Shared deployment MVP completeness gaps)
- Pattern precedents:
HealthService.spec.mjs (module-scope pure-function unit testing pattern)
Origin Session ID: 23b9cbcd-4938-4a46-b21a-0d48dd12e7e7
Retrieval Hint: query_raw_memories(query="TransportService proxy-identity unit test trust-boundary OIDC precedence header spoof gate 10727 10768")
Context
Follow-up to PR #10768 (
feat(auth): add proxy identity injection via X-PREFERRED-USERNAME (#10727)). Surfaced during cross-family review by @neo-opus-4-7 (review comment) — the trust-boundary code inTransportService.mjsships without unit-test coverage. Substantively correct (verified end-to-end in review), but security-relevant trust-boundary code without tests is a structural risk: regressions to OIDC precedence or source-tag propagation would be caught only at integration time.The Problem
The proxy-identity injection logic added in PR #10768 (
TransportService.mjs:142-170) handles three distinct branches:req.authpopulated → proxy header IGNORED, OIDC takes precedencereq.authundefined +trustProxyIdentity=true+ header present → identity injected withsource: 'proxy-header'trustProxyIdentity=false→ no identity injected, single-tenant fallthroughEach branch has trust-boundary semantics. Without tests, future refactors could silently flip precedence, drop the source-tag, or leak proxy-header values into OIDC-active deployments.
The Architectural Reality
ai/mcp/server/shared/services/TransportService.mjs:142-170— the branch logic introduced by PR #10768aiConfig.auth.trustProxyIdentity— the gate-config flag (defaultfalse)x-preferred-username(canonical) +x-auth-request-preferred-username(oauth2-proxy variant)Server.mjs#buildRequestContext:259— downstream consumer that propagatessource: reqAuth.source || 'oidc'Existing test infrastructure:
test/playwright/unit/ai/mcp/server/memory-core/services/HealthService.spec.mjsprovides the pattern (module-scope pure-function unit tests withtest.beforeAllimport).TransportServiceis testable similarly if the branch logic is extractable into a pure function, OR via mockreqshape with the existing class instance.The Fix
Add a unit-test spec at
test/playwright/unit/ai/mcp/server/shared/services/TransportService.spec.mjscovering the three branches:req.authpopulated +trustProxyIdentity=true+ proxy header set → identity comes fromreq.auth, NOT proxy header.sourceis whateverreq.auth.sourcewas (typically'oidc').req.authundefined +trustProxyIdentity=true+x-preferred-usernameheader set → identity injected withuserIdmatching header,source: 'proxy-header'.x-auth-request-preferred-usernameheader instead — both header conventions yield the same result.req.authundefined +trustProxyIdentity=false+ header present → no identity injected, requestContext is single-tenant fallthrough shape.x-preferred-usernameANDx-auth-request-preferred-usernamepresent → first non-empty wins (canonical name preferred, currently).req.authundefined +trustProxyIdentity=false+ adversarial proxy headers → identity NOT injected. Pins the gate behavior so a future refactor can't accidentally enable header reading without the explicit opt-in flag.Acceptance Criteria
test/playwright/unit/ai/mcp/server/shared/services/TransportService.spec.mjs(or extend an existing TransportService spec if one exists post-PR)TransportService.mjs:142-170branch logicresolveAuthContext(req, cfg)pure function (similar tobuildIdentityBlock/buildTopologyBlock/buildEmbeddingProviderBlockprecedents); delegate fromTransportService.setupnpx playwright test test/playwright/unit/...infrastructurefeedback_makesafe_strips_internal_flagsdiscipline — direct module imports for trust-boundary tests, not test-harness wrappers)Out of Scope
Related
HealthService.spec.mjs(module-scope pure-function unit testing pattern)Origin Session ID: 23b9cbcd-4938-4a46-b21a-0d48dd12e7e7
Retrieval Hint:
query_raw_memories(query="TransportService proxy-identity unit test trust-boundary OIDC precedence header spoof gate 10727 10768")