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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions