Skip to content

fix(cli): show re-login guidance when token refresh is unavailable - #359

Open
SystemSculpt wants to merge 1 commit into
mainfrom
mstolarz/eng-2815-show-re-login-guidance-when-bl-token-cannot-refresh-an
Open

fix(cli): show re-login guidance when token refresh is unavailable#359
SystemSculpt wants to merge 1 commit into
mainfrom
mstolarz/eng-2815-show-re-login-guidance-when-bl-token-cannot-refresh-an

Conversation

@SystemSculpt

@SystemSculpt SystemSculpt commented Jul 29, 2026

Copy link
Copy Markdown
Member

Summary

  • load and validate local credentials before making the workspace access request in bl token
  • retrieve/refresh the token before workspace validation so expired access-only credentials return bl login <workspace> guidance without ever printing the token
  • preserve workspace validation before stdout output, and add the same login guidance when an OAuth refresh attempt fails
  • add full-command regression coverage for the expired-token/no-refresh path

Fixes ENG-2815

Verification

  • go test ./cli -count=1 -run 'Test(Token|Bearer)'
  • make test
  • make lint
  • go build ./...

Note

Reorders the bl token command logic to load and validate credentials before making the workspace API call. This ensures that expired/non-refreshable tokens produce actionable "bl login" guidance instead of cryptic API failures. Adds a login guidance message when OAuth refresh fails, and includes a subprocess-based integration test for the expired-token-without-refresh path.

Written by Mendral for commit 22c4b55.

@mendral-app

mendral-app Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

🧪 Testing Guide

What this PR addresses

When a user's access token is expired and no refresh token is available (or the refresh fails), bl token previously attempted a workspace API call that failed with a confusing error. This PR reorders the logic so credential validation happens before the API call, allowing the CLI to surface actionable guidance (Please run 'bl login <workspace>') instead of an opaque API error.

Steps to reproduce the original issue

  1. Log in to a workspace: bl login <workspace>
  2. Manually edit ~/.blaxel/config.yaml — set the access_token to an expired JWT and remove the refresh_token field entirely (simulating a session with no refresh capability).
  3. Run bl token <workspace>.
  4. Before this PR: the command fails with a workspace API error (e.g., "failed to get workspace") without telling the user how to fix it.

What to verify (expected behavior)

  1. Expired token, no refresh token: bl token <workspace> exits with code 1, prints nothing to stdout, and stderr contains bl login <workspace> guidance.
  2. Expired token, invalid refresh token: Same behavior — the error message includes both the refresh failure reason and the bl login <workspace> hint.
  3. Valid token: bl token <workspace> still prints the token to stdout as before (no regression).
  4. Inaccessible workspace: The token is never printed if workspace access validation fails (security invariant preserved).
  5. Tests pass:
    go test ./cli -count=1 -run 'Test(Token|Bearer)'
    make test
    make lint

Note

Posted by PR Testing Guide · Tag @mendral-app with feedback.

@mendral-app

mendral-app Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

✅ Linked to Linear issue ENG-2815 — status already In Progress, assigned to Michael Stolarz.

Note

Posted by Linear Issue Enforcer · Tag @mendral-app with feedback.

@mendral-app

mendral-app Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Interaction Flow Diagram

This PR reorders the bl token command's execution flow so credential validation happens before workspace API calls, ensuring expired tokens surface actionable login guidance without leaking tokens or making unnecessary network requests.

sequenceDiagram
    participant User
    participant CLI as bl token (CLI)
    participant Creds as blaxel.LoadCredentials
    participant Auth as tokenForCredentials
    participant OAuth as authHeadersForCredentials
    participant Client as blaxel.Client (API)

    User->>CLI: bl token <workspace>
    CLI->>Creds: LoadCredentials(workspace)
    alt credentials missing/invalid
        Creds-->>CLI: error
        CLI-->>User: ❌ "bl login <workspace>"
    end
    Creds-->>CLI: credentials

    CLI->>Auth: tokenForCredentials(ctx, workspace, credentials)
    Auth->>OAuth: authHeadersForCredentials(ctx, credentials, workspace)
    alt refresh fails & both tokens present
        OAuth-->>Auth: error
        Auth-->>CLI: ❌ "bl login <workspace>"
        CLI-->>User: exit 1 + login guidance (no token printed)
    end
    alt token expired & no refresh token
        OAuth-->>Auth: error (expired)
        Auth-->>CLI: ❌ "bl login <workspace>"
        CLI-->>User: exit 1 + login guidance
    end
    OAuth-->>Auth: headers with valid token
    Auth-->>CLI: token

    CLI->>Client: Workspaces.Get(workspace)
    alt workspace inaccessible
        Client-->>CLI: error
        CLI-->>User: ❌ workspace error (token not printed)
    end
    Client-->>CLI: ok

    CLI-->>User: ✅ print token to stdout
Loading

Summary of the new flow (before → after)

Step Before (old) After (this PR)
1 Workspace API call (validates access + refreshes token) Load local credentials
2 Load credentials Retrieve/refresh token locally — fail fast with bl login guidance
3 Retrieve token Workspace API call (validates access)
4 Print token Print token

Key improvement: Expired or non-refreshable tokens now fail with clear re-login guidance before any API call is made, and tokens are never printed for inaccessible workspaces.

Note

Posted by PR Sequence Diagram · Tag @mendral-app with feedback.

@mendral-app mendral-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

The reordering is sound: credentials are loaded first, token retrieval/refresh happens next (surfacing login guidance on failure), then the workspace access check gates output. The two error paths (refresh-failed with both tokens at line 131, expired-without-refresh at line 142) both produce actionable guidance correctly. The subprocess test pattern is standard Go practice for testing os.Exit paths.

Tag @mendral-app with feedback or questions. View session

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant