Skip to content

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