8 min read

How does an agent get a token for a user?

Alice has to say yes, and the agent must never see her password. OAuth is the routine that makes both true. You have clicked through it a hundred times.

This lesson builds on the ones before it. If you arrived here first and a term is unfamiliar, lesson 1 is where they start, and each is in the glossary.

You have seen this before

An app sends you to Google. Google asks whether the app may see your calendar. You click Allow and land back in the app. That is the routine this lesson takes apart, and it is called OAuth.

In that example Google’s sign-in page is the authorization server, the Google Calendar API is the API, and the app is the client. Inside a company, the authorization server is usually whatever employees already sign in with. A company can also take the decision away from Alice: an administrator decides which agents may reach which applications, and she is shown no approval screen. That is a flow of its own, cross-app access. In this lesson, Alice is asked. A library does most of the work in both cases, which is how so many of us have shipped this without ever seeing the steps.

For an agent the steps are worth seeing once. Each exists to close a particular hole, and an agent leans on all of them.

The exchange, step by step

Four parties and eight steps, in four parts:

  1. Alice asks, and the agent sends her to sign in. The agent has no access yet. It sends her browser to the authorization server, and keeps a one-time value to itself.
  2. Alice signs in and approves, away from the agent. She proves who she is to the authorization server and says yes there. Her approval is recorded there, which is called consent.
  3. The agent trades a one-time code for a token. Her browser comes back with a code that works once. The agent trades it, together with the value it kept, on a direct connection. That check is called PKCE.
  4. The agent calls the API. The access token goes in a header, the API runs its five checks, and Alice gets her answer.

The diagram walks through each step. Before you start, guess at which step the agent first holds something it could send to the calendar API.

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

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

1Alice asks, and the agent sends her to sign in2Alice signs in and approves, away from the agent3The agent trades a one-time code for a token4The agent calls the API1“What’s ontomorrow?”2Sends Aliceto sign in3Signs in and approves4One-time code5Code + thevalue it kept6Access token(+ refresh)7Request, with the token8Tomorrow’s events

  1. Part 1 of 4Alice asks, and the agent sends her to sign in

    1Alice to AgentDirect call

    Alice asks for something the agent cannot reach yet

    The agent has no token for the calendar. Asking for one now, when a task needs it, means the screen Alice is about to see matches something she just asked for.

  2. Part 1 of 4Alice asks, and the agent sends her to sign in

    2Agent to Authorization serverVia the user’s browser or device

    The agent sends Alice’s browser to the authorization server

    The agent does not ask Alice for anything. It opens her browser on the authorization server’s own page, with a note in the address: which app is asking, what for, which API the token is for, and where to send her back. This is the jump you have seen behind every “Sign in with” button.

    The agent also makes up a random value for this one sign-in and sends only a scrambled version of it, the code_challenge. It keeps the original, the code_verifier, to itself. That matters in step 5.

    GET /authorize?response_type=code
      &client_id=calendar-assistant
      &scope=calendar.read
      &resource=https://calendar.example
      &redirect_uri=https://assistant.example/callback
      &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
      &code_challenge_method=S256 HTTP/1.1
    Host: auth.example

    Who holds what after this step

    Agent
    newcode_verifier

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

  3. Part 2 of 4Alice signs in and approves, away from the agent

    3Alice to Authorization serverVia the user’s browser or device

    Alice signs in and says yes, away from the agent

    Alice proves who she is to the authorization server, in whatever way it requires: a password, a passkey, a second factor. The agent is not part of this and sees none of it.

    Then the server shows her what is being asked, in plain words, and she approves. That approval is recorded at the authorization server. It is called consent, and Alice can withdraw it there later without going near the agent.

    Who holds what after this step

    Agent
    code_verifier

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

  4. Part 3 of 4The agent trades a one-time code for a token

    4Authorization server to AgentVia the user’s browser or device

    Her browser comes back carrying a one-time code

    The authorization server sends Alice’s browser back to the agent with an authorization code. It is not a token. It works once, and only for a short time.

    It is weak on purpose. This trip runs through the browser, where addresses end up in history and logs. So what travels here is worth nothing without the value the agent kept.

    GET /callback?code=SplxlOBeZQQYbYS6WxSbIA HTTP/1.1
    Host: assistant.example

    Who holds what after this step

    Agent
    code_verifiernewauthorization code

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

  5. Part 3 of 4The agent trades a one-time code for a token

    5Agent to Authorization serverDirect call

    The agent trades the code for a token, directly

    This call goes straight from the agent to the authorization server, not through the browser. The agent sends the code together with the code_verifier from step 2. The server scrambles it the same way and compares. Only whoever started the sign-in can finish it.

    That check has a name, PKCE (said “pixie”). Someone who stole the code from the browser has half of what they need, and the half that never left the agent is the half that counts.

    Who holds what after this step

    Agent
    used upauthorization codeused upcode_verifier

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

  6. Part 3 of 4The agent trades a one-time code for a token

    6Authorization server to AgentResponse

    The authorization server issues the token

    Back comes the access token from the previous lesson: about Alice, issued to this app, for the calendar API, read only, good for ten minutes.

    Often a second token comes with it. A refresh token is not sent to any API. The agent keeps it and shows it to the authorization server when the access token runs out, to get a new one without sending Alice through sign-in again.

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

    Who holds what after this step

    Agent
    newaccess tokennewrefresh token

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

  7. Part 4 of 4The agent calls the API

    7Agent to Calendar APIDirect call

    The agent calls the API

    The token goes in the Authorization header, and the API runs its five checks.

    One habit to form now: the token is held by your program and never shown to the model. Not in the prompt, not in the conversation, not in a log. A token the model can read is a token that text on a web page can ask it to repeat.

    GET /events?day=tomorrow 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.

  8. Part 4 of 4The agent calls the API

    8Calendar API to AgentResponse

    The API answers

    Alice gets her answer. The agent never saw her password, and it holds something that can do exactly one thing: read her calendar.

    Who holds what after this step

    Agent
    access tokenrefresh token

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

Why so many hops?

Each one answers a question people ask the first time they see this.

Why send Alice to another site? So that her password goes to one place: the service that already has it. An agent that took her password would hold her whole account and not the part the task needs, with no way to take that back short of changing the password. Copies would sit in your database and your logs. And anything typed into a conversation, the model can be asked to repeat by the next page it reads.

So for a service that offers this exchange the rule has no exceptions: an agent never asks a user for their password. Only the authorization server does. Asking would also teach people to type it into whatever asks, which is the habit phishing depends on. OAuth once had a way for an app to collect a password and trade it for a token, and the current security guidance says it must not be used. For a service that offers nothing else, see the final lesson.

Why a code first, and not the token? Because the trip back runs through the browser, and a browser is a leaky place. The code is built to be worthless if it leaks, as long as the authorization server insists on the value the agent kept. Current guidance requires every authorization server to. The token only ever travels on the direct connection in step 6.

Why does the token expire so fast? Because it is a bearer token, and a short life limits what a stolen copy is worth. The refresh token is what makes a short life bearable.

Before the first sign-in

The authorization server has to know the app that is asking. Usually a developer registers it once, in a console, and receives a client ID: the calendar-assistant you saw in the token. Registration also fixes the address the browser may be sent back to, so that a code cannot be delivered to someone else’s server.

Where the app runs changes one detail:

  • A hosted app with a backend can keep a secret. It is given a client secret at registration, or registers the public half of a key of its own, and proves which app it is when it trades the code. OAuth calls this a confidential client. Each user’s refresh token stays on the server, filed under that user.
  • An app on the user’s machine, such as a desktop app, a command-line tool or an editor extension, cannot keep a secret: anything shipped inside it can be extracted. It is a public client, and PKCE is what it relies on. The value PKCE uses is made up fresh for each sign-in, so there is nothing shipped inside the app to extract.
  • An agent with no browser where it runs shows a short code, and Alice approves on her phone or laptop instead. That variation is called device authorization.

When Alice has gone home

The refresh token is what lets an agent keep working after Alice closes the tab. That makes it the most valuable thing the agent holds, and it changes what you have to build:

  • Store it where only your code can reach it: encrypted on your server and filed under its user, or in the operating system’s credential store on a desktop. Never in the prompt, and never in a log.
  • Expect it to stop working. Alice can withdraw her consent, an administrator can revoke it, and the authorization server may replace it every time it is used.
  • Notice what you have built: software holding a person’s access, with that person no longer watching. The next lesson comes back to this.

And MCP?

If your agent connects to a remote MCP server, this same exchange runs. MCP adds two things in front of it, and one rule. The agent may never have heard of the server before, so the server’s first reply points to a document that names its authorization server, and the agent can identify itself by a web address it controls instead of a client ID registered in advance. The rule is that every token is requested for that one server. After that, it is the eight steps above.

One caution comes with it. A server nobody has vetted is telling your agent which addresses to fetch and where to send the user. Connect only to servers the user chose, and show the user which site they are about to sign in to. If your agent runs on your own servers, refuse addresses inside your own network as well.

Check yourself

Answer before you read on. Nothing is recorded.

1Where does Alice type her password?
  • Not quite. The agent never handles it. Keeping the password away from the app is the reason the browser is sent elsewhere.

  • Right. She signs in at the one service that already has her password. The agent learns that she approved, and nothing about how she proved who she is.

  • Not quite. The API takes no part in sign-in. It only ever sees the token.

2Someone copies the authorization code out of Alice’s browser history. Why can they not use it?
  • Not quite. It is a plain random string. What protects it is that it is not enough alone.

  • Not quite. A code cannot be sent to an API at all. It can only be traded for a token, once, by the app that holds the matching value.

  • Right. That is PKCE. The half that travels through the browser is useless without the half that never left the agent.

3The access token expires while the agent is still working. What happens next?
  • Right. Alice is not asked again. That convenience is also why the refresh token is the thing most worth protecting.

  • Not quite. Only if there is no refresh token, or it has been revoked. Normally the agent renews without her.

  • Not quite. The API cannot extend a token. It can only check one, and an expired token fails the check.

What to keep

  • The user signs in and approves at the authorization server, never in the agent.
  • What comes back through the browser is a one-time code. The token itself only ever travels on a direct connection.
  • PKCE ties the code to the app that asked for it.
  • A refresh token lets the agent carry on without the user. Guard it as if it were the user’s access, because it is.
  • Tokens are held by your program and never shown to the model.

One rule

An agent never asks a user for their password. Only the authorization server does.

The names for these

The rest of this guide, and every specification, uses these terms. Each links to its entry in the glossary.

In this lessonIts nameDefined as
The whole routineOAuthA framework that lets one program obtain limited access to something, for a person or for itself. When it acts for a person, it does not need that person’s password. OAuth 2.0 is the published standard; OAuth 2.1 is the draft that consolidates it.
Alice saying yes, on the recordConsentA user’s recorded approval, at the authorization server, for a client to act with specified scopes.
The one-time code that comes back through the browserAuthorization codeA single-use, short-lived code returned through the browser after a user consents, which the client exchanges for tokens over a direct connection.
The agent’s proof that it started the sign-inPKCEA one-time secret that ties an authorization code to the client instance that requested it.
What gets a new access token without asking AliceRefresh tokenA longer-lived credential used to obtain new access tokens without the user. The thing most worth protecting in an agent runtime.
An app that can keep a secretConfidential clientA client that can keep a secret, because it runs on a server its operator controls. It proves which application it is with a client secret or a key. Contrast with a public client.
An app that cannotPublic clientA client that cannot keep a secret, because it runs on a user’s device. Desktop, CLI and browser-based agents are public clients.

Go deeper

The same ground at full depth, when you want it. None of it is needed for the next lesson.