How Agents Authenticate to Willow
Every call to the Willow gateway settles one question first: which Willow identity is making this call? From there everything follows — the identity's groups decide which tools exist, guards evaluate against it, and the audit log attributes the call to it.
All the options below answer that one question. They differ in who vouches for the answer: your identity provider, you, or the user themselves.
This page covers the front door only. Which credential Willow then sends to the upstream service is a separate choice, in Authentication: Connector vs Proxy Modes.
The options
| Option | Who vouches for the identity | Use it when |
|---|---|---|
| Auth exchange | Your identity provider | The user is already signed in to your system through Okta or Keycloak, and you hold their token. |
| Impersonation | You do | You know the user's email, and Willow can trust the calling client like an admin. |
| OAuth | The user, by signing in | You have to ask the user to sign in, and the client can store what it gets back. |
| OTT | The user, by signing in | You have to ask the user to sign in, and the client can't store an access token. |
| Machine users and background agents | Nobody — there is no user | The caller acts as itself, not on behalf of a person. |
Auth exchange
If your own product already authenticated the user against Okta or Keycloak, you're holding a JWT that proves who they are. Send it to the gateway with x-auth-exchange: true and Willow verifies it against your JWKS endpoint and acts as the matching Willow user.
This is the right answer for an internal system with an agent in it. The user signed in once, to you, and there is no reason to make them do it again. You also store nothing: no token to keep, no refresh to manage.
Security: the JWT decides who the call acts as; the API token you send alongside it only authorizes your application to perform exchanges at all. Both are required, and that API token is an organization credential that must stay on your server. Users must already exist in Willow — auth exchange resolves an existing user by email and never creates one.
Read more: Auth Exchange
Impersonation
When you know who the user is but have no token proving it, you can send an API token carrying the run:impersonate scope plus an x-user-email header, and Willow acts as that user. There is no proof — you assert the identity and Willow believes you.
That trade is the whole story: you are asking Willow to trust the calling client the way it trusts an admin. It's a reasonable ask for a service you run on infrastructure you control. It is not something to extend to anything running on an end user's machine.
Security: the token can act as any user in the organization, including admins, so a leak is equivalent to every account at once. Whatever code decides the value of x-user-email has become an authentication system — a spoofing or injection bug there is account takeover. Keep the token server-side, grant it only run:impersonate (never all, which implies it — as do legacy tokens created before scopes existed), and use a separate token per application, since revocation is all-or-nothing.
Where you can get an IdP token for the same user, auth exchange gives you the same experience with the identity proven instead of asserted. Prefer it.
Read more: API Tokens
OAuth
If nobody has signed the user in yet, ask them to. Willow runs an OAuth 2.0 / OpenID Connect authorization server: the client registers itself, sends the user to /authorize, and exchanges the resulting code for an access token. The user signs in through your normal identity provider, so the identity that comes out is the same one they get everywhere else.
This is the default for a human at an MCP client. Cursor, VS Code, Claude, and ChatGPT all implement the MCP OAuth spec and will handle the whole flow once you give them the gateway URL. Tokens expire and refresh, nothing long-lived sits in a config file, and the sign-in inherits whatever your IdP enforces — MFA, device posture, conditional access.
What OAuth asks of the client is durability: it has to keep its registration, store the access and refresh tokens, and host somewhere for the redirect to land. Established clients do all of this. If yours can't, use OTT.
Security: dynamic client registration is open by design, so any client that can reach the Connect host can register itself — turn on Allow only authenticated clients in security settings to limit this to clients you've configured. Require PKCE (S256): loopback redirect URIs match on scheme, host, and path only, so any local port is accepted and PKCE is what makes an intercepted code useless. Token lifetime defaults to 24 hours and is configurable.
Read more: OAuth Authorization Server
OTT
OTT gets you the same real sign-in as OAuth without the client having to hold on to anything. Your code invents its own token, asks the gateway for a sign-in URL carrying it, and gets that URL to the user however you like — a Slack DM, an email, a link in your own UI. The user signs in through SSO, which binds the token to their Willow account, and your code polls until that lands. From then on every call carries Authorization: <userAccessKey>:<OTT>.
The difference from OAuth is where the credential comes from. Because your code proposes the token itself, there's nothing server-issued to persist: no client registration, no refresh token, no redirect endpoint to host. A client with no database can hold the pair in memory and simply re-run the flow after a restart. That makes OTT the option for anything too thin to be a proper OAuth client — a chat bot, a CLI, an embedded runtime — and for clients that don't implement OAuth at all.
The identity is a genuine signed-in user, identical to the OAuth path. What you skip is the bookkeeping, not the sign-in.
Security: despite the name the OTT is not one-time — it's a reusable session token, 24 hours by default, after which the user signs in again. More importantly, the token binds to whoever completes the sign-in, not to the person you meant to send it to. Deliver the link privately to exactly one person; a link posted somewhere shared means the first person to click it becomes the identity your code acts as from then on.
Read more: User Session Authentication
Machine users and background agents
Both are non-human identities that authenticate the same way, with Authorization: Bearer <access_key>:<secret>. Neither acts on behalf of a person; each acts as itself. The difference isn't the authentication — it's how you give it tools.
| Machine user | Background agent | |
|---|---|---|
| You are | integrating a platform you already run | building an agent you want Willow to define |
| Tools come from | the groups you add it to, like a person | capabilities granted to that one agent |
| Willow also manages | nothing else | system prompt, skills, triggers, sessions, deployment to a runtime |
So: a CI job, a cron script, or your own backend calling tools is a machine user. Something you want Willow to hold the definition of — prompt, scoped tools, triggers, session history — is a background agent.
Security: the secret is static with no expiry, so rotation is something you schedule rather than something the protocol does. Because there's no person behind it, everyone who interacts with it shares its access, so scope it to the least any of them should have. And it sits outside SSO entirely: no MFA, no conditional access, and no offboarding through your IdP — deprovisioning happens in Willow or not at all. For background agents, remember that deploying rotates the secret as a side effect, so any copy you were holding stops working.
Read more: Machine Users · Background Agents · Programmatic Gateway Access
Worked example
Authenticate a Slack Agent runs a single Slack bot through these options end to end: resolving the Slack user who triggered it to an email address, then impersonating them, asking them to sign in with an OTT, or running as a background agent instead — with the requests for each.
Related
- How Identities Authenticate — what credential reaches an internal MCP server behind the gateway
- Identity and Permissions — how access resolves once the identity is settled
- Connect Your AI Client — the end-user path