# Credential Handoff

> Pattern 12 · Trust & transparency · part of Agent Consent Patterns.
> Human page: https://agentconsent.dev/patterns/credential-handoff/

The safest secret is the one the agent never holds. When a task needs a password or a card number, the agent should step aside and hand the exchange to something the user already trusts (a password manager, a passkey, the provider's own payment page), then take back only a scoped credential, never the secret itself.

## Problem

Agents need to do things that require secrets: sign in to a service, pay for a booking, authorise a charge. The tempting shortcut is to let the agent collect the password or card number: "just tell me your login and I'll handle it." That shortcut is a disaster. A secret the agent sees is a secret that can be logged, cached in a prompt, leaked through an injection, or replayed later. It hands the most dangerous thing the user owns to the least trustworthy point in the system.

The deeper problem is that a credential typed to the agent grants far more than the task needs. The user wanted to book one flight, and now the agent holds reusable, unscoped access to their whole account. Every principle elsewhere in this collection (least privilege, scoped grants, legible authority) collapses the moment the agent becomes the thing that holds the keys.

## Solution

Keep the agent out of the credential exchange entirely:

- **Hand off to a trusted holder.** Route sign-in and payment to a password manager, a passkey/WebAuthn flow, or the provider's own payment page: a party the user already trusts with this secret.
- **Make the exclusion legible.** State plainly that the secret goes from the user to that holder and never through the agent. The boundary is the point of the pattern, so it should be visible, not implied.
- **Take back only a scoped credential.** What returns to the agent is a signed-in session, a one-time token, or a confirmation. Never the secret, and scoped to just the task at hand.

> **Live demo** — an interactive Credential Handoff demo runs on the page: https://agentconsent.dev/patterns/credential-handoff/

The demo is the `CredentialHandoff` component from `@agentconsent/react`. Continuing hands the exchange to the password manager; what comes back is a booking-scoped session, never the password.

## Anatomy

> **Anatomy** — a labeled breakdown of the Credential Handoff component's parts is shown on the interactive page: https://agentconsent.dev/patterns/credential-handoff/

## When to use it

- **Any sign-in.** Passwords, passkeys, MFA: delegate to a password manager or the platform authenticator rather than collecting them in the agent's surface.
- **Any payment.** Route to Apple Pay, a card-on-file tokeniser, or the merchant's own checkout, so the agent works from a payment token, not a card number.
- **Any high-value secret.** Recovery codes, API keys, private keys: if leaking it would be serious, the agent should hand off rather than hold it.

## When not to use it

- **Non-secret inputs.** A shipping address or a display name isn't a credential; routing it through a handoff is pointless friction. Reserve this for secrets.
- **Where a scoped grant already exists.** If the user has connected the service through a [Scoped Grant](/patterns/scoped-grant/) or OAuth, the agent already has task-scoped access and shouldn't be prompting for credentials at all.
- **When you can't actually keep the agent out.** Don't dress up a flow that still funnels the secret through the agent as a handoff, that's the anti-pattern wearing the pattern's clothes.

## Real-world examples

- **1Password Secure Agentic Autofill.** When a browser agent hits a login, the flow pauses for the human to approve via biometric on their own device; credentials are then injected into the browser session through an encrypted channel. The agent gets a signed-in session. It never sees the password.
- **ChatGPT agent's takeover mode.** When a task reaches a login or payment form, control flips to the human, who types directly into the page; the agent is excluded from that input. The handoff boundary drawn in the simplest possible way: the secret goes person-to-site, never through the model.
- **Apple Pay and passkeys.** The pre-agent ceiling to aim for: tokenization hands the merchant a one-time token instead of the card number, and a passkey never transmits a shared secret at all, proof that "act on my behalf without holding my secret" scales to billions of users.

*Annotated screenshots of these flows are being collected. Products are credited, annotations follow the site-wide callout conventions, and any screenshot is removed on request. See [About](/about/).*

## Accessibility

- The surface is a `role="group"` labelled by its task title, so assistive tech announces the whole handoff as one named region.
- **The exclusion boundary is a `note`**, so the fact that the agent is excluded is announced to a screen reader. It isn't carried by the visual fence alone.
- The trusted holder's kind is a **text badge** (Password manager, Passkey, Payment, Provider), and what the agent receives is a real list, so the shape of the handoff survives without vision.
- Continue and Cancel are ordinary buttons in a logical order; nothing here traps focus, because the handoff itself moves the user into the trusted holder's own accessible flow.

## Anti-patterns

- **The agent as keyholder.** Letting the agent collect, store, or replay a password or card number. The exact thing this pattern exists to prevent. If the agent can see it, it's already too exposed.
- **A fake handoff.** A flow that looks like a delegation but still routes the secret through the agent (or a server the agent controls). The boundary has to be real, not cosmetic.
- **An invisible boundary.** Handing off correctly but never telling the user the agent is excluded, so they can't tell this apart from a phishing prompt asking for their password. Make the exclusion legible.
- **Unscoped return.** Taking back long-lived, full-account access instead of a task-scoped token squanders the whole point. The agent should get exactly enough to finish, and no more. This is the same [least-privilege](/principles/) bound as everywhere else: the handoff must not hand the agent more than its principal meant to delegate.
- **Typing secrets into chat.** "Paste your password here and I'll log in". The most direct version of the failure, normalising exactly the behaviour that makes users phishable.

## Code

```tsx
import { CredentialHandoff } from "@agentconsent/react";
import "@agentconsent/react/theme.css";

<CredentialHandoff.Root onHandoff={continueInHolder} onCancel={backOut}>
  <CredentialHandoff.Header>
    <CredentialHandoff.Icon>🔑</CredentialHandoff.Icon>
    <CredentialHandoff.Title>Sign in to Delta</CredentialHandoff.Title>
  </CredentialHandoff.Header>

  <CredentialHandoff.Handler kind="password-manager" name="1Password" />

  <CredentialHandoff.Boundary>
    Your password goes straight to 1Password and Delta. The agent never sees it.
  </CredentialHandoff.Boundary>

  <CredentialHandoff.Returns>
    <CredentialHandoff.Return>A booking-scoped session</CredentialHandoff.Return>
    <CredentialHandoff.Return>Expires in 1 hour, no stored password</CredentialHandoff.Return>
  </CredentialHandoff.Returns>

  <CredentialHandoff.Actions>
    <CredentialHandoff.Cancel>Not now</CredentialHandoff.Cancel>
    <CredentialHandoff.Handoff>Continue in 1Password</CredentialHandoff.Handoff>
  </CredentialHandoff.Actions>
</CredentialHandoff.Root>
```

`Boundary` renders a `role="note"` so the exclusion is announced; `Handler`
names who holds the secret; `Returns` lists the scoped credential the agent gets
back. This surface never collects the secret. It's a decision to hand off, not
a form. All parts are unstyled primitives with `data-acp` attributes; skip
`theme.css` and style them yourself, or override the `--acp-*` tokens to
retheme.
