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
5 changed files with 201 additions and 182 deletions
|
|
@ -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 =
|
||||
|
|
|
|||
Loading…
Reference in a new issue