An agent that logs in as you is not working for you
Every agent integration starts the same way. The agent needs access to a service you already use, and there is no way in, so somebody hands over a credential. It is the only thing that works today, and it is the thing the rest of the design is trying to get away from.
The credential ends up somewhere it was never meant to be. A shell command line. A config file committed by accident. A context window that some other party may log. The failure is not hypothetical and it is not exotic: a prompt-injected agent asked to “check on the build” will read the environment, and the environment has the key in it.
The obvious objection is that this is fine, because the agent is acting on your behalf. It is not. An agent process that has your credential can be steered. Whatever is talking to it — a prompt, a tool result, a web page it was told to summarise — is in a position to ask it to use that credential somewhere you did not mean. The agent’s intent is a property of the model, and it is not a security boundary.
What has to be true for the credential to be safe
Three things, and they are all properties of the protocol rather than of the person using it:
- The agent never holds the long-lived credential. It stays on a machine you control, and the agent talks to a process that holds it. The agent sees a token, not a secret.
- The token is short-lived. Not “revocable, in principle, if you get round to it”. Five minutes. A stolen token is a credential with a half-life measured in seconds, and the blast radius of a leak shrinks to match.
- The token is scoped to one thing. A token minted for one service is rejected by every other service, compared by full equality rather than by prefix. A token that leaks buys an attacker nothing outside the one API it was minted for.
Those three are the whole design. Everything else — the exchange endpoint, the CLI, the SDK — exists to make them possible to keep without a human present.
Why not a refresh token
The obvious design is the one you already know: a long-lived secret exchanged for short-lived access tokens, refreshed periodically. It is the right answer when the client is software you wrote and the user is a person at a keyboard.
The agent is neither. It is a process the user does not supervise and cannot see, executing instructions that arrive from somewhere else. A refresh token in that position is the original long-lived credential with a nicer name, and it will be exfiltrated the same way. The refresh loop is a way of extending a secret’s life, which is only safe if the thing holding it is as trustworthy as the person who would have refused to hand over the original.
So the long-lived key never leaves a machine the user chose, and nothing it can read is enough to impersonate it elsewhere.
The part that is genuinely unsolved
Any identity layer that works this way has a consequence, and it is worth being blunt about it. Issuing a token requires no per-service consent. The user authorised the machine once, and the machine may then present a genuine identity to any service that accepts one, whether or not that service has ever heard of it.
A verified token proves the inbox was checked here. It does not prove the person is your customer, is not on your blocklist, or has agreed to anything. So the service is the only place that can tell the difference, and the only thing standing between “signed in” and “an account exists” is a check the service runs itself, against its own data, returning nothing for an address it does not recognise.
This is a real cost, and it is not free. But it is the cost of not putting a permanent credential in an unsupervised process, and it falls on the party that can actually reason about who their users are. The alternative — a password in the agent’s environment — pushes the same cost onto everyone, invisibly, by making every service an email-accepting machine.
If you are building the receiving side, the account mapping page is the part that matters. It covers the resolver contract and the two ways integrations usually get it wrong.