Enrich Identity with upstream tokens and simplify upstreamswap#4355
Enrich Identity with upstream tokens and simplify upstreamswap#4355
Conversation
There was a problem hiding this comment.
Large PR Detected
This PR exceeds 1000 lines of changes and requires justification before it can be reviewed.
How to unblock this PR:
Add a section to your PR description with the following format:
## Large PR Justification
[Explain why this PR must be large, such as:]
- Generated code that cannot be split
- Large refactoring that must be atomic
- Multiple related changes that would break if separated
- Migration or data transformationAlternative:
Consider splitting this PR into smaller, focused changes (< 1000 lines each) for easier review and reduced risk.
See our Contributing Guidelines for more details.
This review will be automatically dismissed once you add the justification section.
c54cd11 to
0b369e5
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4355 +/- ##
==========================================
+ Coverage 69.55% 69.65% +0.09%
==========================================
Files 480 480
Lines 48837 48948 +111
==========================================
+ Hits 33971 34095 +124
+ Misses 12249 12229 -20
- Partials 2617 2624 +7 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Large PR justification has been provided. Thank you!
|
✅ Large PR justification has been provided. The size review has been dismissed and this PR can now proceed with normal review. |
70131b5 to
0d2fbeb
Compare
The auth middleware will populate upstream provider access tokens on the Identity struct, allowing downstream middleware (upstreamswap) to read tokens without coupling to the storage layer. MarshalJSON redacts token values while preserving provider keys, and both nil and empty maps are omitted via omitempty. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduce a narrow UpstreamTokenReader interface that decouples the auth middleware from storage internals. InProcessService.GetAllValidTokens performs a bulk read of all upstream providers for a session, refreshing expired tokens transparently and falling back to expired tokens when refresh fails. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add GetUpstreamTokenReader to MiddlewareRunner so the auth middleware can access the bulk token reader for identity enrichment. The runner stores the reader as a separate field (set alongside the token service) avoiding type assertions. GetAuthenticationMiddleware now accepts variadic TokenValidatorOption for forward-compatible option passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add WithUpstreamTokenReader option to TokenValidator that loads all upstream provider tokens from storage when a tsid claim is present in the JWT. The enrichment happens between claimsToIdentity and context injection, populating Identity.UpstreamTokens for downstream middleware consumption. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the ServiceGetter/GetValidTokens pattern with a direct read from the pre-enriched Identity.UpstreamTokens map. The auth middleware now handles storage reads and token refresh, so upstreamswap becomes a pure reader that looks up the provider token and injects it into the request header. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The upstreamswap middleware no longer calls the token service directly, so GetUpstreamTokenService is dead code. Remove the interface method, its Runner implementation, the mock, tests, and docs reference. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename the type to avoid stutter (upstreamtoken.TokenReader instead of upstreamtoken.UpstreamTokenReader). Method and function names that include "UpstreamTokenReader" are unchanged since they don't stutter. Also remove the unused providerName parameter from the test helper requestWithIdentity in upstreamswap tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace hand-written mockUpstreamTokenReader with go.uber.org/mock generated MockTokenReader, per project convention of never hand-writing mocks. Add //go:generate directive to types.go. Add providerName to log lines in refreshOrFail so operators can identify which upstream provider failed during concurrent refreshes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0d2fbeb to
bd92306
Compare
Summary
upstreamswapmiddleware was coupled to the token storage layer via aServiceGetterpattern, making it responsible for session lookup, token refresh, and error classification on every request. This moves all that complexity into the auth middleware where it belongs.tsidclaim is present, the auth middleware now bulk-loads all upstream provider tokens viaGetAllValidTokensand attaches them toIdentity.UpstreamTokens. Theupstreamswapmiddleware becomes a trivial map reader:identity.UpstreamTokens[cfg.ProviderName].main.tsidsession identifier is filtered fromPrincipalInfo.Claimsso it does not leak into webhook payloads or audit logs.GetUpstreamTokenServiceis removed from theMiddlewareRunnerinterface (no remaining callers).Closes #4139
Type of change
Test plan
task test)task lint-fix)Changes
pkg/auth/identity.goUpstreamTokensfield with redactedMarshalJSONpkg/auth/identity_test.gopkg/auth/upstreamtoken/types.goUpstreamTokenReaderinterfacepkg/auth/upstreamtoken/service.goGetAllValidTokens(bulk read with refresh)pkg/auth/upstreamtoken/service_test.gopkg/auth/token.goWithUpstreamTokenReaderoption,loadUpstreamTokens, 503 on storage failurepkg/auth/token_test.goloadUpstreamTokens+ integration tests for full middleware pipelinepkg/auth/context.gotsidfrom externalized claimspkg/auth/utils.goTokenValidatorOptionpkg/auth/middleware.goUpstreamTokenReaderfrom runner into validatorpkg/auth/middleware_test.gopkg/transport/types/transport.goGetUpstreamTokenReader, removeGetUpstreamTokenServicepkg/transport/types/mocks/mock_transport.gopkg/runner/runner.goupstreamTokenReader, remove deadupstreamTokenServicefieldpkg/runner/runner_test.goGetUpstreamTokenReaderpkg/auth/upstreamswap/middleware.goIdentity.UpstreamTokenspkg/auth/upstreamswap/middleware_test.godocs/middleware.mdMiddlewareRunnerinterface blockDoes this introduce a user-facing change?
No. Non-AS deployments are completely unaffected (the enrichment guard is
nil-checked). AS deployments get the same token injection behavior through a different internal path.Special notes for reviewers
GetAuthenticationMiddlewareoutside the runner path pass zero options, soupstreamTokenReaderis nil and the enrichment code is unreachable.upstreamswapdistinguished infrastructure errors (503) from client errors (401). The new architecture preserves this — the auth middleware returns 503 when storage is down, andupstreamswapreturns 401 when the provider token is missing.GetAllValidTokensomits providers whose tokens expired and could not be refreshed, matchingmain's behavior.Generated with Claude Code
Large PR Justification