You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Server edition: agent-token names are a global namespace and AuthContext() drops UserID, so a tenant's token carries no user identity #1168
Two related gaps in how the server edition's per-user agent tokens map onto the core token store. Neither leaks a credential on its own, but together they mean a tenant's token has no user identity downstream and the token name space is shared across all users.
1. Global name namespace
internal/storage/agent_tokens.go keys the name→hash mapping in a single AgentTokenNamesBucket by the bare name, with no user scoping. GetAgentTokenByName(name) reads that global bucket ignoring UserID.
Consequences in the server edition, where every user creates tokens at POST /api/v1/user/tokens (internal/serveredition/api/user_handlers.go:707):
Two users cannot both have a token called ci. The second gets a conflict.
The 403-vs-409 split is an oracle: revokeUserToken (user_handlers.go:~752) returns 403 "Cannot revoke another user's token" when the name exists but belongs to someone else, and 404 when it does not exist — so any user can probe other users' token names.
2. AuthContext() drops UserID
internal/auth/agent_token.go:54-66 copies Type, AgentName, TokenPrefix, AllowedServers, Permissions and ProfilePin — but not UserID, which the server edition stores on the token (user_handlers.go:705-712). Both middlewares build the context through this single constructor, so once a request is authenticated by an agent token there is no way to tell which tenant it belongs to.
That matters wherever a downstream surface wants a per-user answer rather than a per-token one: per-user credential resolution, per-user activity attribution, and any future per-tenant filtering. It is also why the enumeration gap in #1166 cannot currently be fixed by user rather than by token scope.
Note the constructor's own doc comment says it exists "so no auth path can silently drop a field" — UserID is exactly that failure, one field later.
Suggested direction
Scope the name bucket by user (or store a composite key) and make GetAgentTokenByName take the owner, so names are per-user and the revoke path stops answering differently for another user's token.
Carry UserID through AgentToken.AuthContext(), and add it to the constructor's field-parity test if one exists.
Both are storage/format changes, so they want a migration plan for existing tokens.
Found during the #1161 residual sweep (#1165); out of scope there.
Activity
fix(serveredition): close the name-collision bypass and stop agent to…
fix(security): re-derive JWT admin role and scope agent tokens per ow…
docs(security): add the release advisory for the credential-exposure …
docs(security): drop the server-edition section from the release advi…