docs(adr): ID tokens with dart_jsonwebtoken, attempts in a table, steps 2–4 in one release (ADR 0020)

The developer's decisions of 23 Oct, evening (D12–D15). Serverpod's own
ID-token verifier, chosen first, refuses every SimpleLogin token: they
carry no typ header, and it checks one with dart_jsonwebtoken's default,
with no way to turn that off. So the register checks them with
dart_jsonwebtoken itself. Attempts go into a table, steps 2 to 4 come in
one release, and the proposal stands as written.

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 19:49:38 +02:00
commit 99c706210c
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -1,7 +1,7 @@
# ADR 0020: Signing in with SimpleLogin only
- **Status:** decided 23 Oct 2026, by the developer (D1–D11 below); the details under
*Proposal* that no decision names are proposed, for review before building.
- **Status:** decided 23 Oct 2026, by the developer (D1–D15 below), the *Proposal*
with it (D15).
- **Built (23 Oct 2026), step 1:** the forwarding page (3),
`/simplelogin-callback` on the register's web server (halkyon_base's
`addSimpleLoginCallbackRoute`, Caddy forwarding the path), ahead of the sign-in, so
@ -82,6 +82,26 @@ against app.simplelogin.io on 23 Oct:
the reminder names both days.
- **D11 An account left unfinished (no keys) is deleted after a day** (recommended, over
finishing it at the next sign-in); its handle is free again.
- **D12 The ID token checked with `dart_jsonwebtoken` itself** (23 Oct, evening,
recommended), already in the lock through Serverpod's sign-ins. The developer first
chose Serverpod's own verifier (`IdTokenVerifier`, which Google's and Firebase's
sign-ins use; not exported, so imported from its `src/`), which can't: SimpleLogin's
ID tokens carry no `typ` in their header (`jose_utils.make_id_token` writes `alg` and
`kid` alone, and jwcrypto 0.8 adds nothing), and the verifier calls
`JWT.verify` with its default `checkHeaderType: true`, so it refuses every one, and
takes no options to change that. It also tries every key rather than the one the
`kid` names, never fetches the keys again for an unknown `kid` (an hour of failed
sign-ins after SimpleLogin changes its key) and fetches them without a timeout. So:
`checkHeaderType` off, `RS256` alone, the key the `kid` names, fetched again for one
unknown, with a timeout (over adapting a copy of Serverpod's verifier, or `jose`).
- **D13 Attempts in a table** (recommended, over the server's memory): a restart or a
deploy in the middle of a sign-in loses none, and the database takes each once.
- **D14 Steps 2 to 4 in one release** (recommended, over the server's side alone first):
the module's side, the keys without SRP with handles and without anonymous accounts,
and the apps' screens come as one release, so people get a sign-in that works; the
cut-off (step 5) follows 30 days after it.
- **D15 The proposal (1–12) as written** (23 Oct, evening); what building it shows
otherwise comes back to the developer.
- **Also (23 Oct):** the developer's own account moves by adding SimpleLogin to it while
signed in with GitHub; the instance's SimpleLogin app goes up for approval once
halkyon.app/learn exists, before learn.halkyon.eu switches.