stacklok / stacklok/toolhive

Outgoing strategies differ on no-subject-provider fallback (xaa hard-fails, token_exchange and aws_sts fall back) and it is undocumented

Open Beginner friendly
#6,177 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Go
Stars
2.2k
Forks
300
Avg merge
1d 15h
Merged PRs (30d)
184

Description

Problem

Three outgoing strategies behave differently when no subject provider is configured, and the
difference is not documented anywhere. Two fall back to the presented token; one hard-fails.

xaa — hard-fails. pkg/vmcp/auth/strategies/xaa.go:113-119:

idToken := identity.UpstreamIDTokens[config.subjectProviderName]
if idToken == "" {
    return fmt.Errorf("provider %q: %w", config.subjectProviderName, authtypes.ErrUpstreamTokenNotFound)
}

No fallback. The stored upstream ID token is populated only by a session-keyed load, so xaa
cannot work from an inbound token alone.

aws_sts — falls back. pkg/vmcp/auth/strategies/aws_sts.go:135-147:

if cfg.SubjectProviderName != "" {
    bearerToken = identity.UpstreamTokens[cfg.SubjectProviderName]
    ...
} else {
    ...
    slog.Debug("aws_sts: SubjectProviderName empty, falling back to identity.Token")
}

token_exchange — falls back. It never reads a subject provider, so it exchanges the
presented token unconditionally.

Why it matters

Two consequences, and the second is the reason to write this down.

Operationally, an operator configuring a deployment where no upstream provider is pinned
gets a working outbound path on two strategies and a runtime failure on the third, with nothing
in the configuration surface signalling which. The xaa failure appears as
ErrUpstreamTokenNotFound naming a provider the operator may not have configured on purpose.

For anything acting on a foreign-issuer token — an agent presenting a delegated token rather
than a browser session — xaa is structurally unavailable, because the stored ID token it needs
is only ever populated by a login flow. token_exchange and aws_sts are the paths that can
work at all. That is worth stating in the strategy documentation rather than being discovered by
whoever tries xaa first.

One related note on aws_sts: its fallback path uses identity.Token, the raw presented token,
and the same strategy reads inbound claims to select which IAM role to assume. So it is the one
strategy where inbound claims are authority-bearing and the presented token is accepted
directly. That combination deserves a sentence in its own docs.

Suggested shape

Document the fallback behaviour per strategy — a short table in the outgoing-auth docs stating,
for each, whether a subject provider is required and what happens when it is absent. Optionally
validate at configuration time so that xaa without a subject provider fails at startup rather
than on the first call.

No behaviour change is being proposed. The strategies may well be right to differ; the point is
that nothing says they do.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read the outgoing-auth documentation and compare it with pkg/vmcp/auth/strategies/xaa.go:113-119 and aws_sts.go:135-147; also review the token_exchange strategy entry point. Done means the documentation clearly states whether each strategy requires a subject provider and what happens when it is absent, including aws_sts fallback details.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
authentication, documentation
Issue type
Documentation
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.