docs(adr): a module for signing in with OpenID Connect, SimpleLogin a preset (ADR 0023)
The developer's decisions of 23 Oct: the sign-in of ADR 0020 becomes a Serverpod module others can use, for any OpenID Connect provider with SimpleLogin as a preset, under BSD-3-Clause, in this repository until it runs on learn.halkyon.eu, built now as ADR 0020's step 2. Serverpod v4.0.4 has a contract for providers of one's own but nothing of OpenID Connect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
99c706210c
commit
630226b51e
2 changed files with 90 additions and 1 deletions
|
|
@ -93,7 +93,8 @@ against app.simplelogin.io on 23 Oct:
|
|||
`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`).
|
||||
unknown, with a timeout (over adapting a copy of Serverpod's verifier, or `jose`); in
|
||||
the OpenID Connect module the sign-in is built on (ADR 0023).
|
||||
- **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):
|
||||
|
|
|
|||
88
docs/adr/0023-openid-connect-module.md
Normal file
88
docs/adr/0023-openid-connect-module.md
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
# ADR 0023: Signing in with OpenID Connect, a module others can use
|
||||
|
||||
- **Status:** decided 23 Oct 2026, by the developer (D1–D4); the design below is built
|
||||
as ADR 0020's step 2, whose D12 and D13 it carries.
|
||||
- **Builds on:** ADR 0014 (halkyon_base, the first Serverpod module), ADR 0020 (signing in
|
||||
with SimpleLogin only).
|
||||
|
||||
## Context
|
||||
|
||||
ADR 0020 has learn.halkyon.eu sign people in with SimpleLogin, through OpenID Connect's
|
||||
code flow. Asked on 23 Oct whether that could be "a separate module that others may use
|
||||
as well at their convenience with Serverpod", what Serverpod v4.0.4 offers:
|
||||
|
||||
- **A contract for providers of one's own**: an `IdentityProvider`, built by an
|
||||
`IdentityProviderBuilder` the host passes to `AuthServices` beside the stock ones, an
|
||||
endpoint on `IdpBaseEndpoint`, an account model linking the provider's ID to
|
||||
Serverpod's `AuthUser`, and a controller on the device ("Custom providers" in
|
||||
Serverpod's documentation, still being written on 23 Oct). `OAuth2PkceUtil` exchanges
|
||||
a code, PKCE optional, and is exported.
|
||||
- **No OpenID Connect**: no provider for it, no discovery, and no exported check of ID
|
||||
tokens (Google's and Firebase's `IdTokenVerifier` is internal and refuses tokens
|
||||
without `typ`, ADR 0020's D12).
|
||||
|
||||
## Decisions (23 Oct 2026, the developer's)
|
||||
|
||||
- **D1 OpenID Connect, with a SimpleLogin preset** (recommended, over SimpleLogin alone):
|
||||
any provider that follows OpenID Connect's code flow (Keycloak, Authentik, Zitadel,
|
||||
Authelia, Pocket ID, …), which Serverpod lacks; SimpleLogin a preset that knows its
|
||||
ways (no PKCE, no `typ`). Only SimpleLogin is tried live; the rest is tested to the
|
||||
specification. The packages' names stay generic.
|
||||
- **D2 BSD-3-Clause** (recommended, over Apache-2.0, MIT, and AGPL v3 as Halkyon's
|
||||
other packages): Serverpod's own licence, so whoever builds on Serverpod can take it,
|
||||
closed apps too.
|
||||
- **D3 In this repository, published later** (recommended, over a repository of its own
|
||||
now, or offering it to Serverpod): under `packages/` with `publish_to: none`, so a
|
||||
change to it and to Halkyon is one commit; to pub.dev once it runs on
|
||||
learn.halkyon.eu, asked again before, as publishing is public.
|
||||
- **D4 Now, as ADR 0020's step 2** (recommended, over moving it out after v0.20.0): the
|
||||
boundary drawn first costs less than pulling Halkyon's code apart later, a table
|
||||
moved between modules included; v0.20.0 comes about a day later.
|
||||
|
||||
## Design
|
||||
|
||||
1. **Packages:** `halkyon_oidc_server`, a Serverpod module (nickname `oidc`), with its
|
||||
generated client `halkyon_oidc_client`; `halkyon_oidc_flutter`, the device's half,
|
||||
with ADR 0020's step 4. halkyon_base builds on the module; nothing of Halkyon's is in
|
||||
it.
|
||||
2. **Providers:** a host names one or more, each under a key of its own that never
|
||||
changes (`simplelogin`): the issuer, the endpoints (from the provider's discovery
|
||||
document, checked to name the same issuer, or given), the client's ID and secret,
|
||||
the scopes, PKCE or not, the signing algorithms its tokens may use (RS256 unless
|
||||
said), and the callback address the provider has registered. The SimpleLogin preset
|
||||
fills in all but the client and the callback.
|
||||
3. **What it keeps:** a person's identity at a provider, the provider's key and the
|
||||
`sub` (`halkyon_oidc_identity`): each `sub` one person's at a provider, one per
|
||||
person and provider. Nothing of what the token says besides; the claims go to the
|
||||
host's code, which decides (Halkyon drops them, ADR 0020's 4).
|
||||
4. **Attempts** (ADR 0020's D13): `halkyon_oidc_attempt`, the ID PostgreSQL's
|
||||
`gen_random_uuid()` (122 random bits), the secret only the device holds; the state
|
||||
and the nonce, 32 random bytes each, and PKCE's verifier where used; who began it,
|
||||
if signed in; ten minutes, taken once. The host may begin the state with a marker of
|
||||
its own (ADR 0020's forwarding page reads one).
|
||||
5. **Checking a sign-in** (ADR 0020's D12): the code exchanged with the client's secret
|
||||
(`OAuth2PkceUtil`); the ID token's header without `typ` asked for, its algorithm one
|
||||
of the provider's, its key the one its `kid` names in the provider's keys (kept an
|
||||
hour, fetched again for an unknown `kid` at most once a minute, every fetch with a
|
||||
timeout); the issuer, the audience (and `azp` with more than one), the expiry and the
|
||||
time issued, a minute of clocks' difference allowed, the nonce the attempt's.
|
||||
6. **Two ways to use it:**
|
||||
- **The stock way**, as Serverpod's providers: the host's endpoint on
|
||||
`OidcIdpBaseEndpoint`, `begin` and `login`: a known `sub` gets a session, an
|
||||
unknown one a new person; linking to the signed-in caller where the attempt was
|
||||
begun so.
|
||||
- **The parts**, for a host with rules of its own (Halkyon: the register, a handle
|
||||
before an account, ADR 0016 and 0020): `begin`, `verify` (the identity proven, and
|
||||
its claims, touching no account), `link`, `accountOf`, `unlink`, called from its
|
||||
own endpoints.
|
||||
7. **The forwarding page** goes into the module (`addOidcCallbackForwarding`), with the
|
||||
targets the host fixes: what ADR 0020's 3 has Halkyon need, a provider that takes
|
||||
https callbacks alone, each registered exactly, others need alike.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Halkyon's sign-in rests on a module of its own making, with its own tests and
|
||||
README, kept usable by others: its interface changes with care once published.
|
||||
- Serverpod hosts get OpenID Connect; a provider other than SimpleLogin may still need
|
||||
a fix the specification's tests didn't foresee.
|
||||
- The module's tables are part of every host's migrations, as halkyon_base's are.
|
||||
Loading…
Reference in a new issue