Best-effort serve for bad stored config + admin self-lockout guards
Serving never refuses to start because of stored realm config: the registry build sanitizes best-effort and warns — misfiled origin entries are reclassified (a cross-domain origins entry is served as a related origin) or dropped, collisions resolve first-come-wins, over-cap related lists truncate, unsalvageable realms are skipped. Fixing the stored config stays the admin interface's job, and it stays reachable on any working realm. Only a config with no servable realm at all is fatal. Admin realm writes stay strict and gain self-lockout guards: an update that would leave the admin's current host unable to run ceremonies for the realm they are on is refused (unless an auth host takes over ceremonies), and deleting the realm currently in use is refused.
This commit is contained in:
+31
-16
@@ -172,9 +172,9 @@ class Config(msgspec.Struct, omit_defaults=True):
|
||||
outside it, are capped (default 5), and must not collide with another
|
||||
realm's rp-id/auth-host/related origins nor fall inside another
|
||||
realm's domain. Misfiled entries (cross-domain in `origins`, in-domain
|
||||
in `related_origins`) are rejected. These rules are enforced both at
|
||||
startup and at admin write time. Origins are never _implicitly_
|
||||
cross-domain.
|
||||
in `related_origins`) are rejected. These rules are enforced at admin
|
||||
write time; at startup the stored config is sanitized best-effort
|
||||
instead (§3.2). Origins are never _implicitly_ cross-domain.
|
||||
|
||||
### 3.2 CLI: bootstrap (`paskia init`) vs. serve (`paskia`)
|
||||
|
||||
@@ -201,12 +201,18 @@ instance:
|
||||
selects `<rp-id>.paskiadb` by name; the others are left in place.
|
||||
- **`paskia`** — serve. Takes **no realm options**; only `--listen`
|
||||
(per-run override of stored `Config.listen`, never persisted). Startup:
|
||||
open `paskia.kantadb` → validate the stored realm set cross-realm
|
||||
(rp-ids distinct; auth hosts distinct from each other and from every
|
||||
rp-id; related origins capped and collision-free) → build the realm
|
||||
registry → serve. The serve command never converts databases: with no
|
||||
`paskia.kantadb`, the startup error points at `paskia init`, or at
|
||||
`paskia migrate` when legacy `*.paskiadb` candidates are present.
|
||||
open `paskia.kantadb` → sanitize the stored realm set best-effort →
|
||||
build the realm registry → serve. Sanitization never refuses to start:
|
||||
misfiled origin entries are reclassified (a cross-domain `origins`
|
||||
entry is served as a related origin) or dropped, colliding
|
||||
auth-hosts/related origins resolve first-come-wins, over-cap related
|
||||
lists truncate, and unsalvageable realms are skipped — each producing
|
||||
a startup warning, because fixing the stored config is the admin
|
||||
interface's job and it must stay reachable to do so. Only a config
|
||||
with no servable realm at all is fatal. The serve command never
|
||||
converts databases: with no `paskia.kantadb`, the startup error points
|
||||
at `paskia init`, or at `paskia migrate` when legacy `*.paskiadb`
|
||||
candidates are present.
|
||||
|
||||
Nested rp-ids are allowed (longest-suffix dispatch determinism). Adding a
|
||||
child rp-id moves **no data** — users are global; only new ceremonies
|
||||
@@ -375,9 +381,16 @@ effective_auth_host(realm) = realm.auth_host or first_configured_auth_host or No
|
||||
Changing a realm's rp-id itself is **not supported** (it would orphan
|
||||
every credential stamped with the old rp-id) — delete and recreate
|
||||
instead.
|
||||
- Delete: refused for the last remaining realm and while any credential
|
||||
- Delete: refused for the last remaining realm, while any credential
|
||||
carries the realm's rp-id (re-enroll or delete those credentials
|
||||
first); cascades nothing else (users/orgs are global).
|
||||
first), and for the realm the admin is currently using; cascades
|
||||
nothing else (users/orgs are global).
|
||||
- **Lockout guard**: an update that would make the admin's current
|
||||
host unable to run passkey ceremonies for the realm they are on is
|
||||
refused (unless an auth host takes over ceremonies — it is always
|
||||
allowed). Fixing a broken stored config is always possible: serve
|
||||
sanitizes it best-effort (§3.2) so the admin interface stays
|
||||
reachable on a working realm.
|
||||
- The admin UI has a Realms section with a table (rp-id, name,
|
||||
effective auth host, sign-in site and related domain counts),
|
||||
per-row edit/delete and an add-realm dialog. The dialog edits the
|
||||
@@ -431,9 +444,9 @@ effective_auth_host(realm) = realm.auth_host or first_configured_auth_host or No
|
||||
background cleanup task (DB is global).
|
||||
- The kanta bootstrap hook only ever fires for a database created by
|
||||
`paskia init` or `paskia migrate`; the serve command never bootstraps.
|
||||
- The registry is built from the stored `Config` after open; per-realm
|
||||
`Passkey` instances constructed (each realm's origins validated at
|
||||
startup — fail-fast, including related-origin cap checks).
|
||||
- The registry is built from the stored `Config` after open, sanitized
|
||||
best-effort (§3.2) so startup never fails on config content; per-realm
|
||||
`Passkey` instances are constructed from the sanitized config.
|
||||
- The admin-credential check runs at serve startup and reprints a usable
|
||||
registration link when the admin lacks a credential under the default
|
||||
realm (§9).
|
||||
@@ -476,8 +489,10 @@ effective_auth_host(realm) = realm.auth_host or first_configured_auth_host or No
|
||||
origins sharing an rp-id share one security boundary — do not mix trust
|
||||
levels within a realm.
|
||||
- **Realm administration**: realm create/update/delete is gated on
|
||||
`auth:admin` — deployment-wide by design; validation runs on every
|
||||
write, not just at startup.
|
||||
`auth:admin` — deployment-wide by design. Writes are strictly
|
||||
validated (cross-realm rules plus self-lockout guards); startup never
|
||||
refuses a stored config — it sanitizes with warnings so the admin
|
||||
interface stays reachable to fix problems.
|
||||
- **Passkeys**: rp-id binding browser-enforced and server-recorded;
|
||||
ceremonies, credential scans, exclude/allow lists all scoped to the
|
||||
origin realm's rp-id. No cross-realm oracle in the scan.
|
||||
|
||||
Reference in New Issue
Block a user