feat(base): SimpleLogin's sign-in comes back by the app's scheme or the loopback (ADR 0020, D9 corrected)
simpleLogin.begin takes the way back (SimpleLoginReturn: loopback, scheme) rather than the platform, so macOS can come back through its scheme as iOS and Android do; the markers become learn-loopback, learn-scheme, todo-loopback and todo-scheme. Not breaking: the endpoint has never been released. D9 corrected by the developer after research: Android, iOS and macOS come back through the app's scheme, Linux and Windows through the loopback. Verified links protected nothing here: a code caught on its way is worthless without the attempt's ID, and a hostile app beginning an attempt of its own can ask for a way back that other platforms need open. The ADR notes the residual risk and the check of prompt=consent still to do. assetlinks.json stays served. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
7eb1961b9f
commit
7ea9f16904
23 changed files with 201 additions and 182 deletions
|
|
@ -26,16 +26,17 @@
|
|||
}
|
||||
|
||||
# SimpleLogin's forwarding page (ADR 0020): where SimpleLogin sends a sign-in back,
|
||||
# which sends it on to the app that began it, on the desktop or a phone.
|
||||
# which sends it on to the app that began it, by the loopback or its scheme.
|
||||
@simplelogin path /simplelogin-callback
|
||||
handle @simplelogin {
|
||||
reverse_proxy server:8082
|
||||
}
|
||||
|
||||
# Android's statement that Halkyon Learn, signed with the project's key
|
||||
# (tool/release/android-certificate.sha256), opens learn.halkyon.eu's links
|
||||
# itself (App Links, ADR 0020's D9), so SimpleLogin's way back reaches the app
|
||||
# without the forwarding page. On learn.halkyon.eu alone, the one instance the
|
||||
# (tool/release/android-certificate.sha256), may open learn.halkyon.eu's links
|
||||
# itself (App Links): built for ADR 0020's D9, whose correction brings the
|
||||
# sign-in back through the app's scheme instead, and kept, as it costs
|
||||
# nothing. On learn.halkyon.eu alone, the one instance the
|
||||
# project's builds name: another operator's domain is no link of the project's
|
||||
# app. Written here rather than by the server, which knows no app's key.
|
||||
@assetlinks {
|
||||
|
|
|
|||
|
|
@ -6,16 +6,16 @@
|
|||
`/simplelogin-callback` on the register's web server (halkyon_base's
|
||||
`addSimpleLoginCallbackRoute`, Caddy forwarding the path), ahead of the sign-in, so
|
||||
development signs in through learn.halkyon.eu's page, which its SimpleLogin app
|
||||
registers too. The state is `<app>-<platform>.<rest>`: the marker one of the module's
|
||||
fixed table (Learn and Todo, the desktop's loopback on ports 43823 and 43824, or the
|
||||
app's scheme), the rest 1 to 512 characters of base64url. A 303 carries the code or
|
||||
registers too. The state is `<app>-<way back>.<rest>`: the marker one of the module's
|
||||
fixed table (Learn and Todo, the loopback on ports 43823 and 43824, or the app's
|
||||
scheme; named `<app>-<platform>` until D9's correction), the rest 1 to 512 characters
|
||||
of base64url. A 303 carries the code or
|
||||
SimpleLogin's error (`deny`) with the state and nothing else; whatever is malformed,
|
||||
repeated or not a GET is refused with a page that repeats nothing. No caching, no
|
||||
referrer, nothing logged. Android's `assetlinks.json` for Halkyon Learn signed with
|
||||
the release key (D9) is answered by `deploy/Caddyfile` on learn.halkyon.eu alone,
|
||||
held to `tool/release/android-certificate.sha256` by a test. The app's intent filter
|
||||
comes with the sign-in; iOS's associated domains wait for an Apple team ID, so iPhones
|
||||
come back through the forwarding page and the scheme until then.
|
||||
the release key is answered by `deploy/Caddyfile` on learn.halkyon.eu alone, held to
|
||||
`tool/release/android-certificate.sha256` by a test: built for D9's verified links,
|
||||
which its correction dropped, and kept, as it costs nothing.
|
||||
- **Built (23 Oct 2026), step 2:** the sign-in on the server's side (1 to 5), on
|
||||
halkyon_oidc (ADR 0023) with its SimpleLogin preset: `simpleLogin.begin` and
|
||||
`simpleLogin.complete` in halkyon_base, a member asking the register for both; the
|
||||
|
|
@ -121,9 +121,20 @@ against app.simplelogin.io on 23 Oct:
|
|||
GitHub ends, 22 Jan 2027, which was recommended): from then on every session of an
|
||||
account without SimpleLogin ends, GitHub and email codes sign nobody in there, and the
|
||||
instance offers SimpleLogin alone.
|
||||
- **D9 Phones come back through verified links** (recommended): Android's App Links and
|
||||
iOS's https callbacks with associated domains, for learn.halkyon.eu; the forwarding
|
||||
page with the app's scheme for other instances, the loopback on desktops.
|
||||
- **D9 Apps come back through their scheme or the loopback** (corrected 23 Oct, night,
|
||||
by the developer, after research; decided first as verified links for phones on
|
||||
learn.halkyon.eu, Android's App Links and iOS's https callbacks): Android, iOS and
|
||||
macOS come back through the app's scheme, which the system's browser sheet catches
|
||||
(Chrome's Auth Tab, since Chrome 132, even after the forwarding page's redirect;
|
||||
`ASWebAuthenticationSession`), Linux and Windows through the loopback.
|
||||
`simpleLogin.begin` takes the way back (`SimpleLoginReturn`: `loopback`, `scheme`),
|
||||
not the platform. Verified links protected nothing here: a code caught on its way is
|
||||
worthless without the attempt's ID (4), and a hostile app beginning an attempt of its
|
||||
own can ask for the scheme or the loopback, which other platforms need open (an
|
||||
Android app can listen on `127.0.0.1` too). They cost: debug builds and other
|
||||
instances need the scheme anyway, Firefox has no Auth Tab, Samsung Internet stays on
|
||||
the page, and flutter_web_auth_2 (v5.1.0) has open bugs with https callbacks (#183,
|
||||
#215). The scheme is what AppAuth calls the most widely supported.
|
||||
- **D10 Accounts without SimpleLogin are deleted 30 days after the cut-off**
|
||||
(recommended, over keeping them unreachable): nobody can add SimpleLogin signed out;
|
||||
the reminder names both days.
|
||||
|
|
@ -200,12 +211,12 @@ against app.simplelogin.io on 23 Oct:
|
|||
ID the app holds, a `nonce`, which app and platform, the caller if signed in; ten
|
||||
minutes, taken once) and answers the authorization address. The app opens it in the
|
||||
browser (`flutter_web_auth_2`, as GitHub's). SimpleLogin sends the browser to
|
||||
`https://<register's host>/simplelogin-callback`, the one address registered. On a
|
||||
phone with learn.halkyon.eu, the system hands that address straight to the app (D9).
|
||||
Elsewhere the instance's page there forwards code and `state` to the app: the
|
||||
desktop's loopback (`http://127.0.0.1:43823/simplelogin-callback`, each app its own
|
||||
port) or the phone's scheme (`app.halkyon.learn://simplelogin-callback`). Its targets
|
||||
are fixed per app and platform, chosen by a marker in `state`, so it sends nobody
|
||||
`https://<register's host>/simplelogin-callback`, the one address registered. The
|
||||
instance's page there forwards code and `state` to the app (D9): the loopback on
|
||||
Linux and Windows (`http://127.0.0.1:43823/simplelogin-callback`, each app its own
|
||||
port) or the app's scheme on Android, iOS and macOS
|
||||
(`app.halkyon.learn://simplelogin-callback`). Its targets
|
||||
are fixed per app and way back, chosen by a marker in `state`, so it sends nobody
|
||||
elsewhere; SimpleLogin's errors (a denial) are forwarded alike. The app hands the code
|
||||
with its attempt's ID to `simpleLogin.complete`.
|
||||
4. **Checking.** The register exchanges the code and checks the ID token: RS256 against
|
||||
|
|
@ -213,8 +224,8 @@ against app.simplelogin.io on 23 Oct:
|
|||
`https://app.simplelogin.io`, the audience its client ID, not expired, and the `nonce`
|
||||
the attempt's. So a code caught on its way to the app is worthless: it can only
|
||||
complete the attempt it was asked for, whose ID never left the app. (A hostile app
|
||||
starting an attempt of its own is what D9's verified links stop: SimpleLogin answers
|
||||
without a consent page for an app already allowed.) It keeps the `sub` alone
|
||||
on the device beginning an attempt of its own isn't stopped, verified links or not
|
||||
(D9): SimpleLogin answers without a consent page for an app already allowed.) It keeps the `sub` alone
|
||||
(`simplelogin_identity`: account ID, `sub`, each unique); the name serves only as the
|
||||
handle's suggestion at sign-up (D5); address, name and avatar are never stored, and
|
||||
SimpleLogin's userinfo is never asked.
|
||||
|
|
@ -268,5 +279,9 @@ against app.simplelogin.io on 23 Oct:
|
|||
- Everyone has a handle, which stays private until they go public.
|
||||
- The keys' password is one more thing to keep, and the recovery phrase also mends a lost
|
||||
SimpleLogin link.
|
||||
- GitHub's sign-in on phones has the weakness D9 avoids (its callback is the scheme)
|
||||
until it ends.
|
||||
- A hostile app on a person's device can begin a sign-in of its own and, once the person
|
||||
has allowed Halkyon at SimpleLogin, come back with a session without a consent page
|
||||
(4, D9); what it gets is the account's server-side data, while private studysets,
|
||||
tags and settings stay sealed under the keys (ADR 0019). Whether SimpleLogin honours
|
||||
OpenID Connect's `prompt=consent`, which would show the consent page every time, is
|
||||
still to be checked.
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@ import 'package:halkyon_base_client/src/protocol/profile/profile_refusal.dart'
|
|||
as _ih722tf9;
|
||||
import 'package:halkyon_base_client/src/protocol/simplelogin/simplelogin_completed.dart'
|
||||
as _ianfx7qj;
|
||||
import 'package:halkyon_base_client/src/protocol/simplelogin/simplelogin_platform.dart'
|
||||
as _i32mw11n;
|
||||
import 'package:halkyon_base_client/src/protocol/simplelogin/simplelogin_return.dart'
|
||||
as _ihc3njmr;
|
||||
import 'package:halkyon_oidc_client/halkyon_oidc_client.dart' as _ibkpwzgl;
|
||||
import 'package:serverpod_auth_core_client/serverpod_auth_core_client.dart'
|
||||
as _iacc;
|
||||
|
|
@ -901,16 +901,17 @@ class EndpointSimpleLogin extends _isc.EndpointRef {
|
|||
@override
|
||||
String get name => 'halkyon_base.simpleLogin';
|
||||
|
||||
/// Begins a sign-in from this server's app on [platform]: the attempt,
|
||||
/// whose ID the device keeps to itself, and SimpleLogin's address to open
|
||||
/// in the browser, which comes back through the forwarding page with a
|
||||
/// code for [complete]. Refusals as `beginSimpleLogin`'s.
|
||||
/// Begins a sign-in from this server's app, coming [back] by the loopback
|
||||
/// or the app's scheme: the attempt, whose ID the device keeps to itself,
|
||||
/// and SimpleLogin's address to open in the browser, which comes back
|
||||
/// through the forwarding page with a code for [complete]. Refusals as
|
||||
/// `beginSimpleLogin`'s.
|
||||
_ida.Future<_ibkpwzgl.OidcStart> begin({
|
||||
required _i32mw11n.SimpleLoginPlatform platform,
|
||||
required _ihc3njmr.SimpleLoginReturn back,
|
||||
}) => caller.callServerEndpoint<_ibkpwzgl.OidcStart>(
|
||||
'halkyon_base.simpleLogin',
|
||||
'begin',
|
||||
{'platform': platform},
|
||||
{'back': back},
|
||||
);
|
||||
|
||||
/// Ends [attempt] with SimpleLogin's [code], as the caller began it:
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ import 'profile/profile_refusal.dart' as _i6j6efd1;
|
|||
import 'profile/profile_refused_exception.dart' as _idvc5e7p;
|
||||
import 'simplelogin/simplelogin_completed.dart' as _i51ry5s2;
|
||||
import 'simplelogin/simplelogin_outcome.dart' as _i5ar1hzl;
|
||||
import 'simplelogin/simplelogin_platform.dart' as _ivny5pkt;
|
||||
import 'simplelogin/simplelogin_return.dart' as _i8dcjd27;
|
||||
export 'auth/email_code_purpose.dart';
|
||||
export 'auth/email_sign_in_refusal.dart';
|
||||
export 'auth/email_sign_in_refused_exception.dart';
|
||||
|
|
@ -92,7 +92,7 @@ export 'profile/profile_refusal.dart';
|
|||
export 'profile/profile_refused_exception.dart';
|
||||
export 'simplelogin/simplelogin_completed.dart';
|
||||
export 'simplelogin/simplelogin_outcome.dart';
|
||||
export 'simplelogin/simplelogin_platform.dart';
|
||||
export 'simplelogin/simplelogin_return.dart';
|
||||
export 'client.dart';
|
||||
|
||||
class Protocol extends _isc.SerializationManager {
|
||||
|
|
@ -248,8 +248,8 @@ class Protocol extends _isc.SerializationManager {
|
|||
if (t == _i5ar1hzl.SimpleLoginOutcome) {
|
||||
return _i5ar1hzl.SimpleLoginOutcome.fromJson(data) as T;
|
||||
}
|
||||
if (t == _ivny5pkt.SimpleLoginPlatform) {
|
||||
return _ivny5pkt.SimpleLoginPlatform.fromJson(data) as T;
|
||||
if (t == _i8dcjd27.SimpleLoginReturn) {
|
||||
return _i8dcjd27.SimpleLoginReturn.fromJson(data) as T;
|
||||
}
|
||||
if (t == _isc.getType<_ifpka2on.EmailCodePurpose?>()) {
|
||||
return (data != null ? _ifpka2on.EmailCodePurpose.fromJson(data) : null)
|
||||
|
|
@ -403,10 +403,8 @@ class Protocol extends _isc.SerializationManager {
|
|||
return (data != null ? _i5ar1hzl.SimpleLoginOutcome.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _isc.getType<_ivny5pkt.SimpleLoginPlatform?>()) {
|
||||
return (data != null
|
||||
? _ivny5pkt.SimpleLoginPlatform.fromJson(data)
|
||||
: null)
|
||||
if (t == _isc.getType<_i8dcjd27.SimpleLoginReturn?>()) {
|
||||
return (data != null ? _i8dcjd27.SimpleLoginReturn.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == Set<String>) {
|
||||
|
|
@ -480,7 +478,7 @@ class Protocol extends _isc.SerializationManager {
|
|||
_idvc5e7p.ProfileRefusedException => 'ProfileRefusedException',
|
||||
_i51ry5s2.SimpleLoginCompleted => 'SimpleLoginCompleted',
|
||||
_i5ar1hzl.SimpleLoginOutcome => 'SimpleLoginOutcome',
|
||||
_ivny5pkt.SimpleLoginPlatform => 'SimpleLoginPlatform',
|
||||
_i8dcjd27.SimpleLoginReturn => 'SimpleLoginReturn',
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
|
@ -570,8 +568,8 @@ class Protocol extends _isc.SerializationManager {
|
|||
return 'SimpleLoginCompleted';
|
||||
case _i5ar1hzl.SimpleLoginOutcome():
|
||||
return 'SimpleLoginOutcome';
|
||||
case _ivny5pkt.SimpleLoginPlatform():
|
||||
return 'SimpleLoginPlatform';
|
||||
case _i8dcjd27.SimpleLoginReturn():
|
||||
return 'SimpleLoginReturn';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
@ -690,8 +688,8 @@ class Protocol extends _isc.SerializationManager {
|
|||
if (dataClassName == 'SimpleLoginOutcome') {
|
||||
return deserialize<_i5ar1hzl.SimpleLoginOutcome>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'SimpleLoginPlatform') {
|
||||
return deserialize<_ivny5pkt.SimpleLoginPlatform>(data['data']);
|
||||
if (dataClassName == 'SimpleLoginReturn') {
|
||||
return deserialize<_i8dcjd27.SimpleLoginReturn>(data['data']);
|
||||
}
|
||||
return super.deserializeByClassName(data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
/* 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 'package:serverpod_client/serverpod_client.dart' as _isc;
|
||||
|
||||
/// How an app comes back from a SimpleLogin sign-in (ADR 0020, 3 and D9),
|
||||
/// which says where the forwarding page sends it: the loopback (Linux,
|
||||
/// Windows) or the app's scheme (Android, iOS, macOS).
|
||||
enum SimpleLoginReturn implements _isc.SerializableModel {
|
||||
loopback,
|
||||
scheme;
|
||||
|
||||
static SimpleLoginReturn fromJson(String name) {
|
||||
switch (name) {
|
||||
case 'loopback':
|
||||
return SimpleLoginReturn.loopback;
|
||||
case 'scheme':
|
||||
return SimpleLoginReturn.scheme;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'Value "$name" cannot be converted to "SimpleLoginReturn"',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toJson() => name;
|
||||
|
||||
@override
|
||||
String toString() => name;
|
||||
}
|
||||
|
|
@ -87,11 +87,12 @@ Endpoints, under the nickname `base` in a host's client (`client.modules.base.em
|
|||
`/simplelogin-callback`, the one address the instance's SimpleLogin
|
||||
app registers, as SimpleLogin takes only https addresses, each registered exactly. It
|
||||
answers a 303 to the app that began the sign-in, with SimpleLogin's `code` (or its
|
||||
`error`) and the `state`, and nothing else: the state is `<app>-<platform>.<rest>`, the
|
||||
marker one of `simpleLoginReturns` (the desktop's loopback, `127.0.0.1` on a port of each
|
||||
app's own, or the phone's scheme, the app's ID), the rest 1 to 512 characters of
|
||||
base64url; anything else gets a page saying the sign-in can't go on, repeating nothing
|
||||
it was given, in English or German. It keeps and logs nothing.
|
||||
`error`) and the `state`, and nothing else: the state is `<app>-<way back>.<rest>`, the
|
||||
marker one of `simpleLoginReturns` (the loopback on Linux and Windows, `127.0.0.1` on a
|
||||
port of each app's own, or the app's scheme, its ID, on Android, iOS and macOS;
|
||||
`simpleLogin.begin` takes the way back as a `SimpleLoginReturn`), the rest 1 to 512
|
||||
characters of base64url; anything else gets a page saying the sign-in can't go on,
|
||||
repeating nothing it was given, in English or German. It keeps and logs nothing.
|
||||
- **Per-network limits** (new anonymous accounts, codes by mail, password sign-ins,
|
||||
proofs of the recovery phrase) count by the visitor's IP address (`networkOf`): the
|
||||
connection's, or, for a connection from a proxy the operator trusts
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ import 'package:halkyon_base_server/src/generated/password/password_setup.dart'
|
|||
as _ikhyee47;
|
||||
import 'package:halkyon_base_server/src/generated/password/wrapped_keys.dart'
|
||||
as _iji0wmu8;
|
||||
import 'package:halkyon_base_server/src/generated/simplelogin/simplelogin_platform.dart'
|
||||
as _ixrtiuh0;
|
||||
import 'package:halkyon_base_server/src/generated/simplelogin/simplelogin_return.dart'
|
||||
as _igmh0yku;
|
||||
import 'package:halkyon_oidc_server/halkyon_oidc_server.dart' as _ipoqfiqr;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
import 'package:serverpod_auth_core_server/serverpod_auth_core_server.dart'
|
||||
|
|
@ -1283,9 +1283,9 @@ class Endpoints extends _is.EndpointDispatch {
|
|||
'begin': _is.MethodConnector(
|
||||
name: 'begin',
|
||||
params: {
|
||||
'platform': _is.ParameterDescription(
|
||||
name: 'platform',
|
||||
type: _is.getType<_ixrtiuh0.SimpleLoginPlatform>(),
|
||||
'back': _is.ParameterDescription(
|
||||
name: 'back',
|
||||
type: _is.getType<_igmh0yku.SimpleLoginReturn>(),
|
||||
nullable: false,
|
||||
),
|
||||
},
|
||||
|
|
@ -1297,7 +1297,7 @@ class Endpoints extends _is.EndpointDispatch {
|
|||
(endpoints['simpleLogin'] as _ia8c9alv.SimpleLoginEndpoint)
|
||||
.begin(
|
||||
session,
|
||||
platform: params['platform'],
|
||||
back: params['back'],
|
||||
),
|
||||
),
|
||||
'complete': _is.MethodConnector(
|
||||
|
|
|
|||
|
|
@ -69,8 +69,8 @@ import 'register/account_deletion_notice.dart' as _i2lyzpm9;
|
|||
import 'register/sessions_ended_notice.dart' as _io5c3tod;
|
||||
import 'simplelogin/simplelogin_completed.dart' as _i51ry5s2;
|
||||
import 'simplelogin/simplelogin_outcome.dart' as _i5ar1hzl;
|
||||
import 'simplelogin/simplelogin_platform.dart' as _ivny5pkt;
|
||||
import 'simplelogin/simplelogin_proof.dart' as _iauqtzog;
|
||||
import 'simplelogin/simplelogin_return.dart' as _i8dcjd27;
|
||||
export 'auth/email_code_purpose.dart';
|
||||
export 'auth/email_code_request.dart';
|
||||
export 'auth/email_identity.dart';
|
||||
|
|
@ -120,8 +120,8 @@ export 'register/account_deletion_notice.dart';
|
|||
export 'register/sessions_ended_notice.dart';
|
||||
export 'simplelogin/simplelogin_completed.dart';
|
||||
export 'simplelogin/simplelogin_outcome.dart';
|
||||
export 'simplelogin/simplelogin_platform.dart';
|
||||
export 'simplelogin/simplelogin_proof.dart';
|
||||
export 'simplelogin/simplelogin_return.dart';
|
||||
|
||||
class Protocol extends _is.DatabaseSerializationManager {
|
||||
Protocol._();
|
||||
|
|
@ -1219,12 +1219,12 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (t == _i5ar1hzl.SimpleLoginOutcome) {
|
||||
return _i5ar1hzl.SimpleLoginOutcome.fromJson(data) as T;
|
||||
}
|
||||
if (t == _ivny5pkt.SimpleLoginPlatform) {
|
||||
return _ivny5pkt.SimpleLoginPlatform.fromJson(data) as T;
|
||||
}
|
||||
if (t == _iauqtzog.SimpleLoginProof) {
|
||||
return _iauqtzog.SimpleLoginProof.fromJson(data) as T;
|
||||
}
|
||||
if (t == _i8dcjd27.SimpleLoginReturn) {
|
||||
return _i8dcjd27.SimpleLoginReturn.fromJson(data) as T;
|
||||
}
|
||||
if (t == _is.getType<_ifpka2on.EmailCodePurpose?>()) {
|
||||
return (data != null ? _ifpka2on.EmailCodePurpose.fromJson(data) : null)
|
||||
as T;
|
||||
|
|
@ -1435,16 +1435,14 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
return (data != null ? _i5ar1hzl.SimpleLoginOutcome.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _is.getType<_ivny5pkt.SimpleLoginPlatform?>()) {
|
||||
return (data != null
|
||||
? _ivny5pkt.SimpleLoginPlatform.fromJson(data)
|
||||
: null)
|
||||
as T;
|
||||
}
|
||||
if (t == _is.getType<_iauqtzog.SimpleLoginProof?>()) {
|
||||
return (data != null ? _iauqtzog.SimpleLoginProof.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == _is.getType<_i8dcjd27.SimpleLoginReturn?>()) {
|
||||
return (data != null ? _i8dcjd27.SimpleLoginReturn.fromJson(data) : null)
|
||||
as T;
|
||||
}
|
||||
if (t == Set<String>) {
|
||||
return (data as List).map((e) => deserialize<String>(e)).toSet() as T;
|
||||
}
|
||||
|
|
@ -1532,8 +1530,8 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
_io5c3tod.SessionsEndedNotice => 'SessionsEndedNotice',
|
||||
_i51ry5s2.SimpleLoginCompleted => 'SimpleLoginCompleted',
|
||||
_i5ar1hzl.SimpleLoginOutcome => 'SimpleLoginOutcome',
|
||||
_ivny5pkt.SimpleLoginPlatform => 'SimpleLoginPlatform',
|
||||
_iauqtzog.SimpleLoginProof => 'SimpleLoginProof',
|
||||
_i8dcjd27.SimpleLoginReturn => 'SimpleLoginReturn',
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
|
@ -1649,10 +1647,10 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
return 'SimpleLoginCompleted';
|
||||
case _i5ar1hzl.SimpleLoginOutcome():
|
||||
return 'SimpleLoginOutcome';
|
||||
case _ivny5pkt.SimpleLoginPlatform():
|
||||
return 'SimpleLoginPlatform';
|
||||
case _iauqtzog.SimpleLoginProof():
|
||||
return 'SimpleLoginProof';
|
||||
case _i8dcjd27.SimpleLoginReturn():
|
||||
return 'SimpleLoginReturn';
|
||||
}
|
||||
className = _isp.Protocol().getClassNameForObject(data);
|
||||
if (className != null) {
|
||||
|
|
@ -1814,12 +1812,12 @@ class Protocol extends _is.DatabaseSerializationManager {
|
|||
if (dataClassName == 'SimpleLoginOutcome') {
|
||||
return deserialize<_i5ar1hzl.SimpleLoginOutcome>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'SimpleLoginPlatform') {
|
||||
return deserialize<_ivny5pkt.SimpleLoginPlatform>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'SimpleLoginProof') {
|
||||
return deserialize<_iauqtzog.SimpleLoginProof>(data['data']);
|
||||
}
|
||||
if (dataClassName == 'SimpleLoginReturn') {
|
||||
return deserialize<_i8dcjd27.SimpleLoginReturn>(data['data']);
|
||||
}
|
||||
if (dataClassName.startsWith('serverpod.')) {
|
||||
data['className'] = dataClassName.substring(10);
|
||||
return _isp.Protocol().deserializeByClassName(data);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
/* 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 'package:serverpod/serverpod.dart' as _is;
|
||||
|
||||
/// How an app comes back from a SimpleLogin sign-in (ADR 0020, 3 and D9),
|
||||
/// which says where the forwarding page sends it: the loopback (Linux,
|
||||
/// Windows) or the app's scheme (Android, iOS, macOS).
|
||||
enum SimpleLoginReturn implements _is.SerializableModel {
|
||||
loopback,
|
||||
scheme;
|
||||
|
||||
static SimpleLoginReturn fromJson(String name) {
|
||||
switch (name) {
|
||||
case 'loopback':
|
||||
return SimpleLoginReturn.loopback;
|
||||
case 'scheme':
|
||||
return SimpleLoginReturn.scheme;
|
||||
default:
|
||||
throw ArgumentError(
|
||||
'Value "$name" cannot be converted to "SimpleLoginReturn"',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toJson() => name;
|
||||
|
||||
@override
|
||||
String toString() => name;
|
||||
}
|
||||
|
|
@ -32,8 +32,8 @@ OidcIdp _simpleLoginOn(Session session) {
|
|||
return AuthServices.instance.oidcIdp;
|
||||
}
|
||||
|
||||
/// Begins a SimpleLogin sign-in for the app and platform [marker] names
|
||||
/// (`learn-desktop`, one of [simpleLoginReturns]), the state beginning with
|
||||
/// Begins a SimpleLogin sign-in for the app and way back [marker] names
|
||||
/// (`learn-loopback`, one of [simpleLoginReturns]), the state beginning with
|
||||
/// it for the forwarding page; [caller] began it signed in, to add
|
||||
/// SimpleLogin to their account, a member's person becoming a person here
|
||||
/// under their ID, as linking would make them (ADR 0016). At most 30 an hour
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import 'package:serverpod/serverpod.dart';
|
|||
// (`oidcCallbackForwarding`, ADR 0023, 7) with Halkyon's apps and look: the
|
||||
// browser comes back here with SimpleLogin's code (or its refusal) and the
|
||||
// state the sign-in began with, and the page sends both on to the app that
|
||||
// began it, the desktop's loopback or the phone's scheme, by the marker its
|
||||
// state begins with. It keeps and logs nothing: a code caught on its way is
|
||||
// began it, the loopback or the app's scheme, by the marker its state begins
|
||||
// with. It keeps and logs nothing: a code caught on its way is
|
||||
// worthless without the attempt the app holds (ADR 0020, 4).
|
||||
|
||||
/// Where SimpleLogin sends the browser back to, on the register's web
|
||||
|
|
@ -17,16 +17,14 @@ import 'package:serverpod/serverpod.dart';
|
|||
const simpleLoginCallbackPath = '/simplelogin-callback';
|
||||
|
||||
/// Where each Halkyon app takes a sign-in back, by the marker its state
|
||||
/// begins with (`<app>-<platform>`): the desktop's loopback, each app on a
|
||||
/// port of its own (RFC 8252, 7.3), and the phone's scheme, the app's ID. A
|
||||
/// phone with Halkyon Learn and learn.halkyon.eu never reaches the page:
|
||||
/// Android hands the page's address to the app itself (App Links,
|
||||
/// `assetlinks.json`).
|
||||
/// begins with (`<app>-<way back>`): the loopback on Linux and Windows, each
|
||||
/// app on a port of its own (RFC 8252, 7.3), and the app's scheme, its ID, on
|
||||
/// Android, iOS and macOS, where the system's browser sheet catches it.
|
||||
const simpleLoginReturns = {
|
||||
'learn-desktop': 'http://127.0.0.1:43823/simplelogin-callback',
|
||||
'learn-phone': 'app.halkyon.learn://simplelogin-callback',
|
||||
'todo-desktop': 'http://127.0.0.1:43824/simplelogin-callback',
|
||||
'todo-phone': 'app.halkyon.todo://simplelogin-callback',
|
||||
'learn-loopback': 'http://127.0.0.1:43823/simplelogin-callback',
|
||||
'learn-scheme': 'app.halkyon.learn://simplelogin-callback',
|
||||
'todo-loopback': 'http://127.0.0.1:43824/simplelogin-callback',
|
||||
'todo-scheme': 'app.halkyon.todo://simplelogin-callback',
|
||||
};
|
||||
|
||||
final _returns = {
|
||||
|
|
|
|||
|
|
@ -161,11 +161,11 @@ OidcIdpConfig? simpleLoginIdpConfig(Serverpod pod) {
|
|||
);
|
||||
}
|
||||
|
||||
/// The marker a sign-in's state begins with for [app] on [platform]
|
||||
/// (`learn-desktop`), by which the forwarding page sends it back
|
||||
/// The marker a sign-in's state begins with for [app] coming [back]
|
||||
/// (`learn-loopback`), by which the forwarding page sends it back
|
||||
/// ([simpleLoginReturns]).
|
||||
String simpleLoginMarker(String app, SimpleLoginPlatform platform) =>
|
||||
'$app-${platform.name}';
|
||||
String simpleLoginMarker(String app, SimpleLoginReturn back) =>
|
||||
'$app-${back.name}';
|
||||
|
||||
/// Puts [app] in place of [pod]'s SimpleLogin app, null meaning none
|
||||
/// whatever the passwords hold, asking SimpleLogin through [client]: tests
|
||||
|
|
|
|||
|
|
@ -15,18 +15,19 @@ import 'package:serverpod_auth_idp_server/core.dart';
|
|||
/// and checks SimpleLogin's codes (ADR 0016); a session is this server's.
|
||||
/// Refusals are `OidcRefusedException`s.
|
||||
class SimpleLoginEndpoint extends Endpoint {
|
||||
/// Begins a sign-in from this server's app on [platform]: the attempt,
|
||||
/// whose ID the device keeps to itself, and SimpleLogin's address to open
|
||||
/// in the browser, which comes back through the forwarding page with a
|
||||
/// code for [complete]. Refusals as `beginSimpleLogin`'s.
|
||||
/// Begins a sign-in from this server's app, coming [back] by the loopback
|
||||
/// or the app's scheme: the attempt, whose ID the device keeps to itself,
|
||||
/// and SimpleLogin's address to open in the browser, which comes back
|
||||
/// through the forwarding page with a code for [complete]. Refusals as
|
||||
/// `beginSimpleLogin`'s.
|
||||
Future<OidcStart> begin(
|
||||
Session session, {
|
||||
required SimpleLoginPlatform platform,
|
||||
required SimpleLoginReturn back,
|
||||
}) {
|
||||
final pod = session.serverpod;
|
||||
return AccountRegister.of(pod).beginSimpleLogin(
|
||||
session,
|
||||
marker: simpleLoginMarker(HalkyonBase.of(pod).app, platform),
|
||||
marker: simpleLoginMarker(HalkyonBase.of(pod).app, back),
|
||||
caller: session.authenticated?.authUserId,
|
||||
network: networkOf(session),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
### Where an app begins a SimpleLogin sign-in (ADR 0020, 3), which says where
|
||||
### the forwarding page sends it back: the desktop's loopback, or the phone's
|
||||
### scheme (or, with learn.halkyon.eu's App Links, the phone's app itself).
|
||||
enum: SimpleLoginPlatform
|
||||
serialized: byName
|
||||
values:
|
||||
- desktop
|
||||
- phone
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
### How an app comes back from a SimpleLogin sign-in (ADR 0020, 3 and D9),
|
||||
### which says where the forwarding page sends it: the loopback (Linux,
|
||||
### Windows) or the app's scheme (Android, iOS, macOS).
|
||||
enum: SimpleLoginReturn
|
||||
serialized: byName
|
||||
values:
|
||||
- loopback
|
||||
- scheme
|
||||
|
|
@ -131,7 +131,7 @@ void main() {
|
|||
) async {
|
||||
final start = await endpoints.simpleLogin.begin(
|
||||
as,
|
||||
platform: SimpleLoginPlatform.desktop,
|
||||
back: SimpleLoginReturn.loopback,
|
||||
);
|
||||
await endpoints.simpleLogin.complete(
|
||||
as,
|
||||
|
|
|
|||
|
|
@ -1183,30 +1183,30 @@ void main() {
|
|||
});
|
||||
|
||||
group('SimpleLogin, by asking the register (ADR 0020)', () {
|
||||
test('begins for this app and platform, signed out, the device '
|
||||
test('begins for this app and way back, signed out, the device '
|
||||
'network passed on', () async {
|
||||
final start = await endpoints.simpleLogin.begin(
|
||||
notSignedIn(),
|
||||
platform: SimpleLoginPlatform.phone,
|
||||
back: SimpleLoginReturn.scheme,
|
||||
);
|
||||
|
||||
expect(register.asked, ['beginSimpleLogin']);
|
||||
expect(register.simpleLoginsBegun.single, (
|
||||
marker: '$testApp-phone',
|
||||
marker: '$testApp-scheme',
|
||||
caller: null,
|
||||
));
|
||||
expect(register.networks, hasLength(1));
|
||||
expect(start.state, startsWith('$testApp-phone.'));
|
||||
expect(start.state, startsWith('$testApp-scheme.'));
|
||||
});
|
||||
|
||||
test('begins signed in for the caller', () async {
|
||||
await endpoints.simpleLogin.begin(
|
||||
asAnna(),
|
||||
platform: SimpleLoginPlatform.desktop,
|
||||
back: SimpleLoginReturn.loopback,
|
||||
);
|
||||
|
||||
expect(register.simpleLoginsBegun.single, (
|
||||
marker: '$testApp-desktop',
|
||||
marker: '$testApp-loopback',
|
||||
caller: anna,
|
||||
));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -565,7 +565,7 @@ void main() {
|
|||
|
||||
final start = await remote.beginSimpleLogin(
|
||||
session,
|
||||
marker: 'todo-desktop',
|
||||
marker: 'todo-loopback',
|
||||
network: '203.0.113.0/24',
|
||||
);
|
||||
final proof = await remote.completeSimpleLogin(
|
||||
|
|
@ -577,7 +577,7 @@ void main() {
|
|||
),
|
||||
);
|
||||
|
||||
expect(start.state, startsWith('todo-desktop.'));
|
||||
expect(start.state, startsWith('todo-loopback.'));
|
||||
expect(proof.outcome, SimpleLoginOutcome.signedIn);
|
||||
expect(proof.account, ben);
|
||||
});
|
||||
|
|
@ -588,7 +588,7 @@ void main() {
|
|||
|
||||
final start = await remote.beginSimpleLogin(
|
||||
session,
|
||||
marker: 'todo-phone',
|
||||
marker: 'todo-scheme',
|
||||
caller: annaOnTodo,
|
||||
network: '203.0.113.0/24',
|
||||
);
|
||||
|
|
@ -617,7 +617,7 @@ void main() {
|
|||
test('passes on the handle suggested to someone new', () async {
|
||||
final start = await remote.beginSimpleLogin(
|
||||
session,
|
||||
marker: 'todo-desktop',
|
||||
marker: 'todo-loopback',
|
||||
network: '203.0.113.0/24',
|
||||
);
|
||||
final proof = await remote.completeSimpleLogin(
|
||||
|
|
@ -639,7 +639,7 @@ void main() {
|
|||
'crossing whole', () async {
|
||||
final start = await remote.beginSimpleLogin(
|
||||
session,
|
||||
marker: 'todo-desktop',
|
||||
marker: 'todo-loopback',
|
||||
network: '203.0.113.0/24',
|
||||
);
|
||||
final code = simpleLogin.authorize(
|
||||
|
|
@ -677,7 +677,7 @@ void main() {
|
|||
final device = await KeysDevice.make();
|
||||
final start = await remote.beginSimpleLogin(
|
||||
session,
|
||||
marker: 'todo-desktop',
|
||||
marker: 'todo-loopback',
|
||||
network: '203.0.113.0/24',
|
||||
);
|
||||
await remote.completeSimpleLogin(
|
||||
|
|
@ -737,7 +737,7 @@ void main() {
|
|||
final device = await KeysDevice.make();
|
||||
final start = await remote.beginSimpleLogin(
|
||||
session,
|
||||
marker: 'todo-desktop',
|
||||
marker: 'todo-loopback',
|
||||
network: '203.0.113.0/24',
|
||||
);
|
||||
await remote.completeSimpleLogin(
|
||||
|
|
@ -810,7 +810,7 @@ void main() {
|
|||
) async {
|
||||
final start = await remote.beginSimpleLogin(
|
||||
session,
|
||||
marker: 'todo-desktop',
|
||||
marker: 'todo-loopback',
|
||||
caller: person,
|
||||
network: '203.0.113.0/24',
|
||||
);
|
||||
|
|
@ -885,7 +885,7 @@ void main() {
|
|||
final next = await KeysDevice.make(seed: 7);
|
||||
final start = await remote.beginSimpleLogin(
|
||||
session,
|
||||
marker: 'todo-desktop',
|
||||
marker: 'todo-loopback',
|
||||
network: '203.0.113.0/24',
|
||||
);
|
||||
await remote.completeSimpleLogin(
|
||||
|
|
@ -988,7 +988,7 @@ void main() {
|
|||
final device = await KeysDevice.make();
|
||||
final first = await remote.beginSimpleLogin(
|
||||
session,
|
||||
marker: 'todo-desktop',
|
||||
marker: 'todo-loopback',
|
||||
network: '203.0.113.0/24',
|
||||
);
|
||||
await remote.completeSimpleLogin(
|
||||
|
|
@ -1008,7 +1008,7 @@ void main() {
|
|||
);
|
||||
final start = await remote.beginSimpleLogin(
|
||||
session,
|
||||
marker: 'todo-desktop',
|
||||
marker: 'todo-loopback',
|
||||
network: '203.0.113.0/24',
|
||||
);
|
||||
await remote.completeSimpleLogin(
|
||||
|
|
@ -1073,7 +1073,7 @@ void main() {
|
|||
await expectLater(
|
||||
remote.beginSimpleLogin(
|
||||
session,
|
||||
marker: 'todo-desktop',
|
||||
marker: 'todo-loopback',
|
||||
network: '203.0.113.0/24',
|
||||
),
|
||||
refused(OidcRefusal.unknownProvider),
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ void main() {
|
|||
}) async {
|
||||
final start = await endpoints.simpleLogin.begin(
|
||||
as,
|
||||
platform: SimpleLoginPlatform.desktop,
|
||||
back: SimpleLoginReturn.loopback,
|
||||
);
|
||||
return (
|
||||
attempt: start.attempt,
|
||||
|
|
@ -138,7 +138,7 @@ void main() {
|
|||
await expectLater(
|
||||
endpoints.simpleLogin.begin(
|
||||
notSignedIn(),
|
||||
platform: SimpleLoginPlatform.desktop,
|
||||
back: SimpleLoginReturn.loopback,
|
||||
),
|
||||
refused(OidcRefusal.unknownProvider),
|
||||
);
|
||||
|
|
@ -159,7 +159,7 @@ void main() {
|
|||
await expectLater(
|
||||
endpoints.simpleLogin.begin(
|
||||
notSignedIn(),
|
||||
platform: SimpleLoginPlatform.desktop,
|
||||
back: SimpleLoginReturn.loopback,
|
||||
),
|
||||
refused(OidcRefusal.unknownProvider),
|
||||
);
|
||||
|
|
@ -196,17 +196,17 @@ void main() {
|
|||
|
||||
group('beginning', () {
|
||||
test('sends the person to SimpleLogin, the state marked with the app '
|
||||
'and its platform for the forwarding page', () async {
|
||||
final desktop = await endpoints.simpleLogin.begin(
|
||||
'and its way back for the forwarding page', () async {
|
||||
final loopback = await endpoints.simpleLogin.begin(
|
||||
notSignedIn(),
|
||||
platform: SimpleLoginPlatform.desktop,
|
||||
back: SimpleLoginReturn.loopback,
|
||||
);
|
||||
final phone = await endpoints.simpleLogin.begin(
|
||||
final scheme = await endpoints.simpleLogin.begin(
|
||||
notSignedIn(),
|
||||
platform: SimpleLoginPlatform.phone,
|
||||
back: SimpleLoginReturn.scheme,
|
||||
);
|
||||
|
||||
final url = desktop.authorizationUrl;
|
||||
final url = loopback.authorizationUrl;
|
||||
expect(
|
||||
'${url.origin}${url.path}',
|
||||
'https://app.simplelogin.io/oauth2/authorize',
|
||||
|
|
@ -216,10 +216,13 @@ void main() {
|
|||
url.queryParameters['redirect_uri'],
|
||||
'https://learn.test/simplelogin-callback',
|
||||
);
|
||||
expect(desktop.state, startsWith('learn-desktop.'));
|
||||
expect(phone.state, startsWith('learn-phone.'));
|
||||
expect(loopback.state, startsWith('learn-loopback.'));
|
||||
expect(scheme.state, startsWith('learn-scheme.'));
|
||||
expect(
|
||||
(await OidcAttempt.db.findById(session, desktop.attempt))!.callerId,
|
||||
(await OidcAttempt.db.findById(
|
||||
session,
|
||||
loopback.attempt,
|
||||
))!.callerId,
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
|
|
@ -227,7 +230,7 @@ void main() {
|
|||
test('signed in, notes who began it', () async {
|
||||
final start = await endpoints.simpleLogin.begin(
|
||||
asAnna(),
|
||||
platform: SimpleLoginPlatform.phone,
|
||||
back: SimpleLoginReturn.scheme,
|
||||
);
|
||||
|
||||
expect(
|
||||
|
|
@ -245,7 +248,7 @@ void main() {
|
|||
await expectLater(
|
||||
endpoints.simpleLogin.begin(
|
||||
notSignedIn(),
|
||||
platform: SimpleLoginPlatform.desktop,
|
||||
back: SimpleLoginReturn.loopback,
|
||||
),
|
||||
throwsA(isA<StateError>()),
|
||||
);
|
||||
|
|
@ -255,14 +258,14 @@ void main() {
|
|||
for (var i = 0; i < 30; i++) {
|
||||
await endpoints.simpleLogin.begin(
|
||||
notSignedIn(),
|
||||
platform: SimpleLoginPlatform.desktop,
|
||||
back: SimpleLoginReturn.loopback,
|
||||
);
|
||||
}
|
||||
|
||||
await expectLater(
|
||||
endpoints.simpleLogin.begin(
|
||||
notSignedIn(),
|
||||
platform: SimpleLoginPlatform.desktop,
|
||||
back: SimpleLoginReturn.loopback,
|
||||
),
|
||||
refused(OidcRefusal.tooManyAttempts),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -60,8 +60,8 @@ import 'package:halkyon_base_server/src/generated/profile/profile_refusal.dart'
|
|||
as _idszjcpz;
|
||||
import 'package:halkyon_base_server/src/generated/simplelogin/simplelogin_completed.dart'
|
||||
as _i8g1l2iv;
|
||||
import 'package:halkyon_base_server/src/generated/simplelogin/simplelogin_platform.dart'
|
||||
as _ixrtiuh0;
|
||||
import 'package:halkyon_base_server/src/generated/simplelogin/simplelogin_return.dart'
|
||||
as _igmh0yku;
|
||||
import 'package:halkyon_oidc_server/halkyon_oidc_server.dart' as _ipoqfiqr;
|
||||
import 'package:serverpod/serverpod.dart' as _is;
|
||||
import 'package:serverpod_auth_core_server/serverpod_auth_core_server.dart'
|
||||
|
|
@ -1866,7 +1866,7 @@ class _SimpleLoginEndpoint {
|
|||
|
||||
_ida.Future<_ipoqfiqr.OidcStart> begin(
|
||||
_ist.TestSessionBuilder sessionBuilder, {
|
||||
required _ixrtiuh0.SimpleLoginPlatform platform,
|
||||
required _igmh0yku.SimpleLoginReturn back,
|
||||
}) async {
|
||||
return _ist.callAwaitableFunctionAndHandleExceptions(() async {
|
||||
var _localUniqueSession =
|
||||
|
|
@ -1879,7 +1879,7 @@ class _SimpleLoginEndpoint {
|
|||
createSessionCallback: (_) => _localUniqueSession,
|
||||
endpointPath: 'simpleLogin',
|
||||
methodName: 'begin',
|
||||
parameters: _ist.testObjectToJson({'platform': platform}),
|
||||
parameters: _ist.testObjectToJson({'back': back}),
|
||||
serializationManager: _serializationManager,
|
||||
);
|
||||
var _localReturnValue =
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue