Headless Agents Can't Log In: Device Flow, CIBA and Out-of-Band Approval
8 min read Fullmakt Team
- business-case
- authentication
- login-handshake
- agentic-ai-gone-wrong
- governance
- mcp
- a2a
An AI agent running in a cron job, a CI runner or a cloud sandbox has a problem that a person with a laptop never has: there is nobody to log in. No browser opens. No consent page appears. Yet the agent has to act on a person’s behalf, against systems that expect a human to prove who they are.
Teams solve this in one of three ways. They paste a long-lived token into the agent’s environment. They hand the agent the person’s password. Or they use a handshake designed for devices that cannot show a login page. Only the third is defensible, and it has sharp edges of its own.
This post explains how the OAuth device authorization grant (RFC 8628) and CIBA (OpenID Connect Client-Initiated Backchannel Authentication) let a headless agent ask a human for permission, how attackers abuse them, and how a credential broker keeps the human decision out of band.
What a headless login handshake has to prove
Any login handshake for an agent answers three questions:
- Who is the human whose authority the agent wants to borrow?
- Did that human actually agree to this specific request, right now?
- What exactly was granted, to which agent, for how long?
The classic authorization code flow with PKCE answers these in a browser. A headless agent cannot do that, so the human has to approve on a different device or channel from the one the agent runs on. That separation is the whole point. It is also what attackers target.
Device authorization grant, step by step
The device flow was built for smart TVs and CLI tools. It maps well onto agents.
- The agent calls the authorization server’s device endpoint with its
client_idand requested scopes. - The server returns a
device_code(secret, for the agent), a shortuser_codesuch asWDJB-MJHT, and averification_uri. - The agent shows or sends the
user_codeand URL to the human, for example in a chat message. - The human opens the URL on their own phone or laptop, signs in with their normal MFA, checks the code and approves.
- Meanwhile the agent polls the token endpoint with the
device_codeuntil the server returns a token, orauthorization_pending,slow_downorexpired_token.
The agent never sees the person’s password or their MFA factor. That is a real improvement over pasting credentials into a prompt.
CIBA: the server pushes the question to the human
Device flow makes the human come to the agent’s URL. CIBA reverses it. The
agent (the “consumption device”) sends a backchannel request naming the user,
usually with a login_hint, and the authorization server contacts that user
directly, typically as a push notification on their phone. A binding_message
such as “Approve transfer of 40 EUR to ACME” is shown on the approval screen.
The agent then polls, or receives a ping or push callback, when the decision
lands.
CIBA is a better fit for agentic actions because the approval can be tied to a specific action rather than a generic login. It also moves the risk. The agent is now able to make a phone buzz.
How headless approval goes wrong
Out-of-band approval is only as good as the human reading the prompt. These are the failures we see in agentic deployments.
- Device-code phishing. An attacker starts a device flow, then sends the victim a message: “Enter this code to join the meeting.” The victim signs in and approves. The attacker’s polling client receives the token. Nothing was intercepted, because the victim authorized the attacker’s device on purpose.
- Prompt spam. An agent in a retry loop sends the same approval request fifty times. The human taps approve to make it stop. Approval becomes noise. We cover this in approval fatigue.
- Vague binding messages. “Agent wants access” tells the approver nothing. They cannot tell a harmless read from a payment.
- Over-broad grants. The approval was for one action, but the resulting token is good for every scope, for hours or days.
- A compromised agent asking for approval. A prompt-injected agent can request legitimate-looking approvals. See indirect prompt injection.
- The agent keeps the token. After approval, the token sits in the agent’s memory and logs. Anything that can read them can replay it.
Design rules for headless approval
Whether you use device flow, CIBA or your own approval queue, these rules hold.
- Approve actions, not sessions. The prompt should name the concrete action, target and amount, not “allow access”.
- Make approvals one-shot and short-lived. An approval that can be reused or that lingers is a standing credential in disguise.
- Bind the approval to the exact request. If the agent changes one argument, the old approval must not apply. Hashing the arguments does this.
- Rate-limit and deduplicate prompts. Fifty identical requests should collapse into one question.
- Keep the decision human-only. An agent, even a trusted one, must not be able to approve its own request through any API it can call.
- Keep the resulting credential away from the agent. The agent should present a narrow, short-lived token to a broker, not hold the upstream secret.
- Log the whole chain. Who asked, who approved, what was granted, what was then executed.
The business case: governed delegation instead of shared secrets
Every company that lets agents touch real systems eventually faces a procurement or audit question: “How do you know a human approved this?” The honest answer for most teams today is a shared API key and a Slack message.
That has a measurable cost. Auditors for frameworks such as SOC 2 and ISO 42001 expect evidence of authorization and traceability for automated actions. A leaked long-lived key means an incident and a rotation scramble. Blanket approvals mean either risky automation or a team that is afraid to turn agents on. Governed delegation lets you say yes to agents safely, which is where the productivity gain is.
How Fullmakt handles headless approval
Fullmakt is a credential broker for AI agents. It sits between the agent and the upstream API, so the approval problem is solved at the broker rather than inside each agent.
- Policy decides when a human is needed. A collection policy can mark a
request as
requireApproval. The agent’s call returns a202 Pending Approvalinstead of executing. - Approval is human-only. The decision is made by a person in Fullmakt. The agent-facing A2A management surface explicitly refuses approval decisions, so a prompt-injected agent cannot approve itself.
- Approval is one-shot and bound to the call. Once approved, the retried identical call consumes the approval and runs. A call with different arguments produces a different argument hash and a fresh pending approval. Approvals expire if left unanswered, and a short window applies after approval.
- The agent never holds the upstream secret. Credentials stay in the vault and are injected at execution time, so a leaked agent context does not leak the key.
- Everything is traced. The request, the policy decision, the approval and the outcome are recorded in the audit trail, so you can answer the auditor with evidence instead of a screenshot.
This works the same whether the agent connects as an MCP client or talks to Fullmakt over A2A. Related reading: human-in-the-loop approval, signed task envelopes and login handshakes compared.
Checklist
- [ ] No password or MFA factor ever enters an agent’s context.
- [ ] Approval prompts name the action, target and amount.
- [ ] Approvals are one-shot, expire quickly, and bind to the exact arguments.
- [ ] Duplicate approval requests are collapsed and rate-limited.
- [ ] Agents cannot approve their own requests through any API.
- [ ] The upstream secret stays in a vault or broker, not in the agent.
- [ ] Request, decision, approval and outcome are logged together.
FAQ
What is the OAuth device authorization grant? It is an OAuth flow (RFC 8628) for clients that cannot show a browser. The client gets a short user code, the person approves it on another device, and the client polls until a token is issued.
What is CIBA? Client-Initiated Backchannel Authentication is an OpenID Connect flow where the client asks the authorization server to contact a named user directly, for example by push notification, and approve a request, without a browser redirect.
Is device flow safe for AI agents? It keeps passwords away from the agent, but it is vulnerable to device-code phishing and to over-broad grants. Pair it with narrow scopes, short lifetimes, clear prompts and policy at the point of use.
How can an AI agent ask a human for approval? Through an out-of-band channel the agent cannot control: a push notification, an approval inbox or a broker-managed queue. The decision must be made by a person and bound to the specific action requested.
How does Fullmakt keep agents from approving their own actions? Approval decisions are human-only. The agent surfaces cannot decide approvals, and an approval is one-shot and tied to the exact call it covered.
See how Fullmakt brokers credentials for AI agents or browse the Fullmakt blog.