Skip to content

[auth] IdentityFromContext returns (nil, true) for typed-nil *Identity #5337

@tgrunnagle

Description

@tgrunnagle

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:

  1. Satisfy hasIdentity == true, so the fallback is suppressed.
  2. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    authenticationbugSomething isn't workinggoPull requests that update go code

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions