fix(app): every change of how an account is secured asks for the password

The app's side of the owner's proof (OwnerProof, the third reading): adding
an authenticator once one is confirmed, removing a confirmed one and new
recovery codes now ask for the current password beside the code, as
changing the password, a new phrase and deleting already did; one
challenge and one answer per sheet (OwnerAnswer). A wrong password or code
reads as the one refusal the instance gives.

Deleting chooses a code by mail from what the instance says
(secondFactorWorks), no longer from whether an authenticator was ever
confirmed: with the authenticators' reset taken effect but not carried
out, the app offered a code field the instance refused (the fourth
finding). Signed-in calls take no address any more, so a device that
doesn't know it changes the password as any other; deleting by mail asks
for the address only where the device doesn't know it, as the code goes
there (the fifth finding).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-21 10:42:55 +02:00
commit a0ae08f975
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -194,7 +194,7 @@ void main() {
await settle();
await expectLater(
passwords.deleteAccount(password: 'battery staple', language: 'en'),
passwords.deleteAccount(password: 'battery staple'),
failsWith(PasswordProblem.wrongCredentials),
);
final mailed = await passwords.mailDeletionCode(language: 'en');
@ -204,7 +204,6 @@ void main() {
password: 'battery staple',
request: mailed.request,
code: gateway.codeFor('carla@example.org'),
language: 'en',
);
expect(gateway.accounts, isNot(contains('carla@example.org')));
});
@ -317,7 +316,6 @@ void main() {
oldPassword: 'correct horse',
newPassword: 'better horse',
totpCode: fakeTotpCode,
language: 'en',
);
expect(anna.password, 'better horse');
@ -340,7 +338,6 @@ void main() {
oldPassword: 'wrong',
newPassword: 'better horse',
totpCode: fakeTotpCode,
language: 'en',
),
failsWith(PasswordProblem.wrongCredentials),
);
@ -350,44 +347,36 @@ void main() {
oldPassword: 'correct horse',
newPassword: 'better horse',
totpCode: fakeTotpCode,
language: 'en',
),
failsWith(PasswordProblem.keysMissing),
);
expect(anna.password, 'correct horse');
});
test('sends the address this device signed in with, and says when it '
"doesn't know it, sending nothing", () async {
test("takes no address while signed in: a device that doesn't know it "
'changes the password, and asks for one only to mail a code for '
'deleting', () async {
addresses.addresses.clear();
await passwords.changePassword(
oldPassword: 'correct horse',
newPassword: 'better horse',
newPassword: 'better horses',
totpCode: fakeTotpCode,
language: 'en',
);
expect(anna.password, 'better horse');
expect(anna.password, 'better horses');
addresses.addresses.clear();
final calls = gateway.calls.length;
await expectLater(
passwords.changePassword(
oldPassword: 'better horse',
newPassword: 'best horse',
totpCode: fakeTotpCode,
language: 'en',
),
failsWith(PasswordProblem.addressUnknown),
);
await expectLater(
passwords.deleteAccount(
password: 'better horse',
totpCode: fakeTotpCode,
language: 'en',
),
passwords.mailDeletionCode(language: 'en'),
failsWith(PasswordProblem.addressUnknown),
);
expect(gateway.calls, hasLength(calls));
expect(anna.password, 'better horse');
expect(
(await passwords.mailDeletionCode(
language: 'en',
address: ' anna@example.org ',
)).address,
'anna@example.org',
);
});
test('deletes the account with the password and a code alone, then '
@ -396,12 +385,11 @@ void main() {
passwords.deleteAccount(
password: 'wrong',
totpCode: fakeTotpCode,
language: 'en',
),
failsWith(PasswordProblem.wrongCredentials),
);
await expectLater(
passwords.deleteAccount(password: 'correct horse', language: 'en'),
passwords.deleteAccount(password: 'correct horse'),
failsWith(PasswordProblem.wrongCredentials),
);
expect(gateway.accounts, contains('anna@example.org'));
@ -410,7 +398,6 @@ void main() {
await passwords.deleteAccount(
password: 'correct horse',
totpCode: fakeTotpCode,
language: 'en',
);
expect(gateway.accounts, isNot(contains('anna@example.org')));
@ -426,7 +413,6 @@ void main() {
phrase,
password: 'correct horse',
code: '000000',
language: 'en',
),
failsWith(PasswordProblem.wrongCredentials),
);
@ -435,7 +421,6 @@ void main() {
phrase,
password: 'wrong',
code: fakeTotpCode,
language: 'en',
),
failsWith(PasswordProblem.wrongCredentials),
);
@ -445,7 +430,6 @@ void main() {
phrase,
password: 'correct horse',
code: fakeTotpCode,
language: 'en',
);
await auth.signOut();