Add upstream_inject strategy type and auth server validation#4349
Add upstream_inject strategy type and auth server validation#4349
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4349 +/- ##
==========================================
+ Coverage 68.45% 69.41% +0.96%
==========================================
Files 479 479
Lines 48642 48639 -3
==========================================
+ Hits 33300 33765 +465
+ Misses 12373 12277 -96
+ Partials 2969 2597 -372 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
pkg/vmcp/config/validator.go
Outdated
| if strategy.Type != authtypes.StrategyTypeTokenExchange { | ||
| continue | ||
| } | ||
| if rc.Issuer == cfg.IncomingAuth.OIDC.Issuer { |
There was a problem hiding this comment.
I'm a little confused about this check - rc.Issuer == cfg.IncomingAuth.OIDC.Issuer when vMCP is pointing to the embedded AS as its authn/z source, right? What's that have to do with token_exchange? Would you be able to add a comment about why upstream_inject is preferred here?
There was a problem hiding this comment.
That function was in an earlier revision but got dropped during the rebase, sorry for the confusing patches. The idea behind it was: if the embedded AS is already managing the upstream OAuth flow, using token_exchange against the same issuer is redundant.
I did expand the doc comment on validateAuthServerIncomingAuthConsistency (the issuer/audience consistency check nearby) to clarify that it's strategy-agnostic.
There was a problem hiding this comment.
(You might have reviewed the push before the rehoming from scaffolding-2 to main perhaps)
afc069c to
0bd1c80
Compare
Add StrategyTypeUpstreamInject and UpstreamInjectConfig to backend auth strategy types, enabling vMCP backends to inject upstream IDP tokens obtained by the embedded authorization server. Add ValidateAuthServerIntegration with cross-cutting validation rules between the embedded auth server config and backend auth strategies: - V-01: upstream_inject requires auth server - V-02: upstream_inject providerName must exist in upstreams - V-04: auth server issuer must match incomingAuth OIDC issuer - V-05: auth server requires issuer and at least one upstream - V-07: incomingAuth audience must be in allowed audiences - V-09: auth server requires OIDC incoming auth - V-10: warn on duplicate upstream_inject provider names - V-11: warn when token_exchange targets the auth server - V-13: AllowedAudiences required for MCP compliance Refs: #4142
d447384 to
39a9a1e
Compare
|
I apologize @tgrunnagle I shouldn't have opened the PR before I went through it carefully myself - that was unfiltered CC output that I just gave a cursory look to. I reviewed the patch manually this time and re-did it. I'm marking it ready for review. |
Remove extra blank line and trailing newline that caused the gci linter to fail in CI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
tgrunnagle
left a comment
There was a problem hiding this comment.
Just a couple non-blocking comments
pkg/vmcp/config/validator.go
Outdated
| } | ||
| pn := authserver.ResolveUpstreamName(strategy.UpstreamInject.ProviderName) | ||
| if first, ok := seen[pn]; ok { | ||
| slog.Warn("multiple upstream_inject backends reference the same provider; likely a copy-paste error", |
There was a problem hiding this comment.
Question: why is this a warning case? Two backends requiring the same upstream token provider seems like a legit scenario.
There was a problem hiding this comment.
hmm actually you're right, I was thinking it would more probably be copy-paste but we'd actually be confusing in a legitimate scenario
| // validateAuthServerRequiresOIDC checks that when the auth server is configured, | ||
| // incomingAuth is OIDC. The AS issues tokens that the OIDC middleware | ||
| // validates; without OIDC incoming auth the entire OAuth flow is pointless. | ||
| func validateAuthServerRequiresOIDC(cfg *Config) error { |
There was a problem hiding this comment.
nit: this code is pretty similar to hasAuthServerWithOIDCIncoming, maybe could be refactored a bit
There was a problem hiding this comment.
not a nit, good call, added
pkg/vmcp/config/validator.go
Outdated
| return result | ||
| } | ||
| if cfg.OutgoingAuth.Default != nil { | ||
| result["(default)"] = cfg.OutgoingAuth.Default |
There was a problem hiding this comment.
nit: does it matter if this is "default" vs with ()? I'm not sure name is used except for logging, but maybe worth being consistent.
There was a problem hiding this comment.
The purpose was to avoid a key collision - this is a map key in a map that also stores user-defined backend names, so using just "default" could be problematic if someone named their backend default as well.. But "(default)" does look confusingly similar to authserver.DefaultUpstreamName.
I extracted the string to defaultStrategyKey = "<default-strategy>", hope this is clearer.
jerm-dro
left a comment
There was a problem hiding this comment.
Clean design — keeping (Config, RunConfig) as separate parameters is the right call. Validation rules are thorough and well-tested. The slices.Contains cleanup is a nice bonus.
Expand the doc comment on validateAuthServerIncomingAuthConsistency to explain that it applies regardless of outgoing backend strategy (upstream_inject, token_exchange, etc.) and why the issuer/audience match matters: the OIDC middleware rejects every token whose iss claim differs from what it expects. Addresses PR review feedback from @tgrunnagle. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Multiple backends referencing the same upstream provider is a valid and expected configuration (e.g., two GitHub-related backends both needing the "github" upstream token). The "likely a copy-paste error" warning was a false positive for legitimate configs. Addresses PR review feedback from @tgrunnagle. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract the synthetic map key for the default outgoing auth strategy into a named constant. Use "<default-strategy>" instead of "(default)" to clearly distinguish it from authserver.DefaultUpstreamName and prevent key collisions with user-defined backend names. Addresses PR review feedback from @tgrunnagle. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Both validateAuthServerRequiresOIDC and hasAuthServerWithOIDCIncoming checked the same three-field condition. Extract a shared hasOIDCIncoming predicate so the logic lives in one place. Addresses PR review feedback from @tgrunnagle. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
the additional commits address each of @tgrunnagle comments, patch per comment for easier review. the original patch is unchanged. |
Summary
upstream_injectstrategy type.StrategyTypeUpstreamInjectandUpstreamInjectConfigto backend auth strategy types.ValidateAuthServerIntegration(cfg *Config, rc *authserver.RunConfig)with cross-cutting validation rules V-01 through V-07 (issuer consistency, audience matching, provider existence, etc.).*Configand*authserver.RunConfigas separate arguments — noRuntimeConfigwrapper type needed.Fixes #4142
Fixes #4144
Type of change
Test plan
task test)task lint-fix)Special notes for reviewers
Draft — stacked on #4348. Review the incremental diff only.
The validation functions accept
(*Config, *RunConfig)as separate parameters rather than a combinedRuntimeConfigtype. This keeps the config package free of auth server dependencies and makes the function signatures explicit about what they inspect.Generated with Claude Code