Skip to content

fix(auth): verify credentials and classify authentication failures - #50

Open
jzeng-bw wants to merge 1 commit into
mainfrom
fix/auth-verification
Open

fix(auth): verify credentials and classify authentication failures#50
jzeng-bw wants to merge 1 commit into
mainfrom
fix/auth-verification

Conversation

@jzeng-bw

@jzeng-bw jzeng-bw commented Sep 9, 2026

Copy link
Copy Markdown

Fixes #24.

What was wrong

band auth status --plain reported authenticated: true whenever the keychain lookup succeeded, even if the client secret had been revoked. The first real API command then failed during token exchange with exit 1, raw OAuth response text, and a full usage block. Scripts could neither trust the status check nor reliably recognize the re-authentication failure.

What this changes

Verified status. band auth status performs a fresh token exchange by default. Verification bypasses the token cache, because a token can outlive the secret that minted it. On success, the command reports fresh JWT accounts, roles, capabilities, and Build type without updating saved configuration or changing the selected account. Verification does not require an account ID and follows the same environment/endpoint selection as API commands.

The existing JSON fields remain, with two additions:

{
  "authenticated": true,
  "credentials_stored": true,
  "token": { "status": "valid", "expires_in": 3599 }
}

credentials_stored means the configured client ID and a non-empty keychain secret are available. It does not assert that they work. authenticated becomes true only after successful verification and JWT parsing.

Result Token status / reason Exit
Verification succeeds valid, with expires_in 0
Credentials rejected: HTTP 401 or HTTP 400 invalid_client rejected / recognized OAuth code (otherwise token_endpoint_error) 2
Network, cancellation, malformed response/JWT, environment, or server failure unknown / probe_failed 1
Token endpoint returns 429 unknown / probe_failed 7
No client ID unknown / not_logged_in 2
Missing, empty, or inaccessible keychain secret unknown / credentials_unavailable 2
Explicit offline inspection with credentials present unknown / not_verified 0

Migration / offline mode. Status previously meant stored state and performed no token exchange. Scripts that need that check should use band auth status --no-verify --plain and inspect credentials_stored. In offline mode, authenticated remains false; missing credentials are reported with the corresponding reason but exit 0. Config/output errors still fail. The background update checker is also skipped, so this mode does not make network requests. Default verification prints its structured result before returning a verification error; human-readable status and remediation go to stderr.

Typed auth errors throughout the CLI. Token exchange now preserves a typed error carrying HTTP status, a recognized OAuth code, and the profile name. Rejected credentials map to exit 2 through the existing API wrapping chain, with guidance such as band auth login --profile admin. Missing stored credentials also map to exit 2. Raw OAuth error bodies and unrecognized error strings are discarded, preventing server-echoed secrets from reaching diagnostics. Token 429 and 5xx remain distinguishable from rejection. Existing resource-API 403 behavior is unchanged.

Runtime error output. The execution boundary renders errors once, suppresses usage for runtime failures, and unwraps auth diagnostics for readable remediation. Usage remains available for argument, unknown-flag, required-flag, and typed FlagError validation failures. Entry into RunE is tracked because Cobra checks required flags after pre-run hooks. Wrappers and rendering settings are restored after execution so repeated invocations do not retain suppression state.

Cancellation. Login, status verification, and API token acquisition now pass the command context into the token request. This closes the token-exchange context gap left by #48 while retaining the 15-second HTTP timeout and cached-token behavior for ordinary API requests.

Scope and compatibility

  • Successful credential verification does not establish authorization for every endpoint or the selected account. SIP and 10DLC account settings still require their separate probes.
  • README.md, AGENTS.md, and SIP status help describe the new verification/offline distinction and migration path.
  • shellescape was already in the module graph; it is promoted to a direct dependency at the same version to quote profile names in remediation commands.
  • This branch starts from main and is independent of the open SIP fix in fix(sip): resolve short realm names through realm list #49.

Tests

  • TestStatusVerification: valid and rejected credentials, OAuth 400 rejection, 403/429/500 distinctions, malformed responses/JWTs, offline inspection, missing credentials, cancellation, and connection failure. Checks stdout JSON, absence of secrets, fresh claims, request counts, and unchanged saved config.
  • TestTokenVerificationRejectsRevokedSecretDespiteCachedToken: a cached token does not turn fresh credential rejection into success.
  • TestTokenContextCancelsExchange: cancellation aborts an in-flight token request.
  • TestTokenErrorDropsUnknownServerText: unrecognized provider text is not printed.
  • TestExecuteOAuthFailure: token failures travel through a real API client and CLI execution boundary; asserts exit codes, profile remediation, no resource request, no raw secret, and no usage dump.
  • TestExecuteUsageAndRepeatedInvocations: validation still prints usage after a prior runtime error, and execution settings are restored.
  • Environment-selection coverage includes production/test, explicit overrides, custom endpoints, and invalid environment/URL inputs. Existing capability consistency tests now explicitly exercise offline inspection.

Verified with Go 1.26.6: full go test ./..., go vet ./..., CLI build, focused race tests for auth/commands/API/cmdutil, golangci-lint 2.13.2 (zero issues), formatting, and git diff --check. Live credentials were not used.

This PR was substantially AI-generated with Codex.

@jzeng-bw
jzeng-bw requested review from a team as code owners September 9, 2026 14:49
@bwappsec

bwappsec commented Sep 9, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

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.

band auth status reports authenticated: true for invalid credentials; token-exchange 401 exits 1 instead of 2

2 participants