OAuth for agents is not one login moment. It is a chain of exchanges. Each step turns a short-lived proof into bearer access the runtime can spend on APIs.
Token exchange is the server-side redeem: authorization codes become access tokens. Refresh tokens renew access. Client-credentials grants prove an agent identity without a browser.
The sections below focus on that redeem step and the grants that feed it. For wiring consent, scopes, and broker choice end to end, use the secure third-party API build sequence linked at the end.
What token exchange means for agents
In the authorization-code flow, the user approves on a hosted screen. The browser lands on your redirect with a one-time code, not an access token.
Your broker or backend POSTs that code to the token endpoint. The response carries access tokens and often a refresh token. The agent runtime should see only the access token.
Refresh grant is a second exchange. The broker trades a refresh token for a new access token and usually a new refresh token. That loop runs while the owner has not revoked.
Client-credentials style grants skip the browser code when the protocol allows an agent id and secret. Relay Identity uses this shape for `authenticateAgent`. It is not a substitute for user delegation when the upstream API requires user consent.
Three grants agents actually use
| Grant | Input at token endpoint | Typical agent use |
|---|---|---|
| `authorization_code` | One-time code, redirect URI, PKCE `code_verifier`, client auth | First connect after owner consent on Gmail, GitHub, or your Relay consent URL |
| `refresh_token` | Refresh token value plus client auth | Unattended jobs renew access without waking the owner |
| `client_credentials` (agent) | Agent id, agent secret, audience or client id, optional scope | Relay Identity: agent proves itself to your app before calling your APIs |
Who performs the exchange (never the model)
The language model is not an OAuth client. It must not receive authorization codes, refresh tokens, or client secrets. Those belong in your broker, worker, or API layer only.
The agent holds an opaque session handle or a short-lived access token injected by your runtime. Tool code calls your server. Your server attaches the bearer header upstream.
The broker holds refresh families, client secrets, PKCE verifiers until redeem, and audit rows tying agent id to owner id. If the model can read any of that, prompt injection becomes account takeover.
This split holds whether you built the broker or use a product. The trust boundary is the same: exchange runs in TLS-terminated server code with secrets outside the LLM context window.
PKCE at exchange time (S256 and one-time codes)
PKCE binds the authorization code to the client that started the flow:
Challenge at authorize
The authorize URL sends `code_challenge` with method S256. The verifier never appears in the browser redirect.
Verifier at token
`exchangeCode` sends `code_verifier` with the code. The token endpoint hashes it and must match the stored challenge.
One-time code
A code presented twice should yield `invalid_grant` on the second attempt. Treat reuse as an incident, not a retry loop.
Public agent clients
Even with a confidential client secret, agent-shaped clients should still send PKCE on user delegation flows.
Token endpoint errors agents should handle
Fail closed. Map errors to owner-visible state instead of silent retries:
`invalid_grant`
Expired code, wrong redirect URI, bad verifier, or spent refresh. Stop refreshing and ask for re-consent unless your broker defines a safe retry.
Pending approval
Relay Identity may return a typed pending result before the owner approves the agent. Retry only after approval, not in a tight loop.
Reused authorization code
Often means interception or double submit. Revoke the session family if your broker supports family semantics.
Wrong `code_verifier`
Usually a bug or a code redeemed by another process. Do not log the verifier. Rotate credentials if the pattern repeats.
Expired authorization code
Codes are minutes, not days. Restart authorize; do not paste the stale code into agent chat.
Refresh token exchange: rotation and family revoke
Long-running agents depend on refresh. Theft must be detectable:
Rotation
Each refresh invalidates the previous refresh token and issues a new one. Store the latest refresh atomically.
Family revocation on replay
If an old refresh token is used after rotation, revoke the whole family. That pattern signals theft more than a flaky network.
Broker-only refresh
Only the token service calls the upstream refresh endpoint. The agent never stores refresh material in env vars the CTO might commit.
Introspect before high-risk calls
When supported, verify access token active status server-side. Unreachable introspection should deny the call, not bypass checks.
Anti-patterns that break agent OAuth
These show up in agent products and fail audits:
Model or prompt holds refresh tokens
Any context leak exfiltrates months of authority. Refresh stays in the broker vault only.
Exchanging in the prompt
Pasting a code into chat trains users to leak grants. Exchange only in server code.
One refresh family per tenant
Shared refresh across customers means one leak compromises every account on that key.
One refresh family per many agents
You cannot revoke a single automation. Map refresh rows per agent id where the protocol allows.
Fail-open introspect
If verification times out, deny the tool call. An outage is not permission to skip auth.
Relay SDK: verified token endpoint shapes (1.0.0)
All three grants POST to `/relay/oauth/token` in `@empyre/relay-sdk` **1.0.0** (read from this repo and registry.npmjs.org on 2026-09-27). Server-side only:
import { RelayClient } from "@empyre/relay-sdk";
const relay = new RelayClient({
clientId: process.env.RELAY_CLIENT_ID,
clientSecret: process.env.RELAY_CLIENT_SECRET,
});
// User delegation after hosted consent (PKCE)
const tokens = await relay.exchangeCode(code, codeVerifier, redirectUri);
// Unattended renew
const renewed = await relay.refresh(tokens.refresh_token);
// Relay Identity: agent id + secret → scoped access (separate client config)
const identity = new RelayClient({ audience: "your-app.example" });
const agent = await identity.authenticateAgent(
process.env.RELAY_AGENT_ID,
process.env.RELAY_AGENT_SECRET,
["openid", "profile"],
);
How this fits Empyre Relay and company operators
Empyre Relay at relay.empyre.dev returned HTTP 200 on 2026-09-27. The npm package is @empyre/relay-sdk 1.0.0. Relay has been feature-frozen since 2026-07-10. Scoped GET /relay/oauth/userinfo (2026-08-06) returns consented profile claims when the token allows.
Empyre Vault (@empyre/vault-sdk 1.0.0, same date) solves signing and decryption, not login-as-user at a third-party API. Use Vault when private key bytes must never enter the agent runtime.
Empyre builds and runs whole businesses after launch, with a first deploy ceiling of thirty minutes when code is the bottleneck. Generated companies still need the same token-exchange discipline for founder integrations. Relay does not remove per-vendor OAuth inside an operator.
Read let an AI agent authenticate with third-party APIs securely for the full build sequence. Pair with OAuth for autonomous software and OAuth for AI agents for identity theory and buyer checks.
Common questions
Is token exchange the same as user login?
No. Login is interactive consent. Exchange is the server redeem that turns a code or refresh credential into bearer access the agent runtime may use.
Where does PKCE matter most?
At the token endpoint when redeeming `authorization_code`. The verifier proves the same client that started the flow is redeeming the code.
When should I use `authenticateAgent` vs `exchangeCode`?
Use `exchangeCode` when a human delegated access to a third-party API. Use `authenticateAgent` for Relay Identity when your app issues scoped tokens to registered agents.
What should the agent do on `invalid_grant`?
Stop refreshing silently. Surface re-consent to the owner or mark the integration disconnected. Repeated invalid grants may mean rotation detected theft.
How is this different from the secure third-party API build sequence?
That page orders consent, scope, broker choice, and wiring. This page stays on grants, redeem responsibility, PKCE at exchange, rotation, and errors.
What npm package version was verified?
`@empyre/relay-sdk` and `@empyre/vault-sdk` at **1.0.0** on registry.npmjs.org, read 2026-09-27. Scope is `@empyre`, not `@empyre/relay`.
Try Empyre free for 3 days
Describe a business in plain words and watch eight AI agents build and deploy it. Starter is free for the first 3 days.
Related
Last updated 2026-09-27. Competitor descriptions reflect each product's publicly documented capabilities at that date; they change often, so check the source before relying on a detail.