Tokens from a Client ID Metadata Document sign-in through an https
callback route were refreshed with the loopback redirect URI, which the
documents do not list, so every expiry needed a browser sign-in.
createMcpAuthProvider takes the app's redirectUrl and startMcpSignIn
passes its own.
startMcpSignIn's wait for the refresh lock now ends when the signal
aborts. Only McpRefreshLockBusyError (coding-agent maps proper-lockfile's
ELOCKED to it) falls back to the browser step; other lock errors fail
the sign-in.
pi-mcp now has startMcpSignIn and completeMcpSignIn, so a server app can
start a sign-in in one request and complete it in another (a callback
route, a pasted redirect URL, or a loopback server via
listenForLoopbackSignIn/waitForLoopbackSignIn), plus createMcpAuthProvider.
Application specifics (client name, Client ID Metadata Document base URL
and listed redirect URIs) are options. coding-agent's signInMcpServer is a
thin wrapper over them.
authorizeMcp gains refreshOnly: a failed refresh no longer registers a
client or replaces a pending sign-in's PKCE verifier and state. Sign-in
refreshes with the stored client before dropping it for another redirect
URI.
oauth.clientRegistration defaults to auto: pi's Client ID Metadata Document
when the authorization server supports it for public clients, otherwise
dynamic client registration. Registered clients stay in use. pi mcp add
gains --oauth-client-registration.
closes#11426
Adds oauth.clientRegistration: "cimd", which identifies pi with its Client ID Metadata Document on pi.dev instead of dynamic client registration, matching Codex: a shared document when the authorization server sends iss, otherwise a document and callback path specific to the MCP server. Authorization responses are only accepted on the exact redirect URI. Dynamic client registration stays the default.
closes#10302
A step-up sign-in requested only the scopes of the insufficient_scope challenge, so the new token lost the scopes granted before and other requests asked for sign-in again. Tokens now record their granted scope and step-up requests the union (SEP-2350).
The configured metadata document replaces discovery and is trusted as
configured. Authorization codes are only exchanged when the response's
iss parameter names the flow's authorization server.
closes#10172
Refreshes hold a per-server lock file from reading the tokens to saving new ones, and use tokens another process already refreshed. Rotating refresh tokens (Cloudflare) were lost when two pi processes refreshed at once, leaving the server needing sign-in. Closing a connection waits for a running refresh so shutdown does not drop rotated tokens.