The `simpleLogin` endpoint on halkyon_oidc's SimpleLogin preset: `begin` for the app and platform (the state's marker for the forwarding page), at most 30 an hour per network; `complete` with SimpleLogin's code, signed out a session for the account with that SimpleLogin ID, signed in SimpleLogin added to the caller's account, who becomes identified and anonymous no longer. Someone new gets no account yet: their identity waits 30 minutes under the attempt's ID (D16), with a handle suggested from their name, `anna_mueller` (D17), for the sign-up of step 3. The register alone holds the instance's SimpleLogin app (`simpleLoginClientId`, `simpleLoginClientSecret`) and exchanges codes; a member asks it for both steps and issues its own session. `SIGN_IN_WAYS` says which ways the register offers, the instance's basics and the ways in carry `simpleLogin`, GitHub links nobody more where SimpleLogin is offered, and Learn's and Todo's pruning forgets the unfinished sign-ins hourly. No migration: the tables are halkyon_oidc's, already in place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
116 lines
3.7 KiB
Dart
116 lines
3.7 KiB
Dart
import 'package:halkyon_base_server/base.dart';
|
|
import 'package:halkyon_oidc_server/testing.dart';
|
|
import 'package:serverpod/serverpod.dart';
|
|
import 'package:serverpod_auth_idp_server/core.dart';
|
|
|
|
import '../test_tools/serverpod_test_tools.dart';
|
|
|
|
/// The app the tests' host is.
|
|
const testAppName = 'Halkyon Test';
|
|
|
|
/// The id of the app the tests' host serves.
|
|
const testApp = 'test';
|
|
|
|
/// Where the tests' host says its project publishes its source.
|
|
const testUpstreamSourceCodeUrl = 'https://code.example.org/halkyon_test';
|
|
|
|
/// The oldest app build the tests' host works with.
|
|
const testMinimumAppBuild = 7;
|
|
|
|
/// Sets the module up on the pod [sessionBuilder]'s sessions run on, as a
|
|
/// host does before its pod starts, for [app], with [deletePersonData] as
|
|
/// its deletions and [profilePageNote] at the foot of profile pages, and
|
|
/// authentication exactly as hosts run it
|
|
/// (`auth_config.dart`). Not on `Serverpod.instance`: that is the pod made
|
|
/// last, and a test file with several `withServerpod` groups has a pod each.
|
|
void useBase(
|
|
TestSessionBuilder sessionBuilder, {
|
|
String app = testApp,
|
|
List<PersonDataDeletion> deletePersonData = const [],
|
|
ProfilePageNote? profilePageNote,
|
|
AccountsMode accounts = const HoldsRegister(),
|
|
}) {
|
|
final pod = sessionBuilder.build().serverpod;
|
|
HalkyonBase.configure(
|
|
pod,
|
|
HalkyonBase(
|
|
app: app,
|
|
appName: testAppName,
|
|
upstreamSourceCodeUrl: testUpstreamSourceCodeUrl,
|
|
minimumAppBuild: testMinimumAppBuild,
|
|
deletePersonData: deletePersonData,
|
|
profilePageNote: profilePageNote,
|
|
accounts: accounts,
|
|
),
|
|
);
|
|
// No password sign-in unless a test gives it keys, and no SimpleLogin
|
|
// unless a test gives it an app, whatever the test run's passwords and
|
|
// environment hold.
|
|
usePasswordKeys(pod, null);
|
|
useOfferedWays(pod, null);
|
|
useSimpleLoginApp(pod, null);
|
|
AuthServices.set(
|
|
tokenManagerBuilders: tokenManagerBuilders,
|
|
identityProviderBuilders: identityProviderBuilders(pod),
|
|
);
|
|
}
|
|
|
|
/// SimpleLogin on the pod [sessionBuilder]'s sessions run on (ADR 0020), the
|
|
/// instance's app answered by [fake] (none for null), and authentication set
|
|
/// up anew as hosts run it.
|
|
void useSimpleLogin(TestSessionBuilder sessionBuilder, FakeProvider? fake) {
|
|
final pod = sessionBuilder.build().serverpod;
|
|
useSimpleLoginApp(
|
|
pod,
|
|
fake == null
|
|
? null
|
|
: (
|
|
clientId: fake.clientId,
|
|
clientSecret: fake.clientSecret,
|
|
callbackUrl: fake.callbackUrl,
|
|
),
|
|
client: fake?.client,
|
|
);
|
|
AuthServices.set(
|
|
tokenManagerBuilders: tokenManagerBuilders,
|
|
identityProviderBuilders: identityProviderBuilders(pod),
|
|
);
|
|
}
|
|
|
|
/// Signs [person] in on a new device and returns the new session's id.
|
|
Future<UuidValue> signInNewDevice(
|
|
Session session,
|
|
UuidValue person, {
|
|
Set<Scope> scopes = const {},
|
|
String method = 'test',
|
|
}) async {
|
|
Future<Set<UuidValue>> sessionIds() async => {
|
|
for (final serverSideSession in await ServerSideSession.db.find(
|
|
session,
|
|
where: (t) => t.authUserId.equals(person),
|
|
))
|
|
serverSideSession.id!,
|
|
};
|
|
|
|
final before = await sessionIds();
|
|
await AuthServices.instance.tokenManager.createToken(
|
|
session,
|
|
authUserId: person,
|
|
method: method,
|
|
scopes: scopes,
|
|
);
|
|
return (await sessionIds()).difference(before).single;
|
|
}
|
|
|
|
/// A person as an app's first launch creates them: anonymous.
|
|
Future<UuidValue> newAnonymousPerson(
|
|
Session session, {
|
|
DateTime? createdAt,
|
|
}) async {
|
|
final person = (await AuthServices.instance.authUsers.create(session)).id;
|
|
await AnonymousAccount.db.insertRow(
|
|
session,
|
|
AnonymousAccount(authUserId: person, createdAt: createdAt),
|
|
);
|
|
return person;
|
|
}
|