Flows / Human present

Authorization code with PKCE

The baseline. A person signs in, approves specific scopes, and the agent receives a token that says so.

Based onRFC 6749 and RFC 7636published RFCsGuide reviewedNovember 2026

Sources and review details
This page
Implementation guideAn explanation, not a specification
Basis 1 of 2
OAuth 2.0 Authorization FrameworkRFC · October 2012
Basis 2 of 2
PKCERFC · September 2015
8 required references
8 published1 more listed as optional or background
Human involvement
Human present
Review
Checked against sources in November 2026How it was checked

This is the flow almost everything else on this site builds on or stands in for. The user is sent to an authorization server they already trust, signs in there, and approves what the agent may do. The agent never sees a password; it gets a short-lived access token scoped to one resource.

This page shows the flow as published: the OAuth 2.0 authorization code grant (RFC 6749) with PKCE (RFC 7636), run the way the security best current practice (RFC 9700) sets out. That means PKCE, which it requires of a public client like this one; exact redirect URI matching; a token restricted to one audience; and no implicit or password grant. If an agent acts for a person and that person is at the keyboard, start here.

Step by step

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

Sequence diagram4 participants, 11 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.

1“Check my calendar”2Create PKCE pair + state3Authorizationrequest4Sign-in and consent screen5Approve calendar.read6Redirect with code7Token request+ verifier8Access token(+ refresh)9API call with Bearer token10Validate iss, aud, exp, scope11200 OK

  1. 1User to AgentDirect call

    The user asks for something the agent cannot do yet

    The agent has no credential for the calendar API. It could ask for one up front, but the better habit is to wait until a task actually needs it: the consent screen then matches something the user just asked for.

  2. 2AgentLocal action

    The agent prepares a one-time secret

    The agent generates a random code_verifier, hashes it into a code_challenge, and creates a state value. The verifier stays with the agent. Only the hash travels through the browser, so an authorization code stolen in transit is useless without it.

    code_verifier  = random(43–128 chars)
    code_challenge = BASE64URL(SHA256(code_verifier))
    state          = random()

    Who holds what after this step

    Agent
    newcode_verifier

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

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

    The agent sends the user’s browser to the authorization server

    Everything in this request is visible to the browser, so it carries no secrets: who the client is, where to send the user back, what is being asked for, and which resource the token is for. client_id is the identifier the authorization server gave this client when it was registered. The resource parameter matters more for agents than for classic apps, because one agent talks to many servers and a token must be good for exactly one.

    GET /authorize?response_type=code
      &client_id=s6BhdRkqt3
      &redirect_uri=http://127.0.0.1:52100/callback
      &scope=calendar.read
      &resource=https://calendar.example/api
      &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
      &code_challenge_method=S256
      &state=af0ifjsldkj HTTP/1.1
    Host: auth.example

    Who holds what after this step

    Agent
    code_verifier

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

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

    The authorization server authenticates the user

    The user signs in with whatever the authorization server requires: a passkey, SSO, a second factor. The agent is not part of this conversation. Then the server shows what is being requested and by whom. This screen is the only place a human sees the scopes in plain language, which is why its wording deserves more care than it usually gets.

    Who holds what after this step

    Agent
    code_verifier

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

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

    The user consents

    Consent is recorded at the authorization server, tied to this user, this client and these scopes. That record is what lets the user (or an admin) see and revoke the grant later without involving the agent.

    Who holds what after this step

    Agent
    code_verifier

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

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

    The browser returns to the agent with an authorization code

    The code is single-use and expires in about a minute. The agent checks that state matches what it sent and, where the server provides it, that iss matches the authorization server it meant to talk to. That second check defeats mix-up attacks, which are a real risk for agents that deal with many authorization servers.

    GET /callback?code=SplxlOBeZQQYbYS6WxSbIA
      &state=af0ifjsldkj
      &iss=https://auth.example HTTP/1.1
    Host: 127.0.0.1:52100

    Who holds what after this step

    Agent
    code_verifiernewauthorization code

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

  7. 7Agent to Authorization serverDirect call

    The agent redeems the code directly

    This call goes straight from the agent to the token endpoint, not through the browser. The agent presents the code and the original code_verifier. The server hashes the verifier and compares it with the challenge from step 3; only the party that started the flow can finish it. redirect_uri is sent again because RFC 6749 requires it here whenever the authorization request carried one.

    POST /token HTTP/1.1
    Host: auth.example
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=authorization_code
    &code=SplxlOBeZQQYbYS6WxSbIA
    &redirect_uri=http://127.0.0.1:52100/callback
    &client_id=s6BhdRkqt3
    &code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
    &resource=https://calendar.example/api

    Who holds what after this step

    Agent
    code_verifierused upauthorization code

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

  8. 8Authorization server to AgentResponse

    The authorization server issues tokens

    The access token is short-lived and bound to one audience. The refresh token, if issued, lets the agent get new access tokens without the user. For public clients the security best current practice (RFC 9700) requires refresh tokens to be either sender-constrained or rotated on use.

    {
      "access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K2p3dCJ9…",
      "token_type": "Bearer",
      "expires_in": 600,
      "refresh_token": "8xLOxBtZp8",
      "scope": "calendar.read"
    }

    Who holds what after this step

    Agent
    newaccess tokennewrefresh tokenused upcode_verifier

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

  9. 9Agent to APIDirect call

    The agent calls the API

    The token goes in the Authorization header and nowhere else. Never in a URL, and never into the model’s context window: a token the model can read is a token a prompt injection can exfiltrate.

    GET /api/events?from=today HTTP/1.1
    Host: calendar.example
    Authorization: Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K2p3dCJ9…

    Who holds what after this step

    Agent
    access tokenrefresh token

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

  10. 10APILocal action

    The API validates the token

    Signature, issuer, expiry, scope, and above all audience. An API that accepts tokens minted for some other service becomes a confused deputy. If the token is for https://calendar.example/api and this is not that, reject it.

    Who holds what after this step

    Agent
    access tokenrefresh token

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

  11. 11API to AgentResponse

    The API responds

    The token in this example is a JWT access token (RFC 9068), so the API can read which user it is for (sub) and which client it was issued to (client_id). What it does not know from a plain access token is that an AI agent was in the middle, or which model run decided to make the call. Flows further down this site add that information.

    Who holds what after this step

    Agent
    access tokenrefresh token

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

Watch out for

  • Consent fatigue. An agent that connects to twenty tools produces twenty consent screens, and people stop reading them. Ask for narrow scopes, late.
  • The token is the user’s authority minus nothing unless scopes are narrow. An agent with mail.readwrite can do anything the user can do to mail.
  • Refresh tokens outlive the conversation. Decide where they are stored and who can revoke them before you ship.
  • Desktop and CLI agents are public clients. Use a loopback redirect and PKCE; do not embed a client secret in a binary.

How OAuth 2.1 relates

OAuth 2.1 (draft-ietf-oauth-v2-1) is an Internet-Draft of the IETF OAuth working group, at revision -17 dated October 30, 2026. It is not an RFC. It gathers RFC 6749, RFC 6750, RFC 7636, RFC 8252, RFC 9700 and RFC 10017 into one document and changes some behavior as it does.

Two of its changes touch this page. A token request no longer carries redirect_uri, and the PKCE plain method is removed. This page shows the published RFCs, so step 7 still sends redirect_uri. A flow built on the draft, such as MCP authorization, leaves it out.

Standards in play

See the full tracker for status and links.

Required

What the flow implements, shows in a step, or must support. These decide its maturity rating.

RFCRFC 7636

PKCE

Mandatory for MCP clients, which must also refuse to proceed if a server does not advertise support.

RFCRFC 8707

Resource Indicators

Required by MCP. An agent talks to many servers; this is what stops a token for one being replayed at another.

RFCRFC 8252

OAuth for Native Apps

Governs desktop agents, IDE extensions and CLIs, which are native public clients.

RFCRFC 6750

Bearer Token Usage

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

Background

Related reading. Not what the flow implements, and not used in a step.

WG draftdraft-ietf-oauth-v2-1

OAuth 2.1

The baseline MCP profiles. Worth knowing that the normative base of most agent authorization is still, formally, a draft.