feat(app): signing in with SimpleLogin on the device, its data layer (ADR 0020)

SimpleLoginRepository signs in with SimpleLogin in the browser
(OidcBrowser: the app's scheme on Android, iOS and macOS, the app's port
on the loopback on Linux and Windows, D9), then makes the account's keys
and signs someone new up, opens an account's keys with the password, gives
an account of before its first keys, or links a new SimpleLogin account in
place of a lost one with the recovery phrase (D7), which opens the keys on
the device first: a phrase that doesn't fit sends no proof, and no
password is asked after. An anonymous person adding SimpleLogin keeps the
keys their device holds. Nothing is the device's until `enter`.

SimpleLoginGateway calls halkyon_base's `simpleLogin` and `keys`
endpoints (ServerpodSimpleLoginGateway), with a fake for tests, as does
FakeOidcBrowser. The calls made on clients of their own (signed out, or as
a session not yet the device's) move from the password gateway into
BaseCalls, which both share.

Fixes BaseProtocol, the client of halkyon_base alone, which didn't read
halkyon_oidc's classes: every refusal of SimpleLogin's sign-in on a
signed-out call would have reached the app as a failure. OidcBrowser keeps
its loopback private, so that a fake can implement it.

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 00:17:04 +02:00
commit e87b156676
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -23,7 +23,7 @@ typedef WebAuthenticate = Future<String> Function({
class OidcBrowser({
/// Waits on the loopback for [OidcReturn.loopback], in place of
/// flutter_web_auth_2's own; its page names the app.
required final LoopbackWebAuth loopback,
required final LoopbackWebAuth _loopback,
final WebAuthenticate _authenticate = FlutterWebAuth2.authenticate,
}) {
@ -48,7 +48,7 @@ class OidcBrowser({
),
// Straight to the loopback, not through the package's front, which
// would end the wait whenever the window regains focus.
final OidcLoopbackReturn loopbackReturn => loopback.authenticate(
final OidcLoopbackReturn loopbackReturn => _loopback.authenticate(
url: url,
callbackUrlScheme: loopbackReturn.address,
options: const {},
@ -72,5 +72,5 @@ class OidcBrowser({
/// Stops waiting on the loopback: the sign-in in progress returns nothing.
/// The system's browser sheet ends by itself when the person closes it.
Future<void> cancel() => loopback.cancel();
Future<void> cancel() => _loopback.cancel();
}