feat(mcp): add local accounts to yt mcp serve #121
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/YT-67-local-accounts"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
yt mcp servehad no identity of its own: a user was a Hub user of the one configured instance (users.hub_user_id TEXT NOT NULL UNIQUE), and Hub OAuth was the only way to become one. Per-user YouTrack instances cannot exist until a person can be authenticated without a YouTrack instance, so local accounts are the prerequisite for the rest of the epic.Adds an
accountstable alongsideusersrather than extending it:usersmeans "a Hub user" and is keyed on a non-null Hub id, which cannot represent an account with no Hub identity. Passwords are argon2id (crate defaults, per-hash salt in the PHC string); the TOTP secret is encrypted at rest with the existingCryptounderYOUTRACK_MCP_MASTER_KEY, because a TOTP secret is a credential and must not sit in cleartext beside the password hash.The routes are
GET/POST /signup,GET/POST /login,POST /logout, andGET/POST /totp/enrol//totp/verify, server-rendered with askama next to the existing pages.GET /loginnow renders the local sign-in form and keeps the Hub button beside it (the Hub flow and the allowlist are unchanged); once signed in, the same route is the account page. TOTP is RFC 6238 (SHA-1, 6 digits, 30s step, +/-1 step of skew) with single-use recovery codes stored as SHA-256 hashes. Replay is refused by a per-account high-water mark (accounts.totp_last_step): a code is accepted only for a strictly greater step, so a code captured inside its own window cannot be used twice.Sessions reuse the existing signed cookie rather than inventing a second mechanism.
BrowserSessiongainsaccount_id(authenticated) andpending_account_id(password verified, second factor still owed), and its Hub fields become optional so a local session carries no Hub state. That is whyhub_callbacknow refuses a session with no state in flight rather than comparing against a default: an empty state would have matched an attacker-supplied emptystate.YOUTRACK_MCP_ADMIN_EMAILis the first-admin bootstrap for local accounts, the counterpart of the Hub-keyedYOUTRACK_MCP_ADMIN_HUB_LOGIN(which stays for the Hub flow). Abannedaccount cannot sign in, and the status is re-checked on every request, so sessions issued before a ban stop working rather than outliving it.Login throttling, lockout, and bot protection are NOT here: they are tracked in #YT-65 and are required before this surface faces the internet. Password auth is online-guessable in a way Hub-backed auth was not;
docs/deployment.mdsays so at the top of the new section.#YT-67
yt mcp serve