docs(mcp): stop calling a secret rotation a refresh-token wipe #111

Merged
Claude-Run merged 2 commits from feat/YT-52-hub-reauth-command into main 2026-07-17 01:36:36 +02:00
Member

The reset section claimed a Hub client-secret rotation "kills every user's refresh token at once", which contradicts the rotation runbook added alongside it: the refresh grant authenticates by client_id, so Hub generally honors existing refresh tokens across a rotation, and yt mcp hub reauth --all re-mints from them rather than sending everyone through a login. Leaving both statements in the same document would have the operator reach for the reset lever (a re-login for every user) where the reauth lever costs nothing. Point the rotation case at the runbook and leave reset described as what it is: the lever for a wedged row or a grant to throw away deliberately.

#YT-52

The reset section claimed a Hub client-secret rotation "kills every user's refresh token at once", which contradicts the rotation runbook added alongside it: the refresh grant authenticates by `client_id`, so Hub generally honors existing refresh tokens across a rotation, and `yt mcp hub reauth --all` re-mints from them rather than sending everyone through a login. Leaving both statements in the same document would have the operator reach for the reset lever (a re-login for every user) where the reauth lever costs nothing. Point the rotation case at the runbook and leave reset described as what it is: the lever for a wedged row or a grant to throw away deliberately. #YT-52
Rotating the Hub client secret had no supported procedure. The secret is read once at startup and used only to authenticate token exchanges; nothing records which secret minted a `hub_tokens` row and nothing re-validates a stored token against current config. So a rotation invalidates the access tokens Hub already issued while every stored row keeps its original `expires_at`: the rows still look live, nothing re-mints them, and every YouTrack call 401s until the clock happens to catch up. Restarting picks up the new secret and changes none of that.

`yt mcp hub reauth [--user <login>|--all]` is the lever that closes that window. It forces `refresh_hub_token` for the selected users regardless of the recorded expiry (the expiry is exactly what a rotation makes a lie) and reports each user's outcome: refreshed, cleared, or failed with the reason. The refresh grant authenticates by `client_id`, so Hub generally honors an existing refresh token across a rotation and the re-mint succeeds; a user Hub refuses with `invalid_grant` has their dead row cleared instead, reusing YT-51's classification, so their next `/authorize` re-mints via the fall-through YT-51 added. That degrades to a single re-login rather than a wedged row. A target is required, as with `yt mcp auth reset`: the difference between one user and everyone is the difference between one Hub round trip and every user's.

Per-user failures are outcomes, not errors: one dead grant must not abandon the rest of a `--all` run halfway, which is the state an operator can least afford mid-rotation. The command exits non-zero if any user failed, so a runbook step that half-worked reads as a failure to the shell and not only to whoever scrolls the output.

Unlike the other maintenance commands this one talks to Hub, so it needs the environment `yt mcp serve` reads (the `YOUTRACK_MCP_HUB_*` client settings, plus `YOUTRACK_MCP_MASTER_KEY` to decrypt the stored refresh tokens). It edits the state DB directly, so it works while the service is running (YT-49's busy timeout) and needs no admin session. docs/deployment.md gains the rotation runbook in order (rotate in Hub, update the env, restart, `reauth --all`, verify), the env-loading incantation for both deployment shapes, and the two failure modes: `invalid_client` means the env and Hub disagree, `invalid_grant` means those users sign in again.

Dual-secret rotation windows were rejected as meaningful config and state surface for a window that is mostly theoretical given refresh tokens survive; fingerprinting the minting secret per row was rejected as a schema migration that only detects a rotation the operator already knows they performed.

#YT-52
docs(mcp): stop calling a secret rotation a refresh-token wipe
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 41s
Create release / Create release from merged PR (pull_request) Has been skipped
706ca7d2b9
The reset section claimed a Hub client-secret rotation "kills every user's refresh token at once", which contradicts the rotation runbook added alongside it: the refresh grant authenticates by `client_id`, so Hub generally honors existing refresh tokens across a rotation, and `yt mcp hub reauth --all` re-mints from them rather than sending everyone through a login. Leaving both statements in the same document would have the operator reach for the reset lever (a re-login for every user) where the reauth lever costs nothing. Point the rotation case at the runbook and leave reset described as what it is: the lever for a wedged row or a grant to throw away deliberately.

#YT-52
Claude-Run deleted branch feat/YT-52-hub-reauth-command 2026-07-17 01:36:36 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
pandoras-box/youtrack-cli!111
No description provided.