feat(oidc): a kept identity taken in the host's transaction, and told kept without taking it

`takeKept` takes a `transaction`, so a host's step that fails (a name
taken meanwhile) gives the identity back instead of losing it; `isKept`
tells whether an identity waits under an attempt for its caller, so a
host can check before asking more of the person.

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-23 21:21:55 +02:00
commit c6831b0b7b
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -454,6 +454,87 @@ void main() {
);
});
test("is taken in the host's transaction, which gives it back as it "
'fails', () async {
final (:attempt, :code) = await allowed('42');
await idp.keep(
session,
attempt: attempt,
identity: await idp.verify(
session,
attempt: attempt,
code: code,
),
keepFor: const Duration(minutes: 10),
);
await expectLater(
session.db.transaction((transaction) async {
await idp.takeKept(
session,
attempt: attempt,
transaction: transaction,
);
throw StateError("the host's own step failed");
}),
throwsStateError,
);
final kept = await idp.takeKept(session, attempt: attempt);
expect(kept.subject, '42');
});
test('is told kept for who began it, without being taken', () async {
final anna = await newPerson();
final (:attempt, :code) = await allowed('42', caller: anna);
expect(
await idp.isKept(session, attempt: attempt, caller: anna),
isFalse,
);
await idp.keep(
session,
attempt: attempt,
identity: await idp.verify(
session,
attempt: attempt,
code: code,
caller: anna,
),
keepFor: const Duration(minutes: 10),
);
expect(
await idp.isKept(session, attempt: attempt, caller: anna),
isTrue,
);
expect(
await idp.isKept(session, attempt: attempt, caller: anna),
isTrue,
);
expect(await idp.isKept(session, attempt: attempt), isFalse);
expect(
await idp.isKept(
session,
attempt: attempt,
caller: await newPerson(),
),
isFalse,
);
expect(
await withClock(
Clock.fixed(DateTime.now().add(const Duration(minutes: 11))),
() => idp.isKept(session, attempt: attempt, caller: anna),
),
isFalse,
);
expect(
(await idp.takeKept(session, attempt: attempt, caller: anna)).subject,
'42',
);
expect(
await idp.isKept(session, attempt: attempt, caller: anna),
isFalse,
);
});
test('an attempt not verified yet is not taken', () async {
final (:attempt, code: _) = await allowed('42');
await expectLater(