fix(auth): a password set up for good stays, through resets too

`password.set` replaced a password whenever the account had no confirmed
authenticator, which is also the state an authenticator's or the
account's reset leaves behind; and in that state a code by mail or GitHub
signed in again. So whoever got a session another way could replace the
password and the keys, skipping the reset's 72 hours.

An account had a confirmed authenticator while it has one or a reset of it
stands (`hadConfirmedAuthenticator`: the reset's row stays until a new
authenticator is confirmed). Then `password.set` is refused
(`passwordExists`), and the account signs in with its password alone:
codes by mail and GitHub are refused (`usePassword`), the latter through
`SignInWays.passwordOnly`, which members read from the register.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-20 14:16:23 +02:00
commit 4e425cb17f
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -179,7 +179,9 @@ Built on the three parts above, on the register (members forward every step thro
each) and both wrappings (24-byte nonce, 112 bytes). It needs an address: the
caller's, or the one a mailed code proves, which becomes theirs as email sign-in makes
it (anonymous → `identified`, same ID). It signs in once an authenticator is
confirmed; until then a new setup replaces it.
confirmed; until then a new setup replaces it, never once one was, even while a reset
leaves the account without one (a new password comes through the recovery phrase or
the account's reset alone).
- **Signing in:** `begin(address)` answers salt, costs and B; `complete(challenge, A, M1,
totpCode | recoveryCode)` answers M2, the public keys, the password's wrapping and a
session. SRP's identity I is the challenge's ID. An address nobody has (or whose
@ -203,9 +205,9 @@ Built on the three parts above, on the register (members forward every step thro
pass (a mail at the start, a sign-in calls it off), a new code carries it out: new
password, new keys, no authenticators. Either change of password ends the account's
sessions on the register's server.
- **Older ways:** an account that signs in with a password (and a confirmed
authenticator) no longer signs in by a mailed code (`usePassword`) or GitHub
(`usePassword`); where passwords are offered, no new GitHub account is linked
- **Older ways:** an account that signs in with a password (it had a confirmed
authenticator, which a reset may have taken since: `passwordOnly` in its ways in) no
longer signs in by a mailed code (`usePassword`) or GitHub (`usePassword`); where passwords are offered, no new GitHub account is linked
(`deprecated`); from `GITHUB_SIGN_IN_ENDS` on GitHub signs nobody in (`ended`) and
`forgetGitHubAccounts` deletes every GitHub ID.