Overview
The device flow lets a program with no browser (a CLI, a headless agent, an MCP client) get a signed-in session. The program asks for a short code, the user approves it in a browser where they are already signed in, and the program receives a token.Server setup
Your
/device page reads user_code from the URL, shows who is asking, and posts to the authorize endpoint with the user’s session cookie:
What is enforced
- The device code is stored only as a SHA-256 hash, as is the user code index.
- Each approving user gets 5 wrong guesses per 15 minutes (
userCodeAttemptLimit,userCodeAttemptWindowSeconds); further attempts get 429. slow_downis tracked in secondary storage, raises the interval by 5 seconds each time as RFC 8628 requires, and holds across instances.- A device code can be exchanged once. A second poll gets
expired_token. - The approval endpoint requires
Content-Type: application/jsonand refuses a cross-siteOriginheader, which blocks one-click approval from another site. Add origins withtrustedOrigins. - Per-IP rate limits cover all three endpoints when you use
rateLimit()(see Rate limiting).
auth.session configured, the token is a TheAuth session token you can send as Authorization: Bearer .... To issue something else, pass issueToken(userId, { clientId, scope }).
The CLI
--server falls back to $THEAUTH_URL, then to the only server you are logged in to. --no-browser prints the code without opening a browser.
Credentials are saved with file mode 0600 in a 0700 directory:
Set
THEAUTH_CREDENTIALS_FILE to use another path. logout revokes the session on the server (best effort) and deletes the local copy. On Windows the mode bits do not apply; the file sits in your per-user profile directory.
Use it from your own CLI or MCP client
loginWithDeviceFlow handles polling, slow_down, denial and expiry, and throws a DeviceFlowError with a code (access_denied, expired_token, aborted, server_error, no_token). Pass save: false to skip the credential cache, and an AbortSignal to cancel.
Standalone module
Without the plugin, usecreateDeviceAuthModule({ verificationUri, resolveUser, issueToken, storage }) and route requests through module.handleRequest(request).