feat: ms.AppID accessor + proxy guard promotes trusted app identity on Public#119
Merged
Conversation
…n Public A module can now read its own trusted app id on every guarded surface, including Public routes which previously had no identity at all. The gap: ms.Public routes mount only proxyGuard; PlatformAuth (which promotes the trusted X-MS-App-ID header into auth.Identity) runs only on Platform routes. So on a Public route auth.Get(ctx).AppID was empty and a module could not read its trusted app id. Prod Lambda already injects identity via runtime.InjectResources; dev/HTTP Public did not — this closes that asymmetry. - requireProxy: on the validated-token success path ONLY (after the token check proves the X-MS-* headers were dispatch-injected, not client-forged), promote them to auth.Identity (AppID/UserID/AppRole) before next.ServeHTTP. Never promote on the inert/standalone or rejected paths; never clobber an identity already set (Lambda's InjectResources). - ms.AppID(ctx): inbound twin of ms.WithAppID; returns auth.Get(ctx).AppID or "". The single unspoofable way a module reads its own app id. - Reconcile ms.WithAppID doc (it now retargets an outbound Call at a different app; reading your own app is ms.AppID). - docs/concepts/scopes.md (+ zh-TW): Public/Platform handlers read their trusted app via ms.AppID, never from request data. - Fold PR #118's requireProxy dedup (rejectNotProxied helper) — #118 should be closed/rebased onto this. - VERSION 0.2.0 -> 0.2.1, CHANGELOG (patch-only per 0.x convention). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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
Implements the SDK half of the #236 redesign (architecture-panel verdict): a first-class trusted app-id accessor plus Public-route identity promotion. A module can now read its own trusted app id on every guarded surface — including Public routes, which previously had no identity at all.
The gap (panel-traced)
ms.Publicroutes mount onlyproxyGuard(internal/core/module.go~315-321).PlatformAuth— which promotes the trustedX-MS-App-IDheader intoauth.Identity— runs only on Platform routes (~303-310). So on a Public routeauth.Get(ctx).AppIDwas empty and a module could not read its trusted app id. Prod Lambda already injects identity viaruntime.InjectResources(inject.go~61-67); dev/HTTP Public did not. This PR closes that asymmetry.Changes
requireProxy(auth/middleware.go) — on the validated-token success path ONLY (the path reached after the platform-token check passes), promote the dispatch-injectedX-MS-User-ID/X-MS-App-ID/X-MS-App-Roleheaders toauth.Identitybeforenext.ServeHTTP.X-MS-*headers came through dispatch (the browser never holds the token, so a direct caller cannot forge a request that reaches here).InjectResources).ms.AppID(ctx) string(mirrorstack.go, forwards tocore.AppID) — the inbound twin ofms.WithAppID. Returnsauth.Get(ctx).AppIDor"". The single unspoofable way a module reads its own app id.VERSION0.2.0 → 0.2.1, CHANGELOG[v0.2.1](patch-only per the 0.x release convention). Add thereleaselabel to cut the tag on merge.docs/concepts/scopes.md(+ zh-TW): Public/Platform handlers read their trusted app viams.AppID(ctx), not request data; reconciled thems.WithAppIDdoc comment (it now describes retargeting an outboundms.Callat a different app, withms.AppIDas the read path for your own app).Coordination with #118
Open simplify PR #118 also edits
requireProxy(dedups its two rejection paths into arejectNotProxiedhelper). This PR folds that dedup in (same helper, same body) since it touches the same lines. #118 should be closed (or rebased onto this) — its change is fully contained here.Tests
TestRequireProxy_ValidToken_PromotesIdentity— Public request with a valid token givesms.AppIDthe injected app id.TestRequireProxy_RejectedRequest_NeverPromotes— rejected (no token) never reaches the handler / promotion.TestRequireProxy_NoSecretInert_DoesNotPromote— standalone (no token configured, guard inert) documented behavior: open surface, no promotion of unvalidated headers.TestRequireProxy_PresetIdentity_NotClobbered— preset identity (Lambda path) wins over headers.TestPublic_ProxyGuard_PromotesTrustedAppID— end-to-end through the real module router: a Public handler readsms.AppID= the dispatch-injected app.TestAppID(facade) — empty when unset, reads context identity, inbound twin ofWithAppID.Verification
go build ./...,go vet ./..., andgo test ./...(full suite, also-raceon the changed packages) all green.Report contract (for the oauth-core track)
func AppID(ctx context.Context) string— exported asms.AppID(mirrorstack.go) forwarding tocore.AppID(internal/core/call.go). Returnsauth.Get(ctx).AppID, else"".auth.requireProxy, on the success path afterconstantTimeEqual(token, expected)passes — setsauth.Identity{UserID, AppID, AppRole}from theX-MS-*headers (only if no identity is already set), beforenext.ServeHTTP. This runs for both Public and Platform surfaces (proxyGuard fronts both); Lambda and inert/rejected paths do not promote.🤖 Generated with Claude Code