feat(auth): PlatformAuth trusts identity from trusted forwarder + local-dev bypass#103
Merged
Merged
Conversation
…ypass
Closes the loop on the tunnel-auth workstream's SDK slice (memory:
feedback_tunnel_auth_no_bypass). PlatformAuth now has three identity
sources, in priority order:
1. Identity preset in ctx (Lambda authorizer path — unchanged).
2. Trusted-forwarder injection: a request carrying a valid
X-MS-Internal-Secret may also assert user identity via
X-MS-User-ID / X-MS-App-ID / X-MS-App-Role headers, which
PlatformAuth promotes to ctx.Identity. Same trust signal
InternalAuth uses; no second secret needed.
3. Local-dev bypass: when MS_INTERNAL_SECRET is unset AND we're
not in Lambda, a synthetic admin identity is injected so
`mirrorstack dev` (no tunnel) can render platform-scope
routes without the developer wiring auth manually. Matches
InternalAuth's local bypass shape.
Behavior matrix mirrors InternalAuth:
inLambda + secret unset → 503 (operator misconfig)
inLambda + secret set → enforce (Lambda authorizer wins if preset)
local + secret unset → bypass with synthetic admin
local + secret set → enforce (tunnel mode — CLI sets the
secret via mirrorstack-cli #33)
The trusted-forwarder design unblocks the cascade:
- api-platform proxy/dispatch attaches signed headers when
forwarding browser requests on behalf of an authenticated
platform user → modules see the right Identity automatically.
- In local dev with no tunnel, the bypass lets module UIs render
against platform-scope routes without coordinating env between
web-applications and the running module.
Test plan:
- All 7 auth.PlatformAuth* tests pass (preset ctx, local bypass,
Lambda 503, missing secret, wrong secret, missing identity
headers, valid headers → identity injected).
- internal/core.TestPlatform_LocalDevBypass_InjectsSyntheticAdmin
and TestPlatform_SecretSet_RejectsNoHeader replace the old
TestPlatform_RejectsNoAuth, pinning the new contract end-to-end
through the SDK's scope-routes plumbing.
- Whole-repo `go test ./...` clean except for a pre-existing
flake in internal/refcache.TestSlowPath_SingleflightCoalesces
(concurrency timing, unrelated to auth).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Second slice of the tunnel-auth workstream (see `memory/feedback_tunnel_auth_no_bypass.md`). Closes the SDK side of the gap that today makes `/platform/*` routes unreachable from `mirrorstack dev` UIs:
This PR teaches PlatformAuth two new identity sources. No second secret is introduced — the same `MS_INTERNAL_SECRET` that PR mirrorstack-cli#33 injects now also gates trusted-forwarder identity injection.
New behavior matrix
```
inLambda + secret unset → 503 (operator misconfig — matches InternalAuth)
inLambda + secret set → enforce: validate secret + identity headers
(Lambda authorizer path still wins if Identity
is preset in ctx)
local + secret unset → BYPASS: inject synthetic admin Identity so
'mirrorstack dev' (no tunnel) can render
platform-scope routes without ceremony
local + secret set → enforce ('mirrorstack dev --tunnel' — CLI sets
the secret via mirrorstack-cli#33)
```
Identity sources, first match wins:
Cascade unblocked
With (1)+(2)+(4) landed, plain `mirrorstack dev` (no tunnel) renders modules' `/platform/*` routes end-to-end against a synthetic admin identity. (3) is the production-shape path that handles real users via dispatch.
Test plan
Notes
🤖 Generated with Claude Code