feat(app)!: no anonymous accounts on the device (ADR 0020, D4, D25)

Every account has a way in, so the apps make no anonymous account any
more. The start screen shows whenever nobody is signed in, a first
start included, and offers SimpleLogin, a password and GitHub for those
who have it, with no start without an account. An instance out of reach
is said so, with Try again, and one offering no way to create an account
too. An anonymous account's session left from before is deleted as the
app starts (D25), falling back to signing out on the device.

What only anonymous accounts needed goes: signing in anonymously, the
signed-out mark, adding GitHub to an account and switching to a GitHub
account from the Profile tab (with its Cancel, the only one), an
anonymous device's own keys, the warning that signing out deletes the
account, and the texts about keeping progress. Settings says on which
instance the account is signed in. Learn's publishing, reports and
suggestions and its learner count no longer ask whether the person has
an identity: everyone signed in has one.

The server still offers anonymous sign-in until the next commit, so
older apps keep working until v0.20.0 raises the minimum build.

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-24 14:50:55 +02:00
commit 5b9df73756
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
5 changed files with 981 additions and 3339 deletions

View file

@ -27,7 +27,7 @@ void main() {
setUp(() {
now = DateTime.utc(2026, 10, 20, 12);
auth = FakeAuthGateway();
auth = FakeAuthGateway(status: const SignedOut());
gateway = FakePasswordGateway(auth);
crypto = FakeAccountCrypto();
passwords = PasswordRepository(

View file

@ -12,7 +12,7 @@ void main() {
late FakePasswordAccount anna;
setUp(() async {
auth = FakeAuthGateway();
auth = FakeAuthGateway(status: const SignedOut());
gateway = FakePasswordGateway(auth);
crypto = FakeAccountCrypto();
passwords = PasswordRepository(

View file

@ -26,7 +26,7 @@ void main() {
setUp(() {
now = DateTime.utc(2026, 10, 20, 12);
auth = FakeAuthGateway();
auth = FakeAuthGateway(status: const SignedOut());
gateway = FakePasswordGateway(auth);
crypto = FakeAccountCrypto();
passwords = PasswordRepository(
@ -190,9 +190,9 @@ void main() {
expect(auth.status, const SignedOut());
});
test('gives a signed-in anonymous person a password with an address a '
'code proves, the session staying theirs', () async {
await auth.signInAnonymously();
test('gives one signed in with GitHub a password with an address a code '
'proves, the session staying theirs (ADR 0017, D2)', () async {
await auth.switchToGitHub();
final person = (auth.status as SignedIn).authUserId;
final cubit = setUpAs(AccountSetupMode.setPassword)
..addressEntered('anna@example.org');
@ -213,7 +213,7 @@ void main() {
test('gives a signed-in person with an address their first password only '
'with a code mailed to that address for it, never with the session '
'alone (the fourth reading)', () async {
await auth.signInAnonymously();
await auth.switchToGitHub();
final person = (auth.status as SignedIn).authUserId;
final first = setUpAs(AccountSetupMode.setPassword)
..addressEntered('anna@example.org');
@ -280,7 +280,7 @@ void main() {
password: 'correct horse',
crypto: crypto,
);
await auth.signInAnonymously();
await auth.switchToGitHub();
final cubit = setUpAs(AccountSetupMode.setPassword)
..addressEntered('anna@example.org');
await cubit.passwordEntered('another horse', 'another horse');

View file

@ -25,7 +25,7 @@ void main() {
setUp(() {
now = DateTime.utc(2026, 10, 20, 12);
auth = FakeAuthGateway();
auth = FakeAuthGateway(status: const SignedOut());
gateway = FakePasswordGateway(auth);
crypto = FakeAccountCrypto();
passwords = PasswordRepository(

View file

@ -61,7 +61,7 @@ void main() {
);
setUp(() {
auth = FakeAuthGateway();
auth = FakeAuthGateway(status: const SignedOut());
passwords = repository();
});
tearDown(() => passwords.dispose());
@ -135,7 +135,7 @@ void main() {
final gateway = _BrokenGateway(auth);
final broken = repository(gateway: gateway, crypto: FakeAccountCrypto());
addTearDown(broken.dispose);
await auth.signInAnonymously();
await auth.switchToGitHub();
final section = AccountSecurityCubit(broken);
addTearDown(section.close);
final states = await statesOf(section, section.load);