Authenticating AI Agents: API Keys vs OAuth Device Flow vs Scoped Tokens
A practical breakdown of the three credential models for non-human callers — static API keys, the OAuth 2.0 device authorization grant, and short-lived scoped tokens — and when each one actually fits.
Your agent needs to hit the GitHub API, your internal deploy service, and a customer’s Postgres. Nobody is at a keyboard. Whatever credential you hand it has to keep working through a 3am retry, survive a rotation, and leave a trail that tells you which run did what.
The three options you actually choose between — a static API key, the OAuth 2.0 device authorization grant (RFC 8628), and short-lived scoped tokens — are not three flavors of the same idea. Each answers a different question about who is present when the credential is minted and who is accountable when it is used. Picking the wrong one does not fail loudly on day one. It fails on the day you need to revoke something, and discover the credential is copy-pasted into four CI configs and a developer’s shell profile.
What each mechanism actually assumes
A static API key assumes the secret is the identity. There is no user, no session, no expiry. Whoever holds the string is the caller. That is genuinely fine for a narrow class of cases: a single-tenant background job, an internal service where the blast radius is already bounded, a local dev loop. It is not fine the moment the key can act on behalf of more than one principal, because the token carries no answer to “on whose behalf?” Your audit log records the key, and the key is the same for every run.
The operational tax is rotation. A static key has no natural refresh point, so rotating it means finding every place it was copied to. Agents make this worse than normal service-to-service calls, because agent frameworks encourage stuffing credentials into environment files, MCP server configs, and tool definitions that get shared across machines.
The OAuth 2.0 device authorization grant assumes a human is reachable, just not on this device. That is the whole point of RFC 8628: the client is input-constrained (a TV, a CLI, a headless box), so it displays a code, the human opens a browser somewhere else, approves, and the device polls the token endpoint until it gets an access token and refresh token.
The assumption that matters is reachable human. Device flow is not a machine-to-machine grant. It is a delegation grant with a deferred consent screen. If your agent runs on a schedule with nobody watching, device flow only works because a human ran it once, weeks ago, and the refresh token has been quietly rolling over ever since. That is a legitimate design — it is roughly how CLI tools like gh auth login behave — but be clear about what you built: an agent that inherits a specific person’s authority, indefinitely, with that person’s name on every action in the audit log.
Short-lived scoped tokens assume the agent has its own identity, and that the identity is separable from the credential. The credential is minted on demand, narrow in scope, and expires in minutes. In OAuth terms this is the client credentials grant (RFC 6749 §4.4) when the agent acts as itself, or token exchange (RFC 8693) when it needs to act on behalf of a user for one specific call. In cloud terms it is workload identity federation — the agent proves what it is via a platform-issued attestation and trades that for a scoped access token. SPIFFE/SPIRE is the vendor-neutral version of the same shape.
This is the model that fits non-human callers, because it is the only one where “the agent” and “the agent’s current credential” are different objects. You can revoke one without hunting for the other.
Pick by who is present, then by what can be scoped
Run the decision in two passes.
First pass — who is present at authorization time?
- A human, at the moment of the call. Use a normal authorization code flow with PKCE in the surrounding app and pass a per-request token down to the agent. Do not promote it to a stored credential.
- A human once, then never again. Device flow, with an explicit refresh-token lifetime and a re-consent interval. Write down what happens when that person leaves.
- Nobody, ever. The agent is a workload. Give it a workload identity and mint scoped tokens per task. Client credentials or federation, not a key file.
Second pass — can the authority be narrowed to the task?
This is where most implementations stop early, and it is the part that actually limits damage. A token scoped to repo is not scoped. A token scoped to one repository, write access to one branch, valid for eight minutes, is scoped. GitHub App installation tokens are a good reference implementation: they expire in an hour and can be restricted to specific repositories and permissions at request time. Cloud STS tokens support similar narrowing through session policies and duration limits.
For agents specifically, add two properties that human-facing OAuth rarely needs:
- Per-run distinctness. Each agent run should be traceable to its own token, so a compromised or misbehaving run is bounded and identifiable. A single long-lived token shared across runs collapses your audit log into one row.
- Sender constraint. Bearer tokens are replayable by anyone who obtains them, and agents leak them through logs, traces, and prompt context more readily than normal services do. DPoP (RFC 9449) or mTLS-bound tokens (RFC 8705) bind the token to a key the holder must prove possession of, so a stolen token alone is not enough.
The Model Context Protocol authorization spec pushed the ecosystem toward this shape — MCP servers act as OAuth resource servers, and clients are expected to obtain tokens with an explicit audience rather than accept a shared secret. If you are building agent tooling now, matching that pattern costs you little and keeps you compatible with where the tooling is heading.
OpenCode
An open-source terminal coding agent. Useful reference material if you are designing agent auth: its provider and MCP credential handling is readable, so you can see exactly where tokens are stored, scoped, and passed to tools rather than guessing at a closed implementation.
Open source; bring your own model API key
Affiliate link · We earn a commission at no cost to you.
What to do if you already shipped API keys
You probably did, because it is what every SDK quickstart hands you. The migration does not have to be a rewrite.
Start by putting a token broker between your agents and the static keys. The agent authenticates to the broker with a workload identity, the broker holds the upstream long-lived credential, and it issues a short-lived, narrowly scoped token per task. Nothing upstream changes. You get expiry, per-run attribution, and a single place to revoke — which is most of the benefit of the full model.
Then fix the audit gap. Add a run identifier that travels with every call the agent makes, and make sure it lands in the same place your upstream logs land. If you cannot answer “which agent run deleted this record?” from logs alone, the credential design is not finished, regardless of which grant type you chose.
Last, set an expiry on anything that currently has none. A key that never expires is a key nobody ever tests the rotation path for, and rotation paths that have never been exercised do not work when you need them at 3am.
FAQ
Can I just use the OAuth client credentials grant for everything?
Is the device flow insecure for agents?
Do short-lived tokens break long-running agent tasks?
Tools used in this review
Some links above are affiliate links. We may earn a commission if you sign up. See our disclosure for details.
Related reading
2026-08-13
Error Messages as an Agent Interface: Designing API Failures an Agent Can Recover From
An AI agent only sees what your error body contains. A field-by-field guide to API errors agents can act on — stable codes, explicit retryable flags, wait hints, fix examples — and the error shapes that trap agents in retry loops.
2026-08-12
Spec-Driven Development With AI Agents: Writing a Spec an Agent Can Actually Execute
How to structure a spec so a coding agent can run it end to end without babysitting: ground truth files, an interface contract, one acceptance command, and explicit out-of-bounds rules.
2026-05-26
Macchiato Day 2: Live Token Metrics and Parallel AI Terminals Reviewed
Macchiato's day-2 build adds a live token/cost sidebar and keyboard shortcuts for swapping between Claude Code and OpenCode in one terminal. Here's what shipped and what it means.
2026-05-21
Agnt Review: An Open-Source CLI for Running Public and MIT-Licensed AI Agents
Agnt is a free, open-source CLI for running any public or MIT-licensed AI agent from one interface. What it does, how it compares to other agent runners, and whether to install it.
2026-05-21
How to Measure AI Coding Agents Beyond Lines of Code and PR Acceptance Rates
Lines of code and PR acceptance rates look like productivity signals but reward verbosity and rubber-stamping. Here is what engineering managers should track instead when adopting Copilot, Cursor, and Claude Code.
Get the best tools, weekly
One email every Friday. No spam, unsubscribe anytime.