Logging an agent in¶
Most agents reach their model without this page. A pi agent — the default runtime — boots on
a provider API key written with agentctl secret set, and that is the whole of its model
auth. agentctl login covers the OAuth flows: claude's model subscription, and the accounts
behind the Google and Microsoft MCPs, which either runtime can be wired to.
agentctl login <provider> <name>|self
Providers: claude, google, ms365, atlassian, powerbi. Every login
and install preflights its secrets and refuses with the exact agentctl secret set … fix
line.
| provider | method | with the auth broker | without a broker |
|---|---|---|---|
| ms365 | device-code | open the URL, enter the code | same |
| claude | PKCE | the plugin DMs the URL → login claude … --code <code#state> |
same |
| redirect | click the DM'd link; the broker catches the code | copy the localhost URL → login google … --code <url> |
|
| atlassian | PKCE + Dynamic Client Registration | click the DM'd link; the broker catches the code | copy the URL → login atlassian … --code <code> |
| powerbi | PKCE (delegated Fabric token) | click the DM'd link; the broker catches the code | refused — broker required |
The broker is a redirect-flow convenience, and applies to google, atlassian and
powerbi: all three need a redirect on an OAuth client agentctl owns (atlassian
registers its own public PKCE client per agent via DCR, discarding any client secret Atlassian
returns — its token auth method is none). claude uses a vendor app with a fixed redirect, so
it never touches the broker; ms365 defaults to device-code but also has a broker branch
(login ms365 picks it when a broker is configured).
powerbi is the one provider with no non-broker path. login powerbi refuses outright when
BROKER_URL is unset rather than falling back to device-code, and mints a delegated Fabric
token into ~/.powerbi-mcp/token-cache.json — a separate resource from Graph, so an ms365
login does not authorize it and the two caches never merge. Set
POWERBI_MCP_CLIENT_ID and POWERBI_MCP_TENANT_ID first. POWERBI_MCP_CLIENT_SECRET is
optional and turns the Entra app registration into a confidential client; set it at either
scope — shared or --agent — since the connector's unit sources both env files.
Auth broker (optional)¶
agentctl secret set BROKER_URL --value https://auth.example.com # your DNS/TLS host
agentctl auth-broker install # generates BROKER_HMAC_KEY, binds :8734
# then register ONE redirect URI per OAuth client (Google console → Authorized redirect URIs):
# https://auth.example.com/oauth/callback
Front BROKER_PORT (:8734) with your own HTTPS reverse proxy, public URL → :8734. TLS and DNS
are yours, not agentctl's.
The signed-state flow¶
| step | what happens |
|---|---|
| 1 | login google mints a signed, single-use state, 10-minute TTL |
| 2 | it sets redirect_uri=$BROKER_URL/oauth/callback on a Web-type client |
| 3 | it registers the pending auth with agentd and DMs the link |
| 4 | the human clicks the link; Google redirects to GET /oauth/callback?code=&state= |
| 5 | the broker verifies the HMAC, then hands (state, code) to agentd over /run/agentctl.sock |
| 6 | agentd consumes the state single-use, then runs the token exchange as that agent |
The state is HMAC-SHA256 over agent, nonce and expiry, keyed by BROKER_HMAC_KEY. A replayed,
expired or unknown state is rejected. The OAuth code travels only over the local socket, and
nothing is logged.