# 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](https://whosonfirst.ai/learn/what-the-api-sees/) is where they start, and each is in the [glossary](https://whosonfirst.ai/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](https://whosonfirst.ai/flows/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.

### 1. Alice asks for something the agent cannot reach yet

Alice → Agent · Direct call

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. The agent sends Alice’s browser to the authorization server

Agent → Authorization server · Via the user’s browser or device

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.

```http
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**: code_verifier (new)

### 3. Alice signs in and says yes, away from the agent

Alice → Authorization server · Via the user’s browser or device

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

### 4. Her browser comes back carrying a one-time code

Authorization server → Agent · Via the user’s browser or device

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.

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

Who holds what after this step

- **Agent**: code_verifier · authorization code (new)

### 5. The agent trades the code for a token, directly

Agent → Authorization server · Direct call

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**: ~~authorization code~~ (used up) · ~~code_verifier~~ (used up)

### 6. The authorization server issues the token

Authorization server → Agent · Response

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.

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

Who holds what after this step

- **Agent**: access token (new) · refresh token (new)

### 7. The agent calls the API

Agent → Calendar API · Direct call

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.

```http
GET /events?day=tomorrow HTTP/1.1
Host: calendar.example
Authorization: Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K2p3dCJ9…
```

Who holds what after this step

- **Agent**: access token · refresh token

### 8. The API answers

Calendar API → Agent · Response

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 token · refresh token

## 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](https://whosonfirst.ai/standards/#rfc9700) says it must not be used. For a service that offers nothing else, see [the final lesson](https://whosonfirst.ai/learn/limits/#no-other-way).

**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.

A common mix-up

“Getting an access token is how my app finds out who the user is.”

An access token is for the API, not for your app. It says what may be done, and your app is not meant to read it. Finding out who signed in is a separate job with its own token, the ID token, which is what most “Sign in with” buttons add on top. Access is _authorization_. Knowing who someone is, is _authentication_. An agent usually needs the first, and only sometimes the second.

## 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.

A common mix-up

“In OAuth, the client is the user.”

The client is always the app. The person is the user, or in the specifications the resource owner. When a document says “the client authenticates,” it means your software is proving which app it is. It never means Alice.

## 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.

**1. Where does Alice type her password?**

- Into the agent, which passes it to the authorization server.

  **Not quite.** The agent never handles it. Keeping the password away from the app is the reason the browser is sent elsewhere.

- Only on the authorization server’s own page.

  **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.

- Into the calendar API.

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

**2. Someone copies the authorization code out of Alice’s browser history. Why can they not use it?**

- Because the code is encrypted.

  **Not quite.** It is a plain random string. What protects it is that it is not enough alone.

- They can. A code is as good as a token.

  **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.

- It works once, and only with the value the agent kept.

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

**3. The access token expires while the agent is still working. What happens next?**

- The agent trades its refresh token for a new access token.

  **Right.** Alice is not asked again. That convenience is also why the refresh token is the thing most worth protecting.

- Alice has to sign in again.

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

- The API extends it, because the agent is in the middle of a task.

  **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](https://whosonfirst.ai/glossary/).

| In this lesson | Its name | Defined as |
| - | - | - |
| The whole routine | [OAuth](https://whosonfirst.ai/glossary/#oauth) | A 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 record | [Consent](https://whosonfirst.ai/glossary/#consent) | A 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 browser | [Authorization code](https://whosonfirst.ai/glossary/#authorization-code) | A 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-in | [PKCE](https://whosonfirst.ai/glossary/#pkce) | A one-time secret that ties an authorization code to the client instance that requested it. |
| What gets a new access token without asking Alice | [Refresh token](https://whosonfirst.ai/glossary/#refresh-token) | A 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 secret | [Confidential client](https://whosonfirst.ai/glossary/#confidential-client) | A 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 cannot | [Public client](https://whosonfirst.ai/glossary/#public-client) | A 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.

- [Authorization code with PKCE](https://whosonfirst.ai/flows/authorization-code-pkce/) The same flow at full size: eleven steps, with the HTTP on the wire.
- [MCP authorization](https://whosonfirst.ai/flows/mcp-authorization/) The same flow when the agent has never seen the server before.
- [Device authorization](https://whosonfirst.ai/flows/device-authorization/) For an agent with no browser where it runs.

[← Lesson 2 What is a token?](https://whosonfirst.ai/learn/tokens/) [Lesson 4 → What if nobody is there to sign in?](https://whosonfirst.ai/learn/whose-authority/)

---

From Who’s on First, a field guide to agent authentication and authorization. This page: https://whosonfirst.ai/learn/getting-a-token/

Standards status last reviewed November 2026. Drafts move quickly: check the linked source before you build on one.
