fix(auth): adding an authenticator takes a code from one confirmed before
Enrolling and confirming an authenticator took only a session, so whoever held one (a stolen token) could add a second factor of their own, then use its codes to remove the owner's and make new recovery codes. While the account has a confirmed authenticator, confirming another now also takes a code from one of those (`existingCode`, each step's code once as signing in takes it, within the same 10 tries an hour), refused as `wrongExistingCode`; it is checked only once the new one's code is right, so a wrong one uses none. The first authenticator, and the first after a reset carried out, need none. Members forward it to the register. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
d9039d36ef
commit
892fba97e8
2 changed files with 161 additions and 12 deletions
|
|
@ -393,18 +393,21 @@ class EndpointAuthenticator extends _isc.EndpointRef {
|
|||
},
|
||||
);
|
||||
|
||||
/// Confirms [authenticator] with a [code] from it; the account's first
|
||||
/// returns its recovery codes, shown once. Refusals as
|
||||
/// Confirms [authenticator] with a [code] from it, and, where the account
|
||||
/// has a confirmed authenticator already, an [existingCode] from that one;
|
||||
/// the account's first returns its recovery codes, shown once. Refusals as
|
||||
/// `confirmAuthenticator`'s.
|
||||
_ida.Future<List<String>> confirm({
|
||||
required _isc.UuidValue authenticator,
|
||||
required String code,
|
||||
String? existingCode,
|
||||
}) => caller.callServerEndpoint<List<String>>(
|
||||
'halkyon_base.authenticator',
|
||||
'confirm',
|
||||
{
|
||||
'authenticator': authenticator,
|
||||
'code': code,
|
||||
'existingCode': existingCode,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,12 @@ enum PasswordRefusal implements _isc.SerializableModel {
|
|||
notDue,
|
||||
|
||||
/// No such reset is waiting, or the address isn't the account's.
|
||||
noReset;
|
||||
noReset,
|
||||
|
||||
/// Adding an authenticator to an account that has one confirmed takes a
|
||||
/// code from one of those, which is missing or isn't right: a session
|
||||
/// alone adds no second factor.
|
||||
wrongExistingCode;
|
||||
|
||||
static PasswordRefusal fromJson(String name) {
|
||||
switch (name) {
|
||||
|
|
@ -105,6 +110,8 @@ enum PasswordRefusal implements _isc.SerializableModel {
|
|||
return PasswordRefusal.notDue;
|
||||
case 'noReset':
|
||||
return PasswordRefusal.noReset;
|
||||
case 'wrongExistingCode':
|
||||
return PasswordRefusal.wrongExistingCode;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'Value "$name" cannot be converted to "PasswordRefusal"',
|
||||
|
|
|
|||
Loading…
Reference in a new issue