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
6 changed files with 1461 additions and 1026 deletions
|
|
@ -21,9 +21,11 @@ abstract class SignInWays
|
|||
bool? password,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
}) : password = password ?? false,
|
||||
authenticator = authenticator ?? false,
|
||||
passwordOnly = passwordOnly ?? false;
|
||||
passwordOnly = passwordOnly ?? false,
|
||||
secondFactorWorks = secondFactorWorks ?? false;
|
||||
|
||||
factory SignInWays({
|
||||
required bool gitHub,
|
||||
|
|
@ -31,6 +33,7 @@ abstract class SignInWays
|
|||
bool? password,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
}) = _SignInWaysImpl;
|
||||
|
||||
factory SignInWays.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
|
|
@ -46,6 +49,11 @@ abstract class SignInWays
|
|||
passwordOnly: jsonSerialization['passwordOnly'] == null
|
||||
? null
|
||||
: _isc.BoolJsonExtension.fromJson(jsonSerialization['passwordOnly']),
|
||||
secondFactorWorks: jsonSerialization['secondFactorWorks'] == null
|
||||
? null
|
||||
: _isc.BoolJsonExtension.fromJson(
|
||||
jsonSerialization['secondFactorWorks'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -64,6 +72,11 @@ abstract class SignInWays
|
|||
/// neither a code by mail nor GitHub signs into it (ADR 0017).
|
||||
bool passwordOnly;
|
||||
|
||||
/// A second factor works: a confirmed authenticator, and no reset of the
|
||||
/// authenticators that took effect. Where none does, deleting the account
|
||||
/// takes a code by mail in place of a TOTP or recovery code.
|
||||
bool secondFactorWorks;
|
||||
|
||||
/// Returns a shallow copy of this [SignInWays]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
|
|
@ -73,6 +86,7 @@ abstract class SignInWays
|
|||
bool? password,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
|
|
@ -83,6 +97,7 @@ abstract class SignInWays
|
|||
'password': password,
|
||||
'authenticator': authenticator,
|
||||
'passwordOnly': passwordOnly,
|
||||
'secondFactorWorks': secondFactorWorks,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -95,6 +110,7 @@ abstract class SignInWays
|
|||
'password': password,
|
||||
'authenticator': authenticator,
|
||||
'passwordOnly': passwordOnly,
|
||||
'secondFactorWorks': secondFactorWorks,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -111,12 +127,14 @@ class _SignInWaysImpl extends SignInWays {
|
|||
bool? password,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
}) : super._(
|
||||
gitHub: gitHub,
|
||||
email: email,
|
||||
password: password,
|
||||
authenticator: authenticator,
|
||||
passwordOnly: passwordOnly,
|
||||
secondFactorWorks: secondFactorWorks,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [SignInWays]
|
||||
|
|
@ -129,6 +147,7 @@ class _SignInWaysImpl extends SignInWays {
|
|||
bool? password,
|
||||
bool? authenticator,
|
||||
bool? passwordOnly,
|
||||
bool? secondFactorWorks,
|
||||
}) {
|
||||
return SignInWays(
|
||||
gitHub: gitHub ?? this.gitHub,
|
||||
|
|
@ -136,6 +155,7 @@ class _SignInWaysImpl extends SignInWays {
|
|||
password: password ?? this.password,
|
||||
authenticator: authenticator ?? this.authenticator,
|
||||
passwordOnly: passwordOnly ?? this.passwordOnly,
|
||||
secondFactorWorks: secondFactorWorks ?? this.secondFactorWorks,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ import 'package:halkyon_base_client/src/protocol/password/key_proof_challenge.da
|
|||
as _ir3ecgdn;
|
||||
import 'package:halkyon_base_client/src/protocol/password/new_password.dart'
|
||||
as _i95l60wq;
|
||||
import 'package:halkyon_base_client/src/protocol/password/owner_proof.dart'
|
||||
as _ikq9b2yz;
|
||||
import 'package:halkyon_base_client/src/protocol/password/password_challenge.dart'
|
||||
as _iykren7a;
|
||||
import 'package:halkyon_base_client/src/protocol/password/password_setup.dart'
|
||||
|
|
@ -343,9 +345,11 @@ class EndpointAccountRecovery extends _isc.EndpointRef {
|
|||
}
|
||||
|
||||
/// The signed-in person's authenticators and recovery codes (ADR 0017),
|
||||
/// which the instance's register keeps (ADR 0016). Every code tried here
|
||||
/// counts for the session (`asSessionCodeTry`): the tenth wrong in a row
|
||||
/// ends it ([PasswordRefusal.sessionEnded]).
|
||||
/// which the instance's register keeps (ADR 0016). Once one is confirmed,
|
||||
/// every change takes the owner's proof (`OwnerProof`: the password and a
|
||||
/// code; the third reading), which counts for the session
|
||||
/// (`asOwnerProofTry`): the tenth wrong in a row ends it
|
||||
/// ([PasswordRefusal.sessionEnded]).
|
||||
/// {@category Endpoint}
|
||||
class EndpointAuthenticator extends _isc.EndpointRef {
|
||||
EndpointAuthenticator(_isc.EndpointCaller caller) : super(caller);
|
||||
|
|
@ -369,20 +373,20 @@ class EndpointAuthenticator extends _isc.EndpointRef {
|
|||
);
|
||||
|
||||
/// 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
|
||||
/// has a confirmed authenticator already, the owner's [proof]; 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,
|
||||
_ikq9b2yz.OwnerProof? proof,
|
||||
}) => caller.callServerEndpoint<List<String>>(
|
||||
'halkyon_base.authenticator',
|
||||
'confirm',
|
||||
{
|
||||
'authenticator': authenticator,
|
||||
'code': code,
|
||||
'existingCode': existingCode,
|
||||
'proof': proof,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -394,28 +398,30 @@ class EndpointAuthenticator extends _isc.EndpointRef {
|
|||
{},
|
||||
);
|
||||
|
||||
/// Removes [authenticator]: a confirmed one with a [code] from one of the
|
||||
/// caller's, while another stays. Refusals as `removeAuthenticator`'s.
|
||||
/// Removes [authenticator]: one never confirmed as it is, a confirmed one
|
||||
/// with the owner's [proof], while another stays. Refusals as
|
||||
/// `removeAuthenticator`'s.
|
||||
_ida.Future<void> remove({
|
||||
required _isc.UuidValue authenticator,
|
||||
String? code,
|
||||
_ikq9b2yz.OwnerProof? proof,
|
||||
}) => caller.callServerEndpoint<void>(
|
||||
'halkyon_base.authenticator',
|
||||
'remove',
|
||||
{
|
||||
'authenticator': authenticator,
|
||||
'code': code,
|
||||
'proof': proof,
|
||||
},
|
||||
);
|
||||
|
||||
/// New recovery codes in place of the old, with a [code] from one of the
|
||||
/// caller's authenticators: shown once. Refusals as `newRecoveryCodes`'.
|
||||
_ida.Future<List<String>> newRecoveryCodes({required String code}) =>
|
||||
caller.callServerEndpoint<List<String>>(
|
||||
'halkyon_base.authenticator',
|
||||
'newRecoveryCodes',
|
||||
{'code': code},
|
||||
);
|
||||
/// New recovery codes in place of the old, with the owner's [proof]:
|
||||
/// shown once. Refusals as `newRecoveryCodes`'.
|
||||
_ida.Future<List<String>> newRecoveryCodes({
|
||||
required _ikq9b2yz.OwnerProof proof,
|
||||
}) => caller.callServerEndpoint<List<String>>(
|
||||
'halkyon_base.authenticator',
|
||||
'newRecoveryCodes',
|
||||
{'proof': proof},
|
||||
);
|
||||
}
|
||||
|
||||
/// Giving the signed-in person a password (ADR 0017): the way an anonymous
|
||||
|
|
@ -448,8 +454,13 @@ class EndpointPassword extends _isc.EndpointRef {
|
|||
},
|
||||
);
|
||||
|
||||
/// The first step of the caller proving their password to change it:
|
||||
/// its salt and costs, and SRP's B. Refusals as `beginPasswordChange`'s.
|
||||
/// The first step of the caller proving their password (`OwnerProof`) to
|
||||
/// change how their account is secured: its salt and costs, and SRP's B.
|
||||
/// At most [changesBegunPerSession] an hour per session, which holds back
|
||||
/// no other session, the owner's among them (the third reading's third
|
||||
/// finding); the proofs that fail count against the session too
|
||||
/// (`asOwnerProofTry`). Refusals as `beginPasswordChange`'s, and
|
||||
/// [PasswordRefusal.tooManyAttempts].
|
||||
_ida.Future<_iykren7a.PasswordChallenge> beginChange() =>
|
||||
caller.callServerEndpoint<_iykren7a.PasswordChallenge>(
|
||||
'halkyon_base.password',
|
||||
|
|
@ -458,69 +469,38 @@ class EndpointPassword extends _isc.EndpointRef {
|
|||
);
|
||||
|
||||
/// Changes the caller's password to [password] (the same keys, wrapped
|
||||
/// under the new password's key on the device), with the old password's
|
||||
/// proof for [challenge] ([beginChange]) and a [totpCode] or
|
||||
/// [recoveryCode], and the account's [address] as the device typed it
|
||||
/// (checked before the code; the owner is mailed in [language] should
|
||||
/// codes come to wait). Every other session of theirs ends, in every
|
||||
/// service of the instance; this one stays. Refusals as `changePassword`'s.
|
||||
/// under the new password's key on the device), with the owner's [proof]
|
||||
/// (the old password's proof for a challenge [beginChange] gave, and a
|
||||
/// TOTP or recovery code). Every other session of theirs ends, in every
|
||||
/// service of the instance; this one stays. The proof counts for this
|
||||
/// session (`asOwnerProofTry`). Refusals as `changePassword`'s.
|
||||
_ida.Future<void> change({
|
||||
required _isc.UuidValue challenge,
|
||||
required _idt.ByteData clientValue,
|
||||
required _idt.ByteData clientProof,
|
||||
String? totpCode,
|
||||
String? recoveryCode,
|
||||
required _ikq9b2yz.OwnerProof proof,
|
||||
required _i95l60wq.NewPassword password,
|
||||
required String host,
|
||||
required String address,
|
||||
required String language,
|
||||
}) => caller.callServerEndpoint<void>(
|
||||
'halkyon_base.password',
|
||||
'change',
|
||||
{
|
||||
'challenge': challenge,
|
||||
'clientValue': clientValue,
|
||||
'clientProof': clientProof,
|
||||
'totpCode': totpCode,
|
||||
'recoveryCode': recoveryCode,
|
||||
'proof': proof,
|
||||
'password': password,
|
||||
'host': host,
|
||||
'address': address,
|
||||
'language': language,
|
||||
},
|
||||
);
|
||||
|
||||
/// A new recovery phrase: [recoveryKeys], the caller's keys wrapped under
|
||||
/// it on the device, replace the old wrapping, and [recoveryProofKey], the
|
||||
/// public half of its proof key, the old one, with the password's proof
|
||||
/// for [challenge] ([beginChange]) and a [totpCode] or [recoveryCode], with
|
||||
/// the account's [address] and [language], as [change] takes. Refusals as
|
||||
/// `setRecoveryKeys`'.
|
||||
/// public half of its proof key, the old one, with the owner's [proof], as
|
||||
/// [change] takes. Refusals as `setRecoveryKeys`'.
|
||||
_ida.Future<void> setRecoveryKeys({
|
||||
required _isc.UuidValue challenge,
|
||||
required _idt.ByteData clientValue,
|
||||
required _idt.ByteData clientProof,
|
||||
String? totpCode,
|
||||
String? recoveryCode,
|
||||
required _ikq9b2yz.OwnerProof proof,
|
||||
required _i3so7e0p.WrappedKeys recoveryKeys,
|
||||
required _idt.ByteData recoveryProofKey,
|
||||
required String host,
|
||||
required String address,
|
||||
required String language,
|
||||
}) => caller.callServerEndpoint<void>(
|
||||
'halkyon_base.password',
|
||||
'setRecoveryKeys',
|
||||
{
|
||||
'challenge': challenge,
|
||||
'clientValue': clientValue,
|
||||
'clientProof': clientProof,
|
||||
'totpCode': totpCode,
|
||||
'recoveryCode': recoveryCode,
|
||||
'proof': proof,
|
||||
'recoveryKeys': recoveryKeys,
|
||||
'recoveryProofKey': recoveryProofKey,
|
||||
'host': host,
|
||||
'address': address,
|
||||
'language': language,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
|||
/// What deleting an account that has a password takes (ADR 0017; the second
|
||||
/// reading): the password's SRP proof for a challenge `password.beginChange`
|
||||
/// gave, a TOTP or recovery code, and the host the device addressed, as a
|
||||
/// change of password does. A session alone deletes only an account without
|
||||
/// one.
|
||||
/// change of password does (`OwnerProof`). A session alone deletes only an
|
||||
/// account without one.
|
||||
abstract class DeletionProof
|
||||
implements _isc.SerializableModel, _isc.ProtocolSerialization {
|
||||
DeletionProof._({
|
||||
|
|
@ -29,8 +29,6 @@ abstract class DeletionProof
|
|||
this.request,
|
||||
this.code,
|
||||
required this.host,
|
||||
required this.address,
|
||||
required this.language,
|
||||
});
|
||||
|
||||
factory DeletionProof({
|
||||
|
|
@ -42,8 +40,6 @@ abstract class DeletionProof
|
|||
_isc.UuidValue? request,
|
||||
String? code,
|
||||
required String host,
|
||||
required String address,
|
||||
required String language,
|
||||
}) = _DeletionProofImpl;
|
||||
|
||||
factory DeletionProof.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
|
|
@ -64,8 +60,6 @@ abstract class DeletionProof
|
|||
: _isc.UuidValueJsonExtension.fromJson(jsonSerialization['request']),
|
||||
code: jsonSerialization['code'] as String?,
|
||||
host: jsonSerialization['host'] as String,
|
||||
address: jsonSerialization['address'] as String,
|
||||
language: jsonSerialization['language'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -91,13 +85,6 @@ abstract class DeletionProof
|
|||
/// checks it is its own before the register binds the proof to it.
|
||||
String host;
|
||||
|
||||
/// The account's address as the device typed it, and the language its
|
||||
/// mails are worded in: checked before the code, so the owner is mailed
|
||||
/// should codes come to wait (the re-review's fourth finding).
|
||||
String address;
|
||||
|
||||
String language;
|
||||
|
||||
/// Returns a shallow copy of this [DeletionProof]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
|
|
@ -110,8 +97,6 @@ abstract class DeletionProof
|
|||
_isc.UuidValue? request,
|
||||
String? code,
|
||||
String? host,
|
||||
String? address,
|
||||
String? language,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
|
|
@ -125,8 +110,6 @@ abstract class DeletionProof
|
|||
if (request != null) 'request': request?.toJson(),
|
||||
if (code != null) 'code': code,
|
||||
'host': host,
|
||||
'address': address,
|
||||
'language': language,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -142,8 +125,6 @@ abstract class DeletionProof
|
|||
if (request != null) 'request': request?.toJson(),
|
||||
if (code != null) 'code': code,
|
||||
'host': host,
|
||||
'address': address,
|
||||
'language': language,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -165,8 +146,6 @@ class _DeletionProofImpl extends DeletionProof {
|
|||
_isc.UuidValue? request,
|
||||
String? code,
|
||||
required String host,
|
||||
required String address,
|
||||
required String language,
|
||||
}) : super._(
|
||||
challenge: challenge,
|
||||
clientValue: clientValue,
|
||||
|
|
@ -176,8 +155,6 @@ class _DeletionProofImpl extends DeletionProof {
|
|||
request: request,
|
||||
code: code,
|
||||
host: host,
|
||||
address: address,
|
||||
language: language,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [DeletionProof]
|
||||
|
|
@ -193,8 +170,6 @@ class _DeletionProofImpl extends DeletionProof {
|
|||
Object? request = _Undefined,
|
||||
Object? code = _Undefined,
|
||||
String? host,
|
||||
String? address,
|
||||
String? language,
|
||||
}) {
|
||||
return DeletionProof(
|
||||
challenge: challenge ?? this.challenge,
|
||||
|
|
@ -205,8 +180,6 @@ class _DeletionProofImpl extends DeletionProof {
|
|||
request: request is _isc.UuidValue? ? request : this.request,
|
||||
code: code is String? ? code : this.code,
|
||||
host: host ?? this.host,
|
||||
address: address ?? this.address,
|
||||
language: language ?? this.language,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,158 @@
|
|||
/* AUTOMATICALLY GENERATED CODE DO NOT MODIFY */
|
||||
/* To generate run: "serverpod generate" */
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: library_private_types_in_public_api
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:typed_data' as _idt;
|
||||
import 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
|
||||
/// What a signed-in owner proves to change how their account is secured
|
||||
/// (ADR 0017; the third reading, as Proton has it): the password's SRP
|
||||
/// proof for a challenge `password.beginChange` gave, bound to the host the
|
||||
/// device addressed, and a TOTP code from one of the account's confirmed
|
||||
/// authenticators or one of its recovery codes. Adding an authenticator once
|
||||
/// one is confirmed, removing a confirmed one, new recovery codes, a new
|
||||
/// recovery phrase and a new password take it: a session alone never gets a
|
||||
/// code checked.
|
||||
abstract class OwnerProof
|
||||
implements _isc.SerializableModel, _isc.ProtocolSerialization {
|
||||
OwnerProof._({
|
||||
required this.challenge,
|
||||
required this.clientValue,
|
||||
required this.clientProof,
|
||||
required this.host,
|
||||
this.totpCode,
|
||||
this.recoveryCode,
|
||||
});
|
||||
|
||||
factory OwnerProof({
|
||||
required _isc.UuidValue challenge,
|
||||
required _idt.ByteData clientValue,
|
||||
required _idt.ByteData clientProof,
|
||||
required String host,
|
||||
String? totpCode,
|
||||
String? recoveryCode,
|
||||
}) = _OwnerProofImpl;
|
||||
|
||||
factory OwnerProof.fromJson(Map<String, dynamic> jsonSerialization) {
|
||||
return OwnerProof(
|
||||
challenge: _isc.UuidValueJsonExtension.fromJson(
|
||||
jsonSerialization['challenge'],
|
||||
),
|
||||
clientValue: _isc.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['clientValue'],
|
||||
),
|
||||
clientProof: _isc.ByteDataJsonExtension.fromJson(
|
||||
jsonSerialization['clientProof'],
|
||||
),
|
||||
host: jsonSerialization['host'] as String,
|
||||
totpCode: jsonSerialization['totpCode'] as String?,
|
||||
recoveryCode: jsonSerialization['recoveryCode'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
_isc.UuidValue challenge;
|
||||
|
||||
_idt.ByteData clientValue;
|
||||
|
||||
_idt.ByteData clientProof;
|
||||
|
||||
/// The host the device addressed the instance by: the server it reached
|
||||
/// checks it is its own before the register binds the proof to it.
|
||||
String host;
|
||||
|
||||
String? totpCode;
|
||||
|
||||
String? recoveryCode;
|
||||
|
||||
/// Returns a shallow copy of this [OwnerProof]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
OwnerProof copyWith({
|
||||
_isc.UuidValue? challenge,
|
||||
_idt.ByteData? clientValue,
|
||||
_idt.ByteData? clientProof,
|
||||
String? host,
|
||||
String? totpCode,
|
||||
String? recoveryCode,
|
||||
});
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.OwnerProof',
|
||||
'challenge': challenge.toJson(),
|
||||
'clientValue': clientValue.toJson(),
|
||||
'clientProof': clientProof.toJson(),
|
||||
'host': host,
|
||||
if (totpCode != null) 'totpCode': totpCode,
|
||||
if (recoveryCode != null) 'recoveryCode': recoveryCode,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJsonForProtocol() {
|
||||
return {
|
||||
'__className__': 'halkyon_base.OwnerProof',
|
||||
'challenge': challenge.toJson(),
|
||||
'clientValue': clientValue.toJson(),
|
||||
'clientProof': clientProof.toJson(),
|
||||
'host': host,
|
||||
if (totpCode != null) 'totpCode': totpCode,
|
||||
if (recoveryCode != null) 'recoveryCode': recoveryCode,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return _isc.SerializationManager.encode(this);
|
||||
}
|
||||
}
|
||||
|
||||
class _Undefined {}
|
||||
|
||||
class _OwnerProofImpl extends OwnerProof {
|
||||
_OwnerProofImpl({
|
||||
required _isc.UuidValue challenge,
|
||||
required _idt.ByteData clientValue,
|
||||
required _idt.ByteData clientProof,
|
||||
required String host,
|
||||
String? totpCode,
|
||||
String? recoveryCode,
|
||||
}) : super._(
|
||||
challenge: challenge,
|
||||
clientValue: clientValue,
|
||||
clientProof: clientProof,
|
||||
host: host,
|
||||
totpCode: totpCode,
|
||||
recoveryCode: recoveryCode,
|
||||
);
|
||||
|
||||
/// Returns a shallow copy of this [OwnerProof]
|
||||
/// with some or all fields replaced by the given arguments.
|
||||
@_isc.useResult
|
||||
@override
|
||||
OwnerProof copyWith({
|
||||
_isc.UuidValue? challenge,
|
||||
_idt.ByteData? clientValue,
|
||||
_idt.ByteData? clientProof,
|
||||
String? host,
|
||||
Object? totpCode = _Undefined,
|
||||
Object? recoveryCode = _Undefined,
|
||||
}) {
|
||||
return OwnerProof(
|
||||
challenge: challenge ?? this.challenge,
|
||||
clientValue: clientValue ?? this.clientValue.clone(),
|
||||
clientProof: clientProof ?? this.clientProof.clone(),
|
||||
host: host ?? this.host,
|
||||
totpCode: totpCode is String? ? totpCode : this.totpCode,
|
||||
recoveryCode: recoveryCode is String? ? recoveryCode : this.recoveryCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@ enum PasswordRefusal implements _isc.SerializableModel {
|
|||
/// The account has as many authenticators as it may: remove one first.
|
||||
tooManyAuthenticators,
|
||||
|
||||
/// The code from the authenticator isn't right.
|
||||
/// The code from the authenticator being set up isn't right.
|
||||
wrongCode,
|
||||
|
||||
/// The reset's time hasn't come yet.
|
||||
|
|
@ -70,11 +70,6 @@ enum PasswordRefusal implements _isc.SerializableModel {
|
|||
/// No such reset is waiting, or the address isn't the account's.
|
||||
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,
|
||||
|
||||
/// The device addressed this instance by another name than its own
|
||||
/// (`apiServer.publicHost`): what it proves is bound to the name it
|
||||
/// addressed, so the operator must configure the name devices use.
|
||||
|
|
@ -84,8 +79,9 @@ enum PasswordRefusal implements _isc.SerializableModel {
|
|||
/// `takesEffectAt` (the second reading's M4). Only after a right password.
|
||||
codesWait,
|
||||
|
||||
/// Ten codes in a row from this signed-in session were wrong: the
|
||||
/// session ended (the re-review's third finding); sign in again.
|
||||
/// Ten proofs in a row from this signed-in session (the password with a
|
||||
/// code, `OwnerProof`) were wrong: the session ended (the third reading);
|
||||
/// sign in again.
|
||||
sessionEnded,
|
||||
|
||||
/// The password was changed (a reset carried out), but this service
|
||||
|
|
@ -128,8 +124,6 @@ enum PasswordRefusal implements _isc.SerializableModel {
|
|||
return PasswordRefusal.notDue;
|
||||
case 'noReset':
|
||||
return PasswordRefusal.noReset;
|
||||
case 'wrongExistingCode':
|
||||
return PasswordRefusal.wrongExistingCode;
|
||||
case 'otherInstance':
|
||||
return PasswordRefusal.otherInstance;
|
||||
case 'codesWait':
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import 'password/authenticator_enrolment.dart' as _iv7tra7v;
|
|||
import 'password/deletion_proof.dart' as _ix1u7hdq;
|
||||
import 'password/key_proof_challenge.dart' as _iehtx95q;
|
||||
import 'password/new_password.dart' as _itvvd6m6;
|
||||
import 'password/owner_proof.dart' as _i5f4sdg4;
|
||||
import 'password/password_challenge.dart' as _iuxisac7;
|
||||
import 'password/password_costs.dart' as _izp492e5;
|
||||
import 'password/password_refusal.dart' as _ibfrwdhm;
|
||||
|
|
@ -57,6 +58,7 @@ export 'password/authenticator_enrolment.dart';
|
|||
export 'password/deletion_proof.dart';
|
||||
export 'password/key_proof_challenge.dart';
|
||||
export 'password/new_password.dart';
|
||||
export 'password/owner_proof.dart';
|
||||
export 'password/password_challenge.dart';
|
||||
export 'password/password_costs.dart';
|
||||
export 'password/password_refusal.dart';
|
||||
|
|
@ -156,6 +158,9 @@ class Protocol extends _isc.SerializationManager {
|
|||
if (t == _itvvd6m6.NewPassword) {
|
||||
return _itvvd6m6.NewPassword.fromJson(data) as T;
|
||||
}
|
||||
if (t == _i5f4sdg4.OwnerProof) {
|
||||
return _i5f4sdg4.OwnerProof.fromJson(data) as T;
|
||||
}
|
||||
if (t == _iuxisac7.PasswordChallenge) {
|
||||
return _iuxisac7.PasswordChallenge.fromJson(data) as T;
|
||||
}
|
||||
|
|
@ -253,6 +258,9 @@ class Protocol extends _isc.SerializationManager {
|
|||
if (t == _isc.getType<_itvvd6m6.NewPassword?>()) {
|
||||
return (data != null ? _itvvd6m6.NewPassword.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _isc.getType<_i5f4sdg4.OwnerProof?>()) {
|
||||
return (data != null ? _i5f4sdg4.OwnerProof.fromJson(data) : null) as T;
|
||||
}
|
||||
if (t == _isc.getType<_iuxisac7.PasswordChallenge?>()) {
|
||||
return (data != null ? _iuxisac7.PasswordChallenge.fromJson(data) : null)
|
||||
as T;
|
||||
|
|
@ -355,6 +363,7 @@ class Protocol extends _isc.SerializationManager {
|
|||
_ix1u7hdq.DeletionProof => 'DeletionProof',
|
||||
_iehtx95q.KeyProofChallenge => 'KeyProofChallenge',
|
||||
_itvvd6m6.NewPassword => 'NewPassword',
|
||||
_i5f4sdg4.OwnerProof => 'OwnerProof',
|
||||
_iuxisac7.PasswordChallenge => 'PasswordChallenge',
|
||||
_izp492e5.PasswordCosts => 'PasswordCosts',
|
||||
_ibfrwdhm.PasswordRefusal => 'PasswordRefusal',
|
||||
|
|
@ -411,6 +420,8 @@ class Protocol extends _isc.SerializationManager {
|
|||
return 'KeyProofChallenge';
|
||||
case _itvvd6m6.NewPassword():
|
||||
return 'NewPassword';
|
||||
case _i5f4sdg4.OwnerProof():
|
||||
return 'OwnerProof';
|
||||
case _iuxisac7.PasswordChallenge():
|
||||
return 'PasswordChallenge';
|
||||
case _izp492e5.PasswordCosts():
|
||||
|
|
@ -486,6 +497,9 @@ class Protocol extends _isc.SerializationManager {
|
|||
if (dataClassName == 'NewPassword') {
|
||||
return deserialize<_itvvd6m6.NewPassword>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'OwnerProof') {
|
||||
return deserialize<_i5f4sdg4.OwnerProof>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'PasswordChallenge') {
|
||||
return deserialize<_iuxisac7.PasswordChallenge>(data['data']);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue