Add upstreamCredentialScope opt-in and enforce its issuer trust - #6458
Open
lorenzozanee wants to merge 1 commit into
Open
Add upstreamCredentialScope opt-in and enforce its issuer trust#6458lorenzozanee wants to merge 1 commit into
lorenzozanee wants to merge 1 commit into
Conversation
Planned platform-user credential work needs deployments to opt in to trusting a durable platform-user identity claim, and needs the token carrying that claim validated as issued by the embedded auth server that owns the credential namespace. Neither the configuration seam nor the enforcement exists today. Add upstreamCredentialScope (session | platformUser) to EmbeddedAuthServerConfig with session as the permanent default, and propagate it through the shared authserver RunConfig consumed by the proxy runner and vMCP. Only a genuinely absent value maps to session; unknown non-empty values are rejected. Introduce a separate UserTokenReader seam and enforce, only when one is supplied, that the configured issuer is non-empty and exactly equals the embedded auth server issuer carried on TokenValidatorConfig.AuthServerIssuer. Gate the matching operator and vMCP checks on platformUser, call ValidateAuthServerIntegration from thv vmcp serve after side-loading the auth server config, and reject runtime activation of platformUser as unsupported until platform-user storage lands. Existing deployments keep identical session behavior: the session TokenReader wiring, the tsid claim, and every session-mode validation result are unchanged. Fixes stacklok#6403 Signed-off-by: lorenzozanee <wyz0707@proton.me>
lorenzozanee
requested review from
ChrisJBurns,
JAORMX,
amirejaz,
aponcedeleonch,
blkt,
jerm-dro,
jhrozek,
rdimitrov,
reyortiz3 and
tgrunnagle
as code owners
August 29, 2026 01:52
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
Planned work on non-interactive agent credentials will read a stored upstream OAuth credential by a durable platform-user identity claim. That claim is only trustworthy when the token carrying it was validated as issued by the embedded auth server that owns the credential namespace, but today there is no way to opt a deployment into that trust model and no check that enforces it.
This PR prepares that ground without changing any existing behavior:
upstreamCredentialScope(session|platformUser) toEmbeddedAuthServerConfig, withsessionas the permanent kubebuilder default, and propagate it through the sharedauthserver.RunConfigconsumed by the proxy runner and vMCP. Only a genuinely absent value maps tosession; unknown non-empty values are rejected rather than reinterpreted as the default.UserTokenReaderinterface and aWithUserTokenReadervalidator option as a separate seam from the existing session-basedTokenReader(which stays wired exactly as it is, keyed ontsid).TokenValidatorConfig(AuthServerIssuer) and enforce, only when aUserTokenReaderis supplied, that the configured issuer is non-empty and exactly equals it — no trimming, no trailing-slash tolerance, no fallback to the session reader.validateOIDCConfigForEmbeddedAuthServer) and in vMCP'sValidateAuthServerIntegrationon theplatformUserscope, and call the latter fromthv vmcp serveafter side-loading the auth server config.platformUserwith an explicit unsupported-capability error until platform-user storage lands; it never silently starts in session mode and never installs a no-op reader.Fixes #6403
Type of change
Test plan
task test)task test-e2e)task lint-fix)New unit tests cover: omitted-scope ≡ explicit
session; unknown values rejected; CRD-to-RunConfig propagation on both MCPServer and VirtualMCPServer paths; the generated CRD schema (enum +default: session); the constructor trust precondition (matching/empty/mismatched/trailing-slash issuers, and legacy behavior without aUserTokenReader); and dormantplatformUseractivation failing with the unsupported-capability error at reconcile (with delegate clients) and at pod start (without).API Compatibility
v1beta1API, OR theapi-break-allowedlabel is applied and the migration guidance is described above.The operator API change is purely additive: a new optional
upstreamCredentialScopefield with a permanentsessiondefault. Existing objects — including ones created before this field existed — behave identically on upgrade, and existing session-mode validation results are unchanged.Changes
cmd/thv-operator/api/v1beta1/mcpexternalauthconfig_types.goupstreamCredentialScopefield + enum/default markers;EffectiveUpstreamCredentialScopehelperpkg/authserver/config.goRunConfig.UpstreamCredentialScope, effective-default resolution, validationcmd/thv-operator/pkg/controllerutil/authserver.gopkg/auth/token.goTokenValidatorConfig.AuthServerIssuer,WithUserTokenReader, construction-time issuer preconditionpkg/auth/upstreamtoken/types.goUserTokenReaderinterface (+ generated mock)pkg/vmcp/config/validator.goValidateAuthServerIntegrationpkg/vmcp/cli/serve.goValidateAuthServerIntegrationon the side-loaded auth server configdeploy/charts/operator-crds/...,docs/...Does this introduce a user-facing change?
No for existing deployments: the default
sessionscope keeps the current session-based token wiring, and none of the new checks run for it.platformUseris a new explicit opt-in that currently validates its trust configuration and then fails with an explicit unsupported-capability error until platform-user storage is implemented.Special notes for reviewers
platformUserlives inauthserver.RunConfig.Validate()rather thanpkg/runner/runner.go: both the proxy runner andthv vmcp serveconstruct the embedded auth server throughNewEmbeddedAuthServer, which validates the RunConfig first, so one check covers both entry points.thv vmcp servenow callsValidateAuthServerIntegration, which the operator already applies at reconcile time. Hand-written vMCP configs that violate pre-existing rules (e.g. issuer consistency between the auth server and incoming auth) will now fail at startup; operator-managed configs already passed these checks. The newly introduced issuer requirements are gated onplatformUseronly.yaml.Unmarshal/json.Unmarshaland would silently drop the field. Today this is unreachable becauseplatformUsercannot activate anywhere; when storage support wires a realUserTokenReader, that issue needs an explicit required-capability/version check so an explicitly configuredplatformUsernever silently degrades to session mode.