CLI device-flow login

bunx @temps-sdk/cli login authenticates your terminal with a Temps server using a browser-based device flow, modeled after the OAuth 2.0 device-authorization grant. You approve the CLI in the same web UI you already use, and the CLI never sees your password.

There are only two ways to authenticate the CLI:

  • Interactive — the browser device flow described below. Credentials are always entered in the web app.
  • Headless / CI — pass a pre-minted API key with --api-key (see Headless and CI: use an API key).

There is no password prompt in the terminal. The same flow works for password accounts, SSO, and MFA: you sign in once in the browser, then approve the device.


How the browser flow works

  1. The CLI requests a device_code and a short, human-readable user_code from the server's POST /api/auth/cli/device/start endpoint.
  2. The CLI prints the approval URL and tries to open it in your default browser.
  3. You sign in to the Temps web app (if you aren't already), then approve the device. The page shows the user_code so you can confirm it matches what the CLI printed.
  4. The CLI polls POST /api/auth/cli/device/poll in the background. Once you approve, the server mints a CLI API key, the poll returns it, and the CLI stores it locally.

You see the plaintext API key exactly once — the CLI persists it to your credential store and the server never returns it again. Device codes expire after 15 minutes if you don't approve them, and the minted API key is valid for 90 days.


Log in (interactive)

The simplest case — your laptop, a real terminal, your default browser:

Log in

bunx @temps-sdk/cli login https://your-temps-server.com

You will see something like:

┌── ✨ Authorize the Temps CLI ─────────────────────────────────┐
│ Open this URL to authorize:                                   │
│   https://your-temps-server.com/cli-login/ABCD-1234           │
│                                                               │
│ If your browser doesn't open automatically, paste the code:   │
│   ABCD-1234                                                   │
│ at https://your-temps-server.com/cli-login                    │
└───────────────────────────────────────────────────────────────┘

⠋ Waiting for browser approval (code ABCD-1234)...

Your browser opens to the approval page, you confirm, and the CLI prints a welcome banner with the stored context name, key prefix, and expiry. After that, every other CLI command (temps projects list, temps deploy, and so on) uses the stored key automatically.

If you omit the URL, the CLI uses the URL from your active context or the one already in your config. Run bunx @temps-sdk/cli whoami afterward to confirm who you are logged in as.


Skip the browser with TEMPS_NO_BROWSER

By default the CLI makes a best-effort attempt to open your browser — open on macOS, xdg-open on Linux, or start on Windows. Set TEMPS_NO_BROWSER=1 to skip that auto-open attempt entirely. The CLI still prints the approval URL and code; it just won't try to launch a browser:

Log in without opening a browser

TEMPS_NO_BROWSER=1 bunx @temps-sdk/cli login https://your-temps-server.com

This is useful inside a tmux session that would otherwise spawn a stray xdg-open, or any environment where a browser launch would just print noise. The CLI also skips the browser-open automatically when CI is set or when standard output is not a TTY, so most CI runners and piped invocations need no extra configuration.


Workspaces and headless shells

The device flow doesn't care which machine the browser runs on — it only cares that someone signed in as you approves the matching user_code. That makes it work cleanly from SSH sessions, containers, and Temps workspaces, where there is no local browser:

  1. Run bunx @temps-sdk/cli login inside the remote shell.
  2. Copy the printed approval URL into a browser on your own machine.
  3. Sign in and approve the device.
  4. The CLI in the remote shell picks up the minted key and stores it.

Because the URL works from any device, you never need a browser inside the container itself.


Headless and CI: use an API key

The device flow is interactive by design — it expects a human to click Approve in a web UI. For unattended CI runs and scripts, use a long-lived API key instead:

  1. Open the Temps dashboard and go to Settings → API Keys.
  2. Create a key, scope it to the permissions your job needs, and copy the plaintext value.
  3. Store it as a CI secret (TEMPS_API_KEY, for example).
  4. Pass it to the CLI with --api-key:

Log in with an API key

bunx @temps-sdk/cli login \
  --url https://your-temps-server.com \
  --api-key "$TEMPS_API_KEY"

With --api-key, the CLI validates the key and stores it without ever opening a browser. The key inherits the role of the user who created it.


MFA and SSO

The browser flow handles MFA and SSO transparently. The web /login page prompts for your TOTP code when MFA is enabled and bounces SSO users through their identity provider. Complete the prompt in the browser before approving the device — the CLI doesn't need to know which authentication factors were involved.

For automated jobs that can't sit through an MFA prompt, generate an API key from the dashboard and use the headless path above.


Troubleshooting

The browser didn't open automatically. The CLI tried open (macOS), xdg-open (Linux), or start (Windows). If none are available — common in SSH sessions, containers, and CI runners — the URL is still printed in the box at the top of the output. Copy it into any browser, even one on a different machine.

"Authorization code expired before approval." The device_code is valid for 15 minutes from the moment you ran login. Run temps login again to get a fresh code.

"Authorization denied in the browser." You clicked Deny on the approval page. Run temps login again if that was a mistake.

The CLI polls forever after I approved in the browser. Confirm the CLI is talking to the same server you approved on. A typo in the URL means the approval succeeds in your browser but the CLI never sees it.

"Already logged in as someone@example.com". The CLI refuses to overwrite an existing context. Run temps logout first, or pass --url / --context to add a second context alongside the current one.

Was this page helpful?