fix(auth)!: a session alone never gets a code checked
The third reading found a stolen session guessing TOTP codes without end: nine wrong codes for new recovery codes, then removing an unconfirmed authenticator of its own, a success that cleared the session's count. As Proton has it, every signed-in change of how an account is secured now takes the owner's proof (OwnerProof): the password's SRP proof for a change's challenge and a TOTP or recovery code. Adding an authenticator once one is confirmed, removing a confirmed one, new recovery codes, a new phrase, a new password and deleting take it; only the first authenticator is confirmed with its own code alone, and removing an unconfirmed one takes nothing. A proof given where none is taken is refused, and a confirmed authenticator's code is never checked for a session alone, so no success passes a proof unchecked. A session's proofs count for it alone, before they are checked, cleared only by one accepted; the tenth wrong in a row ends the session. They never count toward the account's waits nor wait for them, so whoever knows the password no longer holds a signed-in owner out of changing it (the second finding), and a new password forgets those waits. Beginning a change counts nothing against the account (the third finding): 30 an hour per session, holding back that session alone. Deleting takes a TOTP code wherever an authenticator is confirmed, a code by mail where no second factor works (the fourth finding), which SignInWays now tells (secondFactorWorks). Signed-in calls no longer take the address: nothing there mails. wrongExistingCode goes, a wrong existing code being a wrong proof. The app follows in the next commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
70b434bb84
commit
a9459bb875
1 changed files with 1461 additions and 1026 deletions
|
|
@ -270,7 +270,9 @@ Future<Map<String, Object?>?> _answer(
|
|||
person: arguments.id('person'),
|
||||
authenticator: arguments.id('authenticator'),
|
||||
code: arguments.string('code'),
|
||||
existingCode: arguments.optionalString('existingCode'),
|
||||
proof: arguments.has('proof')
|
||||
? arguments.model('proof', OwnerProof.fromJson)
|
||||
: null,
|
||||
);
|
||||
return {'recoveryCodes': codes};
|
||||
case RegisterOperation.authenticatorsOf:
|
||||
|
|
@ -288,14 +290,16 @@ Future<Map<String, Object?>?> _answer(
|
|||
session,
|
||||
person: arguments.id('person'),
|
||||
authenticator: arguments.id('authenticator'),
|
||||
code: arguments.optionalString('code'),
|
||||
proof: arguments.has('proof')
|
||||
? arguments.model('proof', OwnerProof.fromJson)
|
||||
: null,
|
||||
);
|
||||
return const {};
|
||||
case RegisterOperation.newRecoveryCodes:
|
||||
final codes = await register.newRecoveryCodes(
|
||||
session,
|
||||
person: arguments.id('person'),
|
||||
code: arguments.string('code'),
|
||||
proof: arguments.model('proof', OwnerProof.fromJson),
|
||||
);
|
||||
return {'recoveryCodes': codes};
|
||||
case RegisterOperation.resetAuthenticator:
|
||||
|
|
@ -361,32 +365,18 @@ Future<Map<String, Object?>?> _answer(
|
|||
await register.changePassword(
|
||||
session,
|
||||
person: arguments.id('person'),
|
||||
challenge: arguments.id('challenge'),
|
||||
clientValue: arguments.bytes('clientValue'),
|
||||
clientProof: arguments.bytes('clientProof'),
|
||||
totpCode: arguments.optionalString('totpCode'),
|
||||
recoveryCode: arguments.optionalString('recoveryCode'),
|
||||
proof: arguments.model('proof', OwnerProof.fromJson),
|
||||
password: arguments.model('password', NewPassword.fromJson),
|
||||
keepSession: arguments.optionalId('keepSession'),
|
||||
host: arguments.string('host'),
|
||||
address: arguments.string('address'),
|
||||
language: arguments.string('language'),
|
||||
);
|
||||
return const {};
|
||||
case RegisterOperation.setRecoveryKeys:
|
||||
await register.setRecoveryKeys(
|
||||
session,
|
||||
person: arguments.id('person'),
|
||||
challenge: arguments.id('challenge'),
|
||||
clientValue: arguments.bytes('clientValue'),
|
||||
clientProof: arguments.bytes('clientProof'),
|
||||
totpCode: arguments.optionalString('totpCode'),
|
||||
recoveryCode: arguments.optionalString('recoveryCode'),
|
||||
proof: arguments.model('proof', OwnerProof.fromJson),
|
||||
recoveryKeys: arguments.model('recoveryKeys', WrappedKeys.fromJson),
|
||||
recoveryProofKey: arguments.bytes('recoveryProofKey'),
|
||||
host: arguments.string('host'),
|
||||
address: arguments.string('address'),
|
||||
language: arguments.string('language'),
|
||||
);
|
||||
return const {};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue