fix(auth): a member's clock running ahead of the register's is refused too

The full reading's L10: issueSessionAfter refused a member clock more than
five seconds behind the register's, but one running ahead without bound,
so sessions made on the member in the gap before a reset's moment
outlived the register's notice. Now a clock that far ahead is refused
alike (PasswordRefusal.passwordChanged: the reset is done, the device
signs in again), with a warning to set both clocks by NTP.

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 13:38:07 +02:00
commit 8e6625855a
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -1474,6 +1474,60 @@ void main() {
0,
);
});
test("refuses a clock further ahead of the register's alike, as "
'sessions made here in the gap would outlive its notice (the '
"full reading's L10)", () async {
register
..proof = PasswordProof(
account: ben,
serverProof: ByteData(32),
encryptionPublicKey: ByteData(32),
signingPublicKey: ByteData(32),
passwordKeys: WrappedKeys(
nonce: ByteData(24),
cipherText: ByteData(112),
),
needsAuthenticator: false,
)
..endedBefore = DateTime.now().toUtc().subtract(
const Duration(minutes: 1),
);
Future<int> sessionsOfBen() => ServerSideSession.db.count(
session,
where: (t) => t.authUserId.equals(ben),
);
final before = await sessionsOfBen();
await expectLater(
endpoints.accountRecovery.resetPassword(
notSignedIn(),
challenge: const Uuid().v7obj(),
signature: ByteData(64),
password: NewPassword(
salt: ByteData(16),
costs: newPasswordCosts,
verifier: ByteData(384),
passwordKeys: WrappedKeys(
nonce: ByteData(24),
cipherText: ByteData(112),
),
),
totpCode: '123456',
host: testHost,
address: 'anna@example.org',
language: 'en',
),
throwsA(
isA<PasswordRefusedException>().having(
(error) => error.reason,
'reason',
PasswordRefusal.passwordChanged,
),
),
);
// Nobody signed in: no session made.
expect(await sessionsOfBen(), before);
});
});
group('pruning', () {