feat(auth): ways in for accounts without a working password
ADR 0017, step 3. Signing up takes an address a mailed code proves and a password's setup, without a session (`passwordSignIn.signUp`): an address nobody has becomes a new identified account, and an account with the address but no password (codes by mail before; no keys exist, so nothing waits) takes the password, keeping its ID. A setup never finished signs in with its password and a code mailed to the address it began with, until an authenticator is confirmed, which also calls off a reset of the account waiting; any other account takes no code by mail in place of its second factor and leaves the code unused. A reset of the whole account waiting no longer counts as having had an authenticator, so its owner isn't locked out until it completes. Members forward both through the register; the tests' shared helpers move into `support/password_support.dart`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
ce2c00c7ea
commit
f8076dcacf
1 changed files with 849 additions and 80 deletions
|
|
@ -269,8 +269,23 @@ final class _Register implements AccountRegister {
|
|||
required ByteData clientProof,
|
||||
String? totpCode,
|
||||
String? recoveryCode,
|
||||
UuidValue? request,
|
||||
String? code,
|
||||
}) => _answer('completePasswordSignIn', _proven);
|
||||
|
||||
@override
|
||||
Future<UuidValue> signUpWithCode(
|
||||
Session session, {
|
||||
required UuidValue request,
|
||||
required String code,
|
||||
required PasswordSetup setup,
|
||||
}) => _answer('signUpWithCode', () {
|
||||
final account = const Uuid().v7obj();
|
||||
add(account);
|
||||
passwords.add(account);
|
||||
return account;
|
||||
});
|
||||
|
||||
@override
|
||||
Future<AuthenticatorEnrolment> enrolAuthenticator(
|
||||
Session session, {
|
||||
|
|
@ -708,6 +723,29 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
test('sign up at the register, the new account signed in here, '
|
||||
'identified', () async {
|
||||
final device = await PasswordDevice.make();
|
||||
|
||||
final signedUp = await endpoints.passwordSignIn.signUp(
|
||||
notSignedIn(),
|
||||
request: const Uuid().v7obj(),
|
||||
code: 'K7QX-M2PD',
|
||||
setup: device.setup(),
|
||||
);
|
||||
|
||||
expect(register.asked, ['signUpWithCode']);
|
||||
expect(register.passwords, {signedUp.authUserId});
|
||||
expect(await scopesOf(signedUp.authUserId), {identified.name});
|
||||
expect(
|
||||
(await ServerSideSession.db.find(
|
||||
session,
|
||||
where: (t) => t.authUserId.equals(signedUp.authUserId),
|
||||
)).single.method,
|
||||
passwordMethod,
|
||||
);
|
||||
});
|
||||
|
||||
test('refuse as the register refuses, signing nobody in', () async {
|
||||
final people = await AuthUser.db.count(session);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue