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:
parent
274b796faa
commit
b0e1557153
1 changed files with 180 additions and 176 deletions
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:clock/clock.dart';
|
||||
import 'package:halkyon_base_server/src/auth/account.dart';
|
||||
import 'package:halkyon_base_server/src/generated/protocol.dart';
|
||||
import 'package:halkyon_base_server/src/halkyon_base.dart';
|
||||
|
|
@ -472,11 +473,13 @@ Future<InstanceAnswer> answerSessionsEnded(
|
|||
}
|
||||
final UuidValue account;
|
||||
final DateTime endedBefore;
|
||||
final DateTime? sentAt;
|
||||
final UuidValue? keepSession;
|
||||
try {
|
||||
final read = _Arguments(arguments);
|
||||
account = read.id('account');
|
||||
endedBefore = DateTime.parse(read.string('endedBefore')).toUtc();
|
||||
sentAt = read.optionalTime('sentAt');
|
||||
keepSession = read.optionalId('keepSession');
|
||||
} on FormatException {
|
||||
return _unreadable;
|
||||
|
|
@ -484,12 +487,26 @@ Future<InstanceAnswer> answerSessionsEnded(
|
|||
await endSessionsHere(
|
||||
session,
|
||||
account,
|
||||
endedBefore: endedBefore,
|
||||
endedBefore: _byThisClock(endedBefore, sentAt: sentAt),
|
||||
keepSession: keepSession,
|
||||
);
|
||||
return (status: 200, body: const <String, Object?>{});
|
||||
}
|
||||
|
||||
/// The register's moment [endedBefore] by this server's clock: as long
|
||||
/// before now as it was before the register sent it ([sentAt], by the
|
||||
/// register's clock), so the clocks' offset drops out (the reading of the
|
||||
/// fixes' C1; one way's travel makes it a moment later, ending a session
|
||||
/// made in that instant too). A moment sent before it was taken (a clock set
|
||||
/// back) is taken as now; without [sentAt] (an older register), as sent.
|
||||
DateTime _byThisClock(DateTime endedBefore, {required DateTime? sentAt}) {
|
||||
if (sentAt == null) {
|
||||
return endedBefore;
|
||||
}
|
||||
final age = sentAt.difference(endedBefore);
|
||||
return clock.now().toUtc().subtract(age.isNegative ? Duration.zero : age);
|
||||
}
|
||||
|
||||
/// A request's arguments, each read as what it must be; a [FormatException]
|
||||
/// for anything else.
|
||||
extension type const _Arguments(Map<String, Object?> _map) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue