fix(auth): a member's clock off the register's ends the sessions of its gap, waiting for nothing

The reading of the full reading's fixes found (C1) that refusing a member
whose clock ran ahead protected only the reset's own session: a session
made on that member in the gap, by whoever knew the old password, outlived
both the member's ending and the register's notice.

issueSessionAfter now ends every session of the account here made before
the register's moment or before now, whichever is later, then makes the
new one, neither waiting nor refusing; and the register sends its clock
with each notice (sentAt), so a member takes the moment by its own clock
(as long before now as it was before it was sent), ending the gap's
sessions and leaving those made since. The passwordChanged refusal, sent
only by the clock checks, goes with them (unreleased).

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-21 15:56:43 +02:00
commit b0e1557153
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -84,12 +84,6 @@ enum PasswordRefusal implements _isc.SerializableModel {
/// sign in again.
sessionEnded,
/// The password was changed (a reset carried out), but this service
/// couldn't open a session for it yet (its clock is behind the
/// register's): sign in again with the new password. Never "this
/// instance has no password sign-in" (the re-review's tenth finding).
passwordChanged,
/// A first password for an account that signs in with GitHub alone takes
/// a GitHub sign-in of the last 10 minutes (the fourth reading: a session
/// alone sets none): sign in with GitHub again, then set it.
@ -141,8 +135,6 @@ enum PasswordRefusal implements _isc.SerializableModel {
return PasswordRefusal.codesWait;
case 'sessionEnded':
return PasswordRefusal.sessionEnded;
case 'passwordChanged':
return PasswordRefusal.passwordChanged;
case 'signInAgain':
return PasswordRefusal.signInAgain;
case 'signedInBefore':