--- name: tenant-identity-recovery description: Restore operator/admin access in systems with central identity, passkeys, tenant-local sessions, and signed handoffs; design scoped password fallbacks and verify the complete browser-to-tenant path. version: 1.0.0 tags: [authentication, recovery, passkeys, passwords, tenants, handoff, security, deployment] metadata: hermes: tags: [authentication, recovery, passkeys, passwords, tenants, handoff, security] --- # Tenant Identity Recovery Use this skill when an operator or customer cannot enter a tenant-backed application and asks for a password reset, recovery link, passkey replacement, or direct admin login. It applies to products where a **central identity service** creates a separate **tenant-local browser session**, often through a signed handoff. ## First classify the identities Before touching credentials, write down which layer owns each identity: 1. **Messaging identity** — Telegram/Discord allowlists control who may talk to an agent or tenant brain. They do not create browser sessions. 2. **Central web identity** — owns login factors, account status/type, tenant directory, and handoff authority. 3. **Tenant-local session** — accepted only after a central handoff and usually host-only. 4. **Runtime/provider identity** — model/API credentials belong to the tenant runtime and are unrelated to human web login. Do not tell the user that a bot connection means they are logged into the web admin. Do not infer that an account has a password because the user calls the problem a “password reset.” Inspect the live schema and routes first. A passkey record is not a password record. ## Choose the smallest durable recovery Prefer, in order: 1. use an existing synced passkey on the user's current device/account; 2. use the product's existing recovery or add-passkey flow; 3. issue an established one-time central recovery flow that lets the user attach a new factor; 4. when the user explicitly requests it and product policy allows, add a narrowly scoped central password fallback; 5. use an emergency direct-entry bridge only as temporary access, never as durable recovery. If the system has no password schema or route, say so plainly. A durable fallback is a feature addition, not a database “reset.” ## Central password fallback contract When a password fallback is appropriate, keep it at the central identity boundary and preserve the ordinary signed handoff into the tenant. Never re-enable a legacy tenant-local password for a tenant whose central identity is authoritative. Require all of the following: - one exact server-configured account ID; the browser cannot choose the target account; - durable account re-read requiring active status and the expected admin/operator account type; - a random high-entropy password stored only as a salted memory-hard hash such as scrypt or Argon2; - same-origin and CSRF protection before credential verification; - small request bodies, per-principal failure limits, and generic invalid-credential errors; - timing-safe digest comparison; - secure central session cookies (`HttpOnly`, `Secure`, `SameSite=Lax` or stricter as the flow permits); - reuse of the existing exact-origin, short-lived signed handoff; - no password, handoff token, session token, or hash in URLs, source, logs, launcher metadata, or shared chat. Keep an existing passkey valid unless the user separately asks to revoke it. Adding a fallback, replacing a passkey, and revoking an old factor are separate operations. ## Stable login page behavior A password login page should have a stable non-secret URL and: 1. fetch public same-origin CSRF/client configuration; 2. submit only the password to the central password-login endpoint; 3. receive a central session cookie and safe account/tenant projection; 4. call the existing authenticated handoff endpoint; 5. form-POST the returned fields to the exact HTTPS tenant action; 6. surface concise errors without exposing raw server diagnostics; 7. never put the password or handoff token in query strings or fragments. The signed central account type remains authoritative. Do not derive product admin from a legacy local tenant role, a configured password, or a stale cookie. ## Secret-safe deployment and rotation Generate the password into a permission-restricted temporary file. Compute the hash locally. Inject only the exact account ID and hash through the app's approved secret environment path. For PM2-managed services, update the environment deterministically, restart only the target process with updated env, then save PM2 state. Deliver the plaintext privately to the verified user—prefer a protected direct message rather than a group. After accepted delivery, delete plaintext and temporary hash staging. Do not print secret values while verifying configuration; report only configured/unconfigured markers. Rotating the fallback means generating a new password and replacing the stored hash. Prefer an application/operator endpoint or a managed secret store when one exists; environment-backed configuration is appropriate only for narrowly scoped operator fallback, not general customer passwords. ## Emergency bridge pitfalls A static HTML file containing a short-lived handoff token is fragile: - the running service may use another release checkout or static root; - cleanup may remove it before the user opens it; - the handoff token can expire while the URL still appears valid; - bearer paths shared in groups can be opened by the wrong person; - success restores only one tenant session, not a repeatable central login. If a bridge is unavoidable, verify the public URL before sending, deliver it privately, state its exact expiry, make it single-use, remove it afterward, and label it temporary. Never call it a password reset or durable recovery. ## End-to-end acceptance Do not stop at unit tests or a PM2 `online` row. Verify the public path: 1. login page returns `200` and identifies the intended admin surface; 2. missing or hostile origin/CSRF is rejected; 3. wrong password returns `401` and sets no session cookie; 4. correct password returns `200`, the expected account type, and a secure central cookie; 5. authenticated handoff targets the exact intended tenant origin; 6. form-POSTing the handoff succeeds; 7. tenant session reports authenticated plus the expected role/account type; 8. the service is healthy with account/hash configuration present, without printing values; 9. browser visual and JavaScript checks pass; 10. plaintext staging and obsolete recovery artifacts are gone. When the user reports a failed recovery URL, reproduce that exact public URL first, explain the failure briefly, and replace it with a verified durable path rather than another unverified bearer file. ## References - `references/caduceus-passkey-admin-password-fallback.md` — Caduceus central/passkey/tenant-handoff implementation pattern and the static-recovery failure lesson.