An AI agent that calls Claude usually carries an Anthropic API key. That key sits in an environment variable or a config file, it works from any machine that copies it, and it lives until somebody remembers to rotate it. Every agent you run is one more place for it to leak.
Anthropic's workload identity federation (WIF) removes the key. A workload proves who it is with a short-lived token from an identity provider Anthropic trusts, and gets a short-lived Claude token back. As of today, the TestifySec Platform is one of those identity providers for the agents enrolled on it.
What the agent already has
cilock enroll agent gives an agent an identity on the platform. A person approves it in a browser with a passkey, picks its lifetime (eight hours by default, seven days at most) and its scope, and the machine stores a refresh credential 0600. The identity is a SPIFFE ID:
spiffe://platform.testifysec.com/tenant/<tenant-id>/agent/<agent-id>
Until now that identity signed attestations. It can now also stand in for an Anthropic credential.
The exchange
- The agent sends its refresh credential to the platform and asks for a token with the audience
https://api.anthropic.com. - The platform answers with an ES256 JWT that lives 300 seconds. Its issuer is
https://platform.testifysec.com/fulcio/oidc, its subject is the agent's SPIFFE ID, and it carries ajti. - The agent hands that JWT to Anthropic's
/v1/oauth/token. Anthropic checks the signature against the platform's published keys, matches your federation rule, and returns an access token for a Console service account, good for about ten minutes.
In our run the exchange returned 200 with scope workspace:developer, and a Messages call with the resulting token returned federated ok. Claude Code 2.1.308 did the same exchange on its own from the standard ANTHROPIC_FEDERATION_RULE_ID family of variables, with no login and no key on the machine.
What it refuses
Two refusals matter more than the success.
Replay. Presenting the same JWT a second time returned401 Authentication failed. Anthropic accepts each jti once, so a token copied out of a log or a process listing is worthless after its first use, and it expires in five minutes anyway.
Another audience. Asking the platform for a token aimed at any other relying party returned 403 audience_not_allowed before the credential was even checked. The platform only mints federation tokens for audiences on its allowlist, so a token meant for Anthropic can't be reused somewhere else, and a compromised agent can't ask the platform for a token to a service you never approved.
The rule is where the security lives
Anthropic decides whether to honour a token by your federation rule. Write it with both halves:
- the subject prefix
spiffe://platform.testifysec.com/tenant/<tenant-id>/agent/*, or one exact agent; - the audience
https://api.anthropic.com.
Leave out the subject and you admit other tenants' agents. Leave out the audience and you admit platform tokens minted for other relying parties.
What it doesn't cover
Federation signs in a Console service account and bills the workspace your rule names. It doesn't sign in to a Claude Pro or Max subscription, so subscription usage can't be federated. And because each token is single-use, a long-running process has to mint a fresh one before each exchange; the guide shows how to refresh the token file so a reader never sees an empty one.
The guide on cilock.dev walks through the Console setup with screenshots, the commands for the agent's machine, and the errors you can hit.