Skip to content

Improve UX when stored credentials fail validation (login, whoami, MCP) #283

@leggetter

Description

@leggetter

Summary

When the profile already has an API key, hookdeck login only re-validates against GET /cli-auth/validate (no browser). A 401 currently surfaces as a raw API error plus a noisy ERROR log from PerformRequest. hookdeck whoami hits the same path. MCP tools map 401 to a short generic line without config path or clear recovery (hookdeck_login with reauth: true).

This issue tracks clearer user-facing messages, quieter logging for expected validate 401s, and consistent MCP recovery text—without changing the "existing key → verify first" behavior of hookdeck login (browser-always login can be a separate product decision).

Problem

  • pkg/login/client_login.go: If Profile.APIKey is set, login only calls ValidateAPIKey(). On failure, users see low-level errors and log noise.
  • pkg/cmd/whoami.go: Same validate call; same experience.
  • MCP (pkg/gateway/mcp/errors.go): 401 → "Authentication failed. Check your API key." with no file path or reauth guidance for the general case. Project listing already has extra copy in tool_projects_errors.go. hookdeck_login can report "Already authenticated" while the on-disk key is stale until the first failing API call—recovery copy should be consistent wherever 401 appears.

Proposed approach

1. Shared detection helper

In pkg/hookdeck (near APIError / IsNotFoundError):

  • IsUnauthorizedError(err error) boolerrors.As into *APIError, StatusCode == 401.

2. Quieter logs for validate-only requests

  • Add a Client flag parallel to SuppressRateLimitErrors, e.g. SuppressAuthFailureLogs.
  • Set it on the shallow copy built in clientForCLIAuthValidate() (pkg/hookdeck/auth.go).
  • In PerformRequest (pkg/hookdeck/client.go), when checkAndPrintError fails with 401 and the flag is set, log at Debug instead of Error (401 only; avoid masking unexpected 403s on other endpoints).

3. CLI: structured failure message

Add a small formatter (e.g. under pkg/login or pkg/cmd) that takes *config.Config + error:

When IsUnauthorizedError(err):

  • Explain the CLI used credentials from the config file (no secret values printed).
  • Print config file path from viper.ConfigFileUsed() when known; otherwise mention --hookdeck-config / unknown path.
  • Mention active profile (aligned with whoami's "Using profile …").
  • Next steps: confirm key in dashboard; hookdeck logout then hookdeck login or hookdeck login -i; note that with a key on disk, plain login only re-verifies.

Wire after ValidateAPIKey() fails in:

  • pkg/login/client_login.go
  • pkg/cmd/whoami.go (the GetAPIClient().ValidateAPIKey() path)

Optional follow-up: drop redundant Profile.ValidateAPIKey() in whoami if it's only an empty-key check.

4. MCP: recovery paragraph

  • Extend TranslateAPIError or add TranslateAPIErrorWithRecovery(err, configPath string) in pkg/gateway/mcp/errors.go.
  • On 401, append guidance: credentials loaded from config (include path when non-empty); invalid/revoked; use hookdeck_login with reauth: true (or logout / browser login outside MCP).
  • Thread configPath from Server.cfg / ConfigFileUsed(); centralize usage (e.g. via wrapWithTelemetry or server.toolAPIError) so every tool doesn't duplicate strings.
  • Deduplicate with listProjectsFailureMessage in tool_projects_errors.go via a shared mcpAuthRecoveryHint(configPath) (or similar).
  • Optionally tweak tool/help strings in tool_help.go / tools.go so "invalid stored key" appears next to "narrow dashboard key" where relevant.

5. Optional later (separate decision)

hookdeck_login could validate the stored key and on 401 suggest reauth: true instead of "Already authenticated." That improves MCP but adds a network round-trip on every no-arg login—track separately if not in scope.

Tests

  • IsUnauthorizedError: APIError, wrapped errors, non-matching errors.
  • Formatter: substrings / table for path, profile, logout, login.
  • MCP: TranslateAPIError / recovery with and without configPath; keep tool_projects_errors_test.go in sync if hints merge.

Files (reference)

Area Files
HTTP client pkg/hookdeck/client.go, pkg/hookdeck/auth.go
CLI New helper + pkg/login/client_login.go, pkg/cmd/whoami.go
MCP pkg/gateway/mcp/errors.go, server.go, tool_projects_errors.go, tests

README: optional one-liner under Login only if we want public docs to match.

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