Authorize CLI tools that require OAuth on a headless server — no browser needed on the server side.
You (the agent) are running on a remote VPS. The user is on a separate local machine with a browser.
This means:
localhost is NOT accessible from the user's browserhttp://127.0.0.1:PORT/callback?code=..., it will fail to load in the user's browser — that is expected and normalcurlAlways make this explicit when asking the user to authorize. Example:
"Open this URL in your browser, log in, and approve. The page will likely fail to load — that's fine. Copy the full URL from the address bar and send it to me."
Split the browser flow across two machines:
text
SERVER LOCAL MACHINE
------ -------------
1. Generate auth URL → 2. Open URL in browser
3. Log in + grant permissions
4. Copy redirect URL or code
5. Exchange for token ←
6. Store token locally
Most OAuth CLIs support this via flags like:
--no-launch-browser — gh (GitHub CLI), gcloud--remote --step 1/2 — gog (Google Workspace CLI)--manual — some generic CLIsMany CLIs store tokens in a system keyring that requires an interactive terminal session to unlock. Check the CLI's documentation for a flag or environment variable that sets the keyring password non-interactively. Set it only for the duration of the auth step — do not persist it in shell configs or agent-global environment. Prefer injecting it from a secrets manager or an ephemeral shell session.
gog supports headless auth via --remote --step 1/2. See the official gog docs for setup details.
Important: Use Desktop app OAuth client type in Google Cloud Console — not Web application. gog uses a random port for the callback, which Web clients reject with
redirect_uri_mismatch.
bash
gh auth login --hostname github.com --git-protocol https --no-launch-browser
# → prints a one-time code and a URL
# Open https://github.com/login/device locally, enter the code
# gh polls and completes automatically once you approve
bash
gcloud auth login --no-launch-browser
# → prints a URL
# Open in local browser, log in, copy the verification code shown, paste back
If a CLI supports device flow (prints a short code + URL):
Some tools (e.g. mcporter) start a local HTTP server on the server to catch the OAuth callback,
but the user's browser can't reach that address on the remote server.
How to handle it:
Start the auth command with a longer timeout so it doesn't expire while waiting for the user. Check the tool's docs for a timeout flag or environment variable.
The tool usually prints a message like: text
If the browser did not open, visit https://... manually.
Send that URL to the user.
Tell the user:
"Open this URL, log in and approve. The page will fail to load — that's normal. Copy the full URL from the address bar and send it to me."
The user sends back something like:
http://127.0.0.1:PORT/callback?code=...&state=...
Forward it to the waiting server with curl: bash
curl -s "http://127.0.0.1:PORT/callback?code=...&state=..."
The tool receives the code, exchanges it for a token, and completes authorization.
ErrorFixredirect_uri_mismatchUse Desktop app OAuth client, not Web applicationNo TTY / keyring unlock failsCheck the CLI's docs for a non-interactive keyring optionAccess blocked (testing mode)Add your email as test user in Google consent screen settingsCommands fail silentlyCheck if the CLI requires an account env var to be setToken expiredRe-run auth steps; most CLIs handle refresh automatically
--help for flags like --no-launch-browser, --remote, --manual, or --headlessssh -L port forwarding to tunnel the callback to your local machine