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
|
|
@ -30,6 +30,8 @@ import 'package:halkyon_base_server/src/generated/password/key_proof_challenge.d
|
|||
as _iaysae70;
|
||||
import 'package:halkyon_base_server/src/generated/password/new_password.dart'
|
||||
as _ijye8mcv;
|
||||
import 'package:halkyon_base_server/src/generated/password/owner_proof.dart'
|
||||
as _ifhod73s;
|
||||
import 'package:halkyon_base_server/src/generated/password/password_challenge.dart'
|
||||
as _isz0atlx;
|
||||
import 'package:halkyon_base_server/src/generated/password/password_setup.dart'
|
||||
|
|
@ -830,7 +832,7 @@ class _AuthenticatorEndpoint {
|
|||
_ist.TestSessionBuilder sessionBuilder, {
|
||||
required _is.UuidValue authenticator,
|
||||
required String code,
|
||||
String? existingCode,
|
||||
_ifhod73s.OwnerProof? proof,
|
||||
}) async {
|
||||
return _ist.callAwaitableFunctionAndHandleExceptions(() async {
|
||||
var _localUniqueSession =
|
||||
|
|
@ -846,7 +848,7 @@ class _AuthenticatorEndpoint {
|
|||
parameters: _ist.testObjectToJson({
|
||||
'authenticator': authenticator,
|
||||
'code': code,
|
||||
'existingCode': existingCode,
|
||||
'proof': proof,
|
||||
}),
|
||||
serializationManager: _serializationManager,
|
||||
);
|
||||
|
|
@ -896,7 +898,7 @@ class _AuthenticatorEndpoint {
|
|||
_ida.Future<void> remove(
|
||||
_ist.TestSessionBuilder sessionBuilder, {
|
||||
required _is.UuidValue authenticator,
|
||||
String? code,
|
||||
_ifhod73s.OwnerProof? proof,
|
||||
}) async {
|
||||
return _ist.callAwaitableFunctionAndHandleExceptions(() async {
|
||||
var _localUniqueSession =
|
||||
|
|
@ -911,7 +913,7 @@ class _AuthenticatorEndpoint {
|
|||
methodName: 'remove',
|
||||
parameters: _ist.testObjectToJson({
|
||||
'authenticator': authenticator,
|
||||
'code': code,
|
||||
'proof': proof,
|
||||
}),
|
||||
serializationManager: _serializationManager,
|
||||
);
|
||||
|
|
@ -930,7 +932,7 @@ class _AuthenticatorEndpoint {
|
|||
|
||||
_ida.Future<List<String>> newRecoveryCodes(
|
||||
_ist.TestSessionBuilder sessionBuilder, {
|
||||
required String code,
|
||||
required _ifhod73s.OwnerProof proof,
|
||||
}) async {
|
||||
return _ist.callAwaitableFunctionAndHandleExceptions(() async {
|
||||
var _localUniqueSession =
|
||||
|
|
@ -943,7 +945,7 @@ class _AuthenticatorEndpoint {
|
|||
createSessionCallback: (_) => _localUniqueSession,
|
||||
endpointPath: 'authenticator',
|
||||
methodName: 'newRecoveryCodes',
|
||||
parameters: _ist.testObjectToJson({'code': code}),
|
||||
parameters: _ist.testObjectToJson({'proof': proof}),
|
||||
serializationManager: _serializationManager,
|
||||
);
|
||||
var _localReturnValue =
|
||||
|
|
@ -1039,15 +1041,8 @@ class _PasswordEndpoint {
|
|||
|
||||
_ida.Future<void> change(
|
||||
_ist.TestSessionBuilder sessionBuilder, {
|
||||
required _is.UuidValue challenge,
|
||||
required _idt.ByteData clientValue,
|
||||
required _idt.ByteData clientProof,
|
||||
String? totpCode,
|
||||
String? recoveryCode,
|
||||
required _ifhod73s.OwnerProof proof,
|
||||
required _ijye8mcv.NewPassword password,
|
||||
required String host,
|
||||
required String address,
|
||||
required String language,
|
||||
}) async {
|
||||
return _ist.callAwaitableFunctionAndHandleExceptions(() async {
|
||||
var _localUniqueSession =
|
||||
|
|
@ -1061,15 +1056,8 @@ class _PasswordEndpoint {
|
|||
endpointPath: 'password',
|
||||
methodName: 'change',
|
||||
parameters: _ist.testObjectToJson({
|
||||
'challenge': challenge,
|
||||
'clientValue': clientValue,
|
||||
'clientProof': clientProof,
|
||||
'totpCode': totpCode,
|
||||
'recoveryCode': recoveryCode,
|
||||
'proof': proof,
|
||||
'password': password,
|
||||
'host': host,
|
||||
'address': address,
|
||||
'language': language,
|
||||
}),
|
||||
serializationManager: _serializationManager,
|
||||
);
|
||||
|
|
@ -1088,16 +1076,9 @@ class _PasswordEndpoint {
|
|||
|
||||
_ida.Future<void> setRecoveryKeys(
|
||||
_ist.TestSessionBuilder sessionBuilder, {
|
||||
required _is.UuidValue challenge,
|
||||
required _idt.ByteData clientValue,
|
||||
required _idt.ByteData clientProof,
|
||||
String? totpCode,
|
||||
String? recoveryCode,
|
||||
required _ifhod73s.OwnerProof proof,
|
||||
required _iji0wmu8.WrappedKeys recoveryKeys,
|
||||
required _idt.ByteData recoveryProofKey,
|
||||
required String host,
|
||||
required String address,
|
||||
required String language,
|
||||
}) async {
|
||||
return _ist.callAwaitableFunctionAndHandleExceptions(() async {
|
||||
var _localUniqueSession =
|
||||
|
|
@ -1111,16 +1092,9 @@ class _PasswordEndpoint {
|
|||
endpointPath: 'password',
|
||||
methodName: 'setRecoveryKeys',
|
||||
parameters: _ist.testObjectToJson({
|
||||
'challenge': challenge,
|
||||
'clientValue': clientValue,
|
||||
'clientProof': clientProof,
|
||||
'totpCode': totpCode,
|
||||
'recoveryCode': recoveryCode,
|
||||
'proof': proof,
|
||||
'recoveryKeys': recoveryKeys,
|
||||
'recoveryProofKey': recoveryProofKey,
|
||||
'host': host,
|
||||
'address': address,
|
||||
'language': language,
|
||||
}),
|
||||
serializationManager: _serializationManager,
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue