fix(sdk): promote validated forwarder identity on Internal-scope requests#138
Merged
Conversation
…ests On the dev/tunnel plane, dispatch strip-then-injects the X-MS-User-ID / X-MS-App-ID / X-MS-App-Role identity headers on every Internal-scope forward, but internalAuth only validated the platform secret and never promoted those headers into ctx. Any Internal handler reading ms.AppID(r.Context()) therefore got "" when tunnel-served and 401'd: ms-app-modules#30 fixed oauth-google's platformCallback to read ctx identity (correct for the deployed path) leaving it latently broken over the tunnel, and the new cross-read route (ms-app-modules#31) hit the same wall, blocking the dev-to-dev and dev-to-deployed E2Es. Fix at the shared seam: after internalAuth's constant-time secret check succeeds, promote the forwarder-injected identity triple into ctx via promoteForwarderIdentity — the exact mechanism extracted from requireProxy's success path, so both HTTP header-ingestion paths share one helper. The validated secret is the same trust signal requireProxy relies on: identity headers on this path can only come from the platform wire (dispatch / dev tunnel proxy). Fail-closed properties preserved: - promotion fires ONLY on the secret-validated success path — never on the local no-secret bypass (unauthenticated transport), never on the 401/503 rejection branches - an identity already on ctx wins (deployed path: PayloadTrusted early-returns before promotion, so the envelope identity set by runtime.InjectResources stays authoritative) - empty headers never mint an identity (same rule InjectResources applies to an all-empty envelope) Covers every internal surface through the single cached m.internalAuth closure: Module.Internal() routes plus the system event/cron/task mounts. Note: PR #137 (feat/ctx-identity-accessors) touches auth/middleware.go doc comments and adds ms.UserID/ms.AppRole accessors; this branch is cut from origin/main with edits kept minimal so a rebase across #137 (in either landing order) stays trivial. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-op branches Simplify-pass follow-up on the Internal-scope identity promotion. - Efficiency: the helper returned context.Context, forcing both call sites into an unconditional r.WithContext() — an http.Request clone (~500B) even on the two no-op branches (identity already preset, or all-empty headers). For internalAuth that was a new regression: its pre-fix success path was a zero-alloc next.ServeHTTP(w, r). Return *http.Request instead and hand back r unchanged on the no-op branches, so a headerless system-to-system internal call (events, crons) stays allocation-free on its hot path. - Doc: the helper comment claimed it was shared by "the two secret-validated HTTP paths", reading as if only two exist. platformAuth step 4 is a third, deliberately stricter (all-three-required) hand-rolled variant — name it as the intentional exception so the doc no longer overclaims. gofmt/build/vet clean; go test ./auth/... ./internal/core/... green. Co-Authored-By: Claude Fable 5 <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.
Problem
Internal scope never promotes forwarded identity into ctx on the dev plane — tunnel-served Internal handlers reading
ms.AppID(ctx)get 401. Latent in ms-app-modules#30; blocks the #31 cross-read E2E.Fix
Promotion lands in the Internal-scope auth middleware, at the same altitude as
requireProxy: identity is derived once at the wire boundary and installed into ctx before any handler runs, so handlers stay transport-agnostic (ms.AppID(ctx), never headers). Trust model is identical torequireProxy— forwarded identity headers are trusted only after the validated internal secret authenticates the platform wire; the deployed envelope identity is never clobbered (envelope-derived ctx identity always wins when present).Verification
gofmt -l clean on all 3 touched files;
go build ./...OK;go vet ./auth/... ./internal/core/...OK;go test ./...all packages ok; targetedgo test -count=1 -run "InternalAuth|RequireProxy|TunnelForwarded" ./auth/... ./internal/core/...verbose: all PASS including the 6 new auth tests and the end-to-end internal/core test. Negative verification reproduced independently (middleware reverted → both regression tests fail with identity nil /ms.AppID=""), worktree restored to clean at commit bd614c3.Relationship
Independent of open #137; trivial rebase whichever merges first.
🤖 Generated with Claude Code