Skip to content

RefreshAgentToken mints credentials with NULL entitled keys and no JTI hash return #1337

Description

@ptone

Problem

RefreshAgentToken (agenttoken.go:256) is an exported method on AgentTokenService that validates an existing agent token and issues a new one. It has zero production callers today.

It mints a credential row with NULL entitled_secret_keys, creating a trap for any future caller:

  1. No entitled keys recorded. GenerateAgentTokenWithExpiry (the underlying generator) builds the AgentCredential struct without EntitledSecretKeys (agenttoken.go:323-329), and CreateAgentCredential omits SetEntitledSecretKeys (credential_store.go:55-60). The column stays NULL.

  2. No JTI hash returned. GenerateAgentTokenWithExpiry returns (string, time.Time, error) — token and expiry, but no JTI hash. Compare GenerateAgentToken (agenttoken.go:162) which returns (string, string, error) — token and JTI hash. A future caller of RefreshAgentToken cannot record entitled keys even knowing they should, because they lack the hash needed to update the credential row. They will have a working token, a credential that looks fine, and a wall. They will not conclude "change this signature" — they will conclude recording happens elsewhere or is not needed here.

This is not a live defect today because nothing reads entitled_secret_keys yet. PR #1332 added the schema and PRs #1333/#1336 wire the recording. The fetch endpoint (PR 4, not yet dispatched) is the moment this becomes dangerous — it will check the stored entitled list, and a NULL list from a RefreshAgentToken-minted credential would fail closed (by design), but the trap is that the function looks correct and the failure mode is invisible until an operator reports it.

Resolution

Delete RefreshAgentToken and GenerateAgentTokenWithExpiry. Both are dead code:

  • RefreshAgentToken: zero callers outside tests (searched cmd/, internal/, all pkg/ packages)
  • GenerateAgentTokenWithExpiry: only caller is RefreshAgentToken and one test

Also delete the CredentialChecker interface and SetCredentialChecker, which exist solely for RefreshAgentToken's revocation check. The auth middleware (auth.go:157-168) performs the same revocation check independently.

Clean up the SetCredentialChecker wiring in server.go:1063 and the comment at server.go:1060.

The live token refresh path is handleAgentTokenRefresh (handlers_agents_core.go:2736), which uses GenerateAgentToken (with JTI hash return) and already records entitled keys as of PR #1336.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:authArea: authtype:bugDefect / incorrect behavior

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions