---
title: "Headless login"
description: "Authenticate an MCP client when the headless machine cannot open a browser."
---

> Documentation Index
> Fetch the complete documentation index at: https://machine-memory.jfa.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Headless login

A sandbox that cannot open a browser can still use the same GitHub allowlist. First [enable MCP](/docs/mcp/enable), then use this flow with a client that implements the Machine Memory protocol; ordinary MCP clients do not automatically use it.

1. Register a public client (`token_endpoint_auth_method=none`) at `/register` with a `redirect_uri`, such as `http://127.0.0.1/callback`. No listener, inbound connection, or tunnel is needed. Retain a random PKCE `code_verifier` on the headless machine.
2. Send an `application/x-www-form-urlencoded` `POST /device/start` with `client_id`, `redirect_uri`, `code_challenge_method=S256`, and the base64url-encoded SHA-256 `code_challenge`. Include `resource=https://<router-url>/mcp`. The response has `user_code`, `device_code`, `verification_uri`, `expires_in`, and `interval`.
3. Open `verification_uri` (`https://<router-url>/activate`), enter `user_code`, and finish the GitHub login. The page says approved. It does not show a token.
4. Poll `POST /device/poll` with form-encoded `device_code` and `client_id`, waiting at least `interval` seconds between requests. HTTP 400 `authorization_pending` means keep waiting; `slow_down` means wait at least another interval. HTTP 200 returns `{ "code": "..." }` exactly once. `access_denied`, `expired_token`, or HTTP 500 `server_error` ends the attempt.
5. Exchange that code at `POST /token` with `grant_type=authorization_code`, `code`, `client_id`, the same `redirect_uri`, `resource`, and the retained `code_verifier`. The standard OAuth provider issues the access and refresh tokens.

The session expires 10 minutes after it starts. Approval and code retrieval are single-use; if the successful poll response is lost, start a new login. Keep `device_code` and `code_verifier` private; only `user_code` belongs in the browser. Do not send `MACHINE_MEMORY_DB_TOKEN` to an MCP client. This replaces the old custom device grant at `/token`; it is not RFC 8628, and `mcp-remote --device-code` does not apply.

Deployment creates a dedicated `${oauthKvName}-devices` D1 database and applies `iac/oauth-migrations` for atomic device approvals. It does not use the project-memory database. OAuth clients and tokens remain in the provider's KV namespace. Expired device rows are removed when a new login starts, and expiry is enforced on every read and transition.

Next: [Init](/docs/mcp/init) for agent instructions, then [Tools](/docs/mcp/tools).

## If something fails

See [Troubleshooting → MCP](/troubleshooting/mcp) for OAuth and sandbox credential issues.

Source: https://machine-memory.jfa.dev/docs/mcp/headless/index.mdx
