Skip to content

brev login --token session cannot be refreshed; website should hand out a refresh credential, not a raw access JWT #360

@brycelelbach

Description

@brycelelbach

Summary

The brev login --token $TOKEN flow is the path agents and scripts use (the snippet copy-pasted from https://brev.nvidia.com/settings/cli). Today the token served by that page is, in practice, a raw access JWT. Because it has no accompanying refresh material, the resulting CLI session expires roughly every time NVIDIA KAS rolls the access token — which is often within the hour — and the user is forced to re-run brev login --token from scratch.

This issue tracks the backend / frontend change required to give that flow a refreshable credential. The CLI-side workaround (stop writing a bogus "auto-login" refresh token and add 401 retry) is already in #359, but that PR cannot extend session lifetime on its own — the CLI has nothing to refresh with.

How the interactive brev login flow already solves this

pkg/auth/kas.go DoDeviceAuthFlow (interactive browser login) does the following:

  1. Generates a random deviceID (UUID) and POSTs to /device/login with the user's email.
  2. KAS returns a sessionKey.
  3. The user completes SSO in the browser.
  4. The CLI polls /token with Authorization: Bearer $sessionKey and x-device-id: $deviceID until it gets a signed ID token.
  5. The CLI stores AccessToken = idToken, RefreshToken = "$sessionKey:$deviceID".

The sessionKey:deviceID pair is already a working refresh credentialKasAuthenticator.GetNewAuthTokensWithRefresh re-calls /token whenever the access JWT expires, and the session key itself is good for up to 24 hours (see comment at pkg/auth/kas.go:207).

So the refresh machinery on the CLI side and on the KAS side is already in place. The --token path just isn't using it.

What needs to change

The page at https://brev.nvidia.com/settings/cli should issue a refresh credential, not a raw access JWT. Two options, smallest first:

Option A (preferred, smallest server change): serve the sessionKey:deviceID pair directly.

  • Server-side: mint a sessionKey bound to a freshly-generated deviceID (same shape as the interactive device flow produces) and show the string sessionKey:deviceID as the --token value in the copy/paste snippet.
  • Client-side: no changes required. LoginWithToken already has an else branch that correctly treats a non-JWT input as a refresh token and drives the first API call through the refresh path.
  • Result: brev login --token $TOKEN produces a 24-hour CLI session that auto-renews its access JWT every time it expires, matching the UX of the interactive browser flow.

Option B (slightly larger, more extensible): serve a base64-encoded JSON blob like {"refresh_token": "sessionKey:deviceID", "version": 1}.

  • Gives future headroom to pass additional metadata (e.g. token expiry, scopes, CLI-friendly capabilities) without another format change.
  • Requires a small CLI-side change: detect the base64/JSON envelope in LoginWithToken and unpack it before handing to the refresh path. Backward-compatible — old plain-string tokens still work.

Option C (largest, most durable): introduce a CLI-scoped PAT-style token.

  • Server-side: new endpoint that issues a long-lived (30–90 day) rotating refresh token scoped to CLI use, separate from the 24-hour KAS sessionKey.
  • Client-side: CLI saves {access_token, refresh_token, refresh_token_exp} and rotates on each refresh.
  • Best UX for agents / long-running automation; largest coordination cost.

Recommendation

Ship Option A first. It's the smallest change that solves the reported user pain ("I get logged out multiple times per hour"), it reuses the refresh path that the interactive flow has relied on for a long time, and it requires no CLI release — users benefit as soon as the webpage is updated.

Option C remains worth doing later for truly long-lived automation, but it's not a prerequisite for fixing the immediate logout problem.

Verifying the current behavior

To confirm the webpage is currently handing out a raw JWT (not a sessionKey:deviceID):

  1. Copy the brev login --token $TOKEN snippet from https://brev.nvidia.com/settings/cli.
  2. Inspect $TOKEN — if it has three base64 segments separated by ., it's a JWT.
  3. Cross-check against pkg/auth/auth.go LoginWithToken: a JWT input takes the if valid branch (which is the broken path fixed in fix(auth): refresh on 401 and stop writing bogus refresh tokens #359), while a sessionKey:deviceID input takes the else branch (the working path).

Out of scope for this issue

  • Whether KAS should also increase access-token TTL (it shouldn't need to; refresh handles it).
  • Whether the CLI should persist token expiry metadata locally (tracked separately as Step 3 of the larger plan).

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions