Skip to content

🩹 fix(auth): distinguish 'stored but unreadable' credentials from 'none' (#746)#747

Merged
ryancheley merged 1 commit into
mainfrom
fix-auth-unreadable-diagnostic-746
Jul 10, 2026
Merged

🩹 fix(auth): distinguish 'stored but unreadable' credentials from 'none' (#746)#747
ryancheley merged 1 commit into
mainfrom
fix-auth-unreadable-diagnostic-746

Conversation

@ryancheley

Copy link
Copy Markdown
Owner

Closes #746.

Not a 0.24.5 regression (verified)

The report attributed a credential-read break to the 0.24.5 security work. The diff disproves that:

  • security.py, config.py, models.py (all credential storage/read code) are byte-identical to 0.24.4.
  • The auth.py change in 0.24.5 is only the http:// warning in verify_credentials() — a different method, not on the load_credentials() path.
  • uv.lock changed only the youtrack-cli version; no dependency (keyring/pydantic/cryptography) moved.
  • An in-process keyring store→load round-trip on 0.24.5 reads the token back correctly.

Actual cause: OS keychain behavior — Keychain items are access-bound to the binary that created them, so uv tool upgrade (which rebuilds the tool venv with a new python path) can cause the OS to deny the new binary access to the saved token. keyring.get_password then returns None, load_credentials() returns None, and you see AUTH_004. Re-running yt auth login re-stores under the new binary. This is independent of 0.24.5's contents.

What this PR does (the actionable part of #746)

Adds AuthManager.credentials_stored_but_unreadable() and uses it in yt auth status so the "credentials present but unreadable" case is no longer misreported as "no credentials found":

⚠ Credentials are stored in the system keyring but can't be read.
This can happen after upgrading or moving the CLI — the OS keychain may no
longer grant the new binary access to the saved token.
Run 'yt auth login' to re-store your credentials.

Detection: config records a keyring-backed login (YOUTRACK_API_KEY=[Stored in keyring] + base URL) but load_credentials() returns None.

Tests

tests/test_auth.py::TestCredentialsStoredButUnreadable — flags the placeholder+unreadable case, and does not flag readable creds or a clean/no-creds config. Full suite: 1402 passed.

🤖 Generated with Claude Code

…ne" (#746)

`yt auth status` reported "No authentication credentials found" when the config
recorded a keyring-backed login (`YOUTRACK_API_KEY=[Stored in keyring]` + base
URL) but the token could no longer be read from the OS keyring — e.g. after an
upgrade/move relocated the CLI binary and the OS keychain stopped granting the
new binary access. That misleads users into thinking they were never logged in.

Add `AuthManager.credentials_stored_but_unreadable()` and use it in `auth status`
to show an actionable message ("stored but can't be read … run `yt auth login`
to re-store") instead of the generic no-credentials error.

Not a regression from 0.24.5: the credential store/read path (security.py,
config.py, models.py) is byte-identical to 0.24.4 and no dependency changed;
verified by an in-process keyring round-trip. This is a diagnostics improvement
for the underlying OS-keychain behavior reported in #746.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0166iS8CRLfRiyqCBaYtRDaR
@github-actions

Copy link
Copy Markdown

Profile summary:

GitHub user: ryancheley
🟢 No concerns found with user's profile.
🟢 No concerns found with recent PR activity.
🟢 No concerns found with recent issue activity.

For a more detailed report, run `gh-profiler ryancheley`.

@ryancheley
ryancheley merged commit c62eb2c into main Jul 10, 2026
14 checks passed
@ryancheley
ryancheley deleted the fix-auth-unreadable-diagnostic-746 branch July 10, 2026 03:35
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.

Upgrading to 0.24.5 silently invalidates existing authentication (AUTH_004 until re-login)

1 participant