Problem
auth.IdentityFromContext (pkg/auth/context.go:51-54) returns (nil, true) when a caller stored a typed-nil *Identity directly via context.WithValue(ctx, IdentityContextKey{}, (*Identity)(nil)), because the type assertion succeeds with ok==true even though the pointed-to value is nil.
func IdentityFromContext(ctx context.Context) (*Identity, bool) {
identity, ok := ctx.Value(IdentityContextKey{}).(*Identity)
return identity, ok
}
WithIdentity guards against this by returning the original context when identity == nil, so the function as a whole is safe via the canonical entrypoint. The hole opens when callers bypass WithIdentity and call context.WithValue directly (legitimate in tests, or via accident if someone copies the wrong pattern).
Why it matters
The vMCP identity round-trippers introduced by #5323 gate their fallback injection on !ok:
if _, hasIdentity := auth.IdentityFromContext(req.Context()); !hasIdentity && i.fallbackIdentity != nil {
...
}
A typed-nil *Identity on the context would currently:
- Satisfy
hasIdentity == true, so the fallback is suppressed.
- Allow
nil to flow through to UpstreamInjectStrategy (and the other strategies), which would nil-deref on identity.UpstreamTokens[providerName].
WithIdentity is nil-safe so this is latent rather than active today — no in-tree caller currently stores a typed-nil — but the hazard is sharp enough that defending at the read site is worthwhile.
Proposed fix
One-line tighten at the read site:
func IdentityFromContext(ctx context.Context) (*Identity, bool) {
identity, ok := ctx.Value(IdentityContextKey{}).(*Identity)
return identity, ok && identity != nil
}
Update the existing test (pkg/auth/context_test.go:86 TestIdentityContext_ExplicitNilValue), which currently asserts the pre-fix behavior (ok=true, identity=nil), to assert the new defensive behavior (ok=false, identity=nil). Update the doc comment to explicitly state that a typed-nil pointer is treated as "no identity".
References
Problem
auth.IdentityFromContext(pkg/auth/context.go:51-54) returns(nil, true)when a caller stored a typed-nil*Identitydirectly viacontext.WithValue(ctx, IdentityContextKey{}, (*Identity)(nil)), because the type assertion succeeds withok==trueeven though the pointed-to value isnil.WithIdentityguards against this by returning the original context whenidentity == nil, so the function as a whole is safe via the canonical entrypoint. The hole opens when callers bypassWithIdentityand callcontext.WithValuedirectly (legitimate in tests, or via accident if someone copies the wrong pattern).Why it matters
The vMCP identity round-trippers introduced by #5323 gate their fallback injection on
!ok:A typed-nil
*Identityon the context would currently:hasIdentity == true, so the fallback is suppressed.nilto flow through toUpstreamInjectStrategy(and the other strategies), which would nil-deref onidentity.UpstreamTokens[providerName].WithIdentityis nil-safe so this is latent rather than active today — no in-tree caller currently stores a typed-nil — but the hazard is sharp enough that defending at the read site is worthwhile.Proposed fix
One-line tighten at the read site:
Update the existing test (
pkg/auth/context_test.go:86 TestIdentityContext_ExplicitNilValue), which currently asserts the pre-fix behavior (ok=true, identity=nil), to assert the new defensive behavior (ok=false, identity=nil). Update the doc comment to explicitly state that a typed-nil pointer is treated as "no identity".References
!okgate is what surfaces the latent hazard.