The Refresh Token Nobody Should Give an AI Agent
9 min read Fullmakt Team
- authentication
- credentials
- oauth
- agents
- governance
- traceability
Refresh tokens are one of OAuth’s better ideas, for the problem they were designed to solve: a human authenticates once, gets a short-lived access token for safety, and gets a longer-lived refresh token so they’re not retyping a password every hour. The access token expires quietly in the background; the refresh token quietly gets a new one. Nobody notices, and the security property holds — a stolen access token is only useful for a short window.
That design assumes something specific: a session with edges. A human logs in, works, and eventually stops — closes the laptop, changes networks, goes home. The refresh token bridges gaps in a presence that’s naturally intermittent. An AI agent has no such gaps. It doesn’t step away. Give it a refresh token and you haven’t shortened its window of access — you’ve handed it a credential that quietly renews itself forever, which is the exact standing-access problem scoped, short-lived credentials exist to get rid of.
What a refresh token actually buys you — and who it’s for
The value of the access/refresh split is real, but it’s specifically a value for interactive sessions: it lets the access token be short enough that a leak barely matters, while sparing a present human from re-authenticating on every expiry. Both halves of that trade assume there’s a human in the loop who benefits from not being interrupted, and whose absence for a while is normal, not a bug.
An unattended agent doesn’t get interrupted by a login prompt — it has no UI to show one in, and no one to answer it. So the “spare the human” half of the trade buys nothing. What’s left is just the second half: a long-lived credential, sitting in whatever the agent’s execution environment is, capable of minting fresh access on its own, indefinitely, without anyone re-entering a password or re-approving anything. That’s not a login handshake anymore. It’s a standing key wearing OAuth’s clothing.
Where this goes wrong in practice
None of the following requires a broken implementation. Refresh-token rotation and reuse detection, done correctly, are good engineering. The failure mode is architectural: applying a pattern built for humans to a principal that’s never absent.
- The refresh token becomes the thing that actually needs protecting, and nobody protects it like a credential. Teams are careful about access tokens — short TTL, scoped, logged. The refresh token that mints them quietly sits in an env var, a config file, or an agent’s persistent memory, because it “isn’t really a secret, it’s just how the agent logs back in.” It is exactly a secret. It’s the more dangerous one, because it doesn’t expire on its own schedule — it expires on nobody’s.
- It’s a standing credential with a rotation step bolted on, not removed. Standing keys were already the problem brokered, short-lived issuance was invented to solve. A refresh token that silently re-mints access every hour for months doesn’t remove that problem — it adds one indirection and keeps the outcome: an agent that can still authenticate long after anyone remembers granting it that ability, which is the same gap agent offboarding exists to close, arriving one layer earlier.
- Refresh-token theft looks identical to normal agent behavior. The security control behind refresh tokens — rotate on use, detect reuse of an old one as a signal of theft — assumes one client using one token at a time. In an A2A delegation chain where a task and its authority get handed from agent to agent, a refresh token passed along with it (or copied so a second hop can keep working independently) makes “two clients used the same refresh token” the normal case, not the anomaly. Reuse detection either fires constantly on legitimate delegation and gets disabled, or gets tuned so loose it stops catching real theft. Either way, the one signal refresh tokens were supposed to give you for free is gone.
- A leaked refresh token outlives the incident it caused. In the lethal trifecta — an agent with access to private data, untrusted content, and a path out — the untrusted content doesn’t need to trick the agent into leaking a password. It only needs to get the agent to echo, log, or forward whatever long-lived token is sitting in its context. An access token leaked that way is a bad hour. A refresh token leaked that way is a bad year, quietly re-minting access for as long as nobody notices.
The fix: agents authenticate with a secret they never receive as a session
The alternative isn’t “shorter refresh token lifetimes.” It’s not giving unattended agents a refresh token at all. The client-credentials grant already fits agent authentication precisely: the agent — or, better, the broker sitting in front of it — holds a client secret, and every access token it uses is minted directly from that secret, on demand, short-lived, and never extended by a second credential the agent also has to keep safe. There is no silent renewal chain to steal. There’s a secret and a mint, and the mint checks whether that secret should still work every single time it’s asked, not once at the start of a long-lived session.
That last part is the actual point: a refresh token, once issued, works until it’s stolen, expires on its own long clock, or someone remembers to revoke it. A client-credentials mint checks the state of the principal at the moment of issuance — is it still enabled, still scoped to this collection, still inside a live delegation — every time a new access token is requested. Revocation isn’t “invalidate a refresh token and hope nothing cached it.” It’s “disable the principal,” and the next mint attempt fails, full stop.
The business case: a login handshake with no renewal chain to leak
This is exactly how Fullmakt’s agent surface is built, and the shape isn’t incidental — it’s the same broker-in-the-middle logic that runs through data ownership, traceability, and every other governance gap agentic AI opens up:
- No refresh tokens on the agent surface, by design. Fullmakt’s agent
token endpoint issues 1-hour, scope-pinned access tokens on a
client_credentialsgrant — no refresh grant, no long-lived session to steal. An agent that wants to keep working past the hour asks again, with the same client secret, and gets checked again. - The client secret never leaves the broker’s control plane. The credential capable of minting new access lives where an agent’s execution context — logs, prompt history, tool output — can’t reach it, which closes off the exact leak path a lethal-trifecta scenario depends on: there’s nothing long-lived in the agent’s own context to exfiltrate.
- Revocation is instant and total, not “wait for the refresh token to expire.” Disabling a principal or client stops every future mint immediately. There’s no window where a stolen refresh token keeps working because nobody’s blocklisted it yet — the check happens at issuance, on every request, not once at the top of a session.
- Delegation chains get their own scoped grants, not a shared renewal
secret. Execution tokens (
/mcp) and management tokens (/a2a) are separate surfaces on separate grants; an A2A hop that needs its own authority mints its own token from its own client, instead of inheriting or forwarding a refresh token that would make theft detection meaningless across the chain. - Every mint is the same auditable event. Because there’s one issuance path instead of an initial login plus an indefinite trickle of silent refreshes, the audit log records the same fact every time an agent gets access: which client, which collection, what scope, right now — not “logged in once in March, still going.”
The commercial argument is the one that keeps showing up across this whole space: any place a workaround quietly reintroduces a standing credential is a place the broker should be sitting instead, checking the thing that actually matters — is this agent still allowed to act — at the only moment that’s ever really true, which is right now.
FAQ
Why are refresh tokens risky specifically for AI agents, and not for human users? The risk profile of a refresh token depends on how long the credential it protects realistically needs to live. A human’s session has natural gaps — they log out, step away, their laptop sleeps — so a refresh token just bridges those gaps safely. An unattended agent has no gaps; a refresh token issued to it functions as a permanent credential, which is exactly the kind of standing access short-lived tokens were invented to prevent.
Isn’t a short-lived refresh token still better than a raw API key? It’s better in theory, but in practice it recreates the same failure: the thing that actually grants long-term access sits in the agent’s environment indefinitely, whether it’s called an API key or a refresh token. What matters is whether a single leaked artifact can mint access indefinitely — and a refresh token that renews itself for months does exactly that.
Does refresh-token rotation and reuse detection not solve this? Reuse detection assumes one client uses one token at a time, so a second use of an already-rotated token is a theft signal. Multi-agent delegation breaks that assumption — a task and its authority routinely pass across more than one agent, making concurrent or repeated use of the same token look normal, not anomalous. The signal that makes reuse detection useful stops working exactly where agentic systems need it most.
What should an unattended agent use instead? A client-credentials grant where the client secret is held by a broker, not the agent’s own execution context, and every access token is short-lived, scoped, and reissued from a fresh check of whether the principal is still allowed to act — rather than a refresh token that renews access without re-checking anything.
How does this change what “revoking an agent” means? With a refresh-token chain, revocation means finding and invalidating a credential that may already be cached or copied downstream. With a client-credentials mint gated at the broker, revocation means disabling the principal once — every future access-token request from that principal fails immediately, with nothing else to hunt down.
A login handshake built for a human who steps away and comes back isn’t automatically safe for a principal that never does. The fix isn’t a shorter expiry on the renewal chain — it’s not building a renewal chain into the agent’s hands in the first place, and checking, at the only moment that counts, whether this specific mint should still succeed.