Device Authorization Grant
The default for headless and command-line agents. Read alongside RFC 10027.
Flows / Human present
For agents with no browser of their own: show a code, let the person approve on another device, and poll.
A coding agent in a remote container, a CLI over SSH, a job in a sandbox: none of them can open a browser window or receive a redirect. The device authorization grant splits the flow across two devices. The agent displays a short code and a URL; the user opens that URL on a phone or laptop, signs in, and types the code. Meanwhile the agent polls the token endpoint until the answer arrives.
It is simple and widely deployed, which is why it is the first thing people reach for with headless agents. It is also the flow most abused for phishing, because the person approving has no technical proof that the code on their screen came from the session they think it did.
10 steps between 4 parties. Each step shows the message and who holds which credential afterward. Use ←→ or select any arrow.
Drag to see every party and step. Tap a step to jump to its explanation.
No user is involved yet. The agent identifies itself and says what it wants.
POST /device_authorization HTTP/1.1
Host: auth.example
Content-Type: application/x-www-form-urlencoded
client_id=cli-agent
&scope=repo.readThe device_code is a long secret the agent keeps and uses to poll. The user_code is short enough to type. verification_uri_complete embeds the user code so it can be rendered as a QR code.
{
"device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS",
"user_code": "WDJB-MJHT",
"verification_uri": "https://auth.example/device",
"verification_uri_complete": "https://auth.example/device?user_code=WDJB-MJHT",
"expires_in": 600,
"interval": 5
}Who holds what after this step
Parties not listed hold none of this flow’s credentials at this point.
In a terminal, a chat message, or a QR code. This hop is the weak point: nothing technical ties the code the user sees to the agent they intend to authorize. Whatever channel carries it is the channel an attacker will imitate.
To sign in, open https://auth.example/device
and enter the code WDJB-MJHTWho holds what after this step
Parties not listed hold none of this flow’s credentials at this point.
The user authenticates in a real browser, with whatever factors the server requires, then enters the code. The server looks up the pending request and shows what is being asked for.
Who holds what after this step
Parties not listed hold none of this flow’s credentials at this point.
While the user is busy, the agent asks the token endpoint whether a decision has been made, no more often than interval seconds. The resource parameter names the API the token is for. RFC 8707 defines it on token requests for every grant type; RFC 8628 gives the device authorization request only client_id and scope.
POST /token HTTP/1.1
Host: auth.example
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:device_code
&device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS
&client_id=cli-agent
&resource=https://git.example/apiWho holds what after this step
Parties not listed hold none of this flow’s credentials at this point.
The expected answer until the user finishes. Other answers: slow_down (back off), access_denied (the user said no), expired_token (start again).
{ "error": "authorization_pending" }Who holds what after this step
Parties not listed hold none of this flow’s credentials at this point.
A good approval screen names the client, says where the request came from, and lists the scopes in plain language. It is the user’s only chance to notice that they did not start this.
Who holds what after this step
Parties not listed hold none of this flow’s credentials at this point.
Same request as before.
Who holds what after this step
Parties not listed hold none of this flow’s credentials at this point.
From here on the agent is in the same position as after an authorization code flow: a short-lived access token and usually a refresh token.
Who holds what after this step
Parties not listed hold none of this flow’s credentials at this point.
The API sees an ordinary access token. It cannot tell that the approval happened on a different device from the one making the call.
Who holds what after this step
Parties not listed hold none of this flow’s credentials at this point.
slow_down error. Respect interval.Both decouple the device that needs a token from the device where the person approves. In the device grant the user carries a code from the agent to the server. In CIBA the server reaches out to a user it already knows. Use the device grant to sign in someone who is sitting there; use CIBA to ask a known user for approval when they are somewhere else.
See the full tracker for status and links.
What the flow implements, shows in a step, or must support. These decide its maturity rating.
The default for headless and command-line agents. Read alongside RFC 10027.
The foundation under MCP authorization and nearly every agent delegation scheme.
Covers the consent-phishing risks of device codes and backchannel prompts, which is how agents usually reach humans. New in 2026.
Required by MCP. An agent talks to many servers; this is what stops a token for one being replayed at another.
The default way agents present tokens, and the thing proof-of-possession schemes are trying to replace.
Related reading. Not what the flow implements, and not used in a step.
The baseline MCP profiles. Worth knowing that the normative base of most agent authorization is still, formally, a draft.
The baseline. A person signs in, approves specific scopes, and the agent receives a token that says so.
The agent asks the authorization server to go and find the user. Approval arrives as a push notification, not a redirect.
How an MCP client that has never seen a server before discovers where to sign in, identifies itself, and gets a token good for that server alone.