Flows / Human in the loop

Step-up and transaction-specific consent

Start with a weak token. When the agent reaches something sensitive, the API says so and the user is asked for exactly that.

Based onRFC 9470published September 2023Guide reviewedNovember 2026

Sources and review details
This page
Implementation guideAn explanation, not a specification
Primary specification
Step-Up Authentication ChallengeRFC · September 2023
All 5 specifications
5 published
Flow maturity
Stable standardsWhat the rating means
Human involvement
Human present
Review
Checked against sources in November 2026How it was checked

Asking for every permission up front is how agents end up holding tokens that can do far more than the task needs. The alternative is to begin with the minimum and let the resource server say when more is required.

Two standards make this workable. The step-up challenge lets an API answer “this token is fine, but not for this: come back with a stronger authentication or a broader scope.” Rich Authorization Requests let the follow-up request describe one specific action in structured detail instead of a coarse scope string. Together they give a pattern where routine work flows without interruption and a human is pulled in only for the moments that matter, to approve something concrete.

Step by step

12 steps between 4 parties. Each step shows the message and who holds which credential afterward. Use ←→ or select any arrow.

Sequence diagram4 participants, 12 steps
  • Direct call
  • Via the user’s browser or device
  • Response
  • Local action

Drag to see every party and step. Tap a step to jump to its explanation.

1POST /transfers (basic token)2Policy: needs more3401 / 403 with challenge4Pause and explain5Authorizationrequest with details6Re-authenticate; show transfer7Approve this transfer8Redirect with code9Token request10Token withacr + details11Retry POST /transfers12201 Created

  1. 1Agent to Banking APIDirect call

    The agent tries a sensitive action with its everyday token

    The agent has a token with accounts.read, obtained when the user first connected the bank. It now attempts a transfer.

    Who holds what after this step

    Agent
    newaccess token (accounts.read)

    Parties not listed hold none of this flow’s credentials at this point.

  2. 2Banking APILocal action

    The API evaluates its own policy

    The token is valid. The policy for this endpoint asks for more: a scope the token lacks, a recent authentication, a phishing-resistant factor, or explicit approval of the transaction. The requirement lives at the API, which is the only place it cannot be bypassed.

    Who holds what after this step

    Agent
    access token (accounts.read)

    Parties not listed hold none of this flow’s credentials at this point.

  3. 3Banking API to AgentResponse

    The API explains what is missing

    The WWW-Authenticate header is machine-readable. insufficient_scope names the scope needed. insufficient_user_authentication names the authentication strength (acr_values) or freshness (max_age) required. The agent does not have to guess.

    HTTP/1.1 401 Unauthorized
    WWW-Authenticate: Bearer error="insufficient_user_authentication",
      error_description="A recent phishing-resistant login is required",
      acr_values="urn:example:acr:phishing-resistant",
      max_age=300

    Who holds what after this step

    Agent
    access token (accounts.read)

    Parties not listed hold none of this flow’s credentials at this point.

  4. 4Agent to UserDirect call

    The agent tells the user why it stopped

    A well-behaved agent surfaces the reason in its own interface before sending the user anywhere. This is courtesy, not security. The authorization server will show the authoritative version.

    Who holds what after this step

    Agent
    access token (accounts.read)

    Parties not listed hold none of this flow’s credentials at this point.

  5. 5Agent to Authorization serverVia the user’s browser or device

    A new authorization request describes one transaction

    The agent echoes the API’s requirements and attaches authorization_details: a typed JSON object describing the transfer. Sending this through a pushed authorization request (PAR) keeps the details out of the browser’s address bar and stops anyone tampering with them on the way.

    {
      "acr_values": "urn:example:acr:phishing-resistant",
      "max_age": 300,
      "authorization_details": [
        {
          "type": "transfer",
          "locations": ["https://bank.example/api"],
          "from_account": "…4411",
          "to": { "name": "Riverside Landscaping", "iban": "DE89 3704 0044 0532 0130 00" },
          "amount": { "currency": "EUR", "value": "480.00" }
        }
      ]
    }

    Who holds what after this step

    Agent
    access token (accounts.read)

    Parties not listed hold none of this flow’s credentials at this point.

  6. 6Authorization server to UserVia the user’s browser or device

    The authorization server shows the real transaction

    The user is asked for the stronger factor and shown the transfer as the server received it: payee, account, amount. If injected instructions changed the payee, this is where a person can catch it.

    Who holds what after this step

    Agent
    access token (accounts.read)

    Parties not listed hold none of this flow’s credentials at this point.

  7. 7User to Authorization serverVia the user’s browser or device

    The user approves one specific thing

    Not “allow the agent to make transfers.” This transfer.

    Who holds what after this step

    Agent
    access token (accounts.read)

    Parties not listed hold none of this flow’s credentials at this point.

  8. 8Authorization server to AgentVia the user’s browser or device

    Back to the agent

    The usual authorization code response, protected by PKCE.

    Who holds what after this step

    Agent
    access token (accounts.read)newauthorization code

    Parties not listed hold none of this flow’s credentials at this point.

  9. 9Agent to Authorization serverDirect call

    The agent redeems the code

    Identical to the baseline flow.

    Who holds what after this step

    Agent
    access token (accounts.read)used upauthorization code

    Parties not listed hold none of this flow’s credentials at this point.

  10. 10Authorization server to AgentResponse

    A token that carries the approval

    The new token records how and when the user authenticated (acr, auth_time) and includes the approved authorization_details.

    {
      "sub": "user:alice",
      "aud": "https://bank.example/api",
      "acr": "urn:example:acr:phishing-resistant",
      "auth_time": 1794268790,
      "exp": 1794269090,
      "authorization_details": [
        { "type": "transfer", "to": { "iban": "DE89 3704 0044 0532 0130 00" },
          "amount": { "currency": "EUR", "value": "480.00" } }
      ]
    }

    Who holds what after this step

    Agent
    access token (accounts.read)newaccess token (this transfer)

    Parties not listed hold none of this flow’s credentials at this point.

  11. 11Agent to Banking APIDirect call

    The agent retries with the stronger token

    The API compares the request body with the authorization details in the token. They must match.

    Who holds what after this step

    Agent
    access token (accounts.read)access token (this transfer)

    Parties not listed hold none of this flow’s credentials at this point.

  12. 12Banking API to AgentResponse

    The transfer goes through

    The stronger token expires in minutes. The agent drops back to its everyday token for everything else.

    Who holds what after this step

    Agent
    access token (accounts.read)used upaccess token (this transfer)

    Parties not listed hold none of this flow’s credentials at this point.

Watch out for

  • Step-up only works if the API enforces it. An API that accepts the weak token for the sensitive call has no step-up.
  • An agent can be steered by injected instructions into triggering a step-up. The consent screen is the defense, so it must show what will actually happen, sourced from the authorization request rather than from the agent’s description of it.
  • Scope accumulation: after several step-ups the token may hold everything. Prefer transaction-bound authorization details that expire with the action.
  • If the user is not in the session, a redirect goes nowhere. Use CIBA for that case.

Standards in play

See the full tracker for status and links.

RFCRFC 9396

Rich Authorization Requests

Consent for one payment or one file, not “payments” or “files.” The base of several agent proposals.

RFCRFC 6750

Bearer Token Usage

The default way agents present tokens, and the thing proof-of-possession schemes are trying to replace.

Final specMCP specification 2026-07-28

MCP Authorization

The most widely implemented authorization profile written specifically for agents and tools.