Skip to content

The public origin is declared, not derived

Status: Accepted — shipped 2026-09-03 (PR #214) · Decided: 2026-09-03 · Supersedes: ADR 0002

Context

ADR 0002 decided the opposite: a realm's public origin should never be configured, only derived from the incoming request. The reasoning was sound at the time — a configured origin is a value that can be wrong, and a derived one cannot drift from reality.

Deriving it turned out to be the thing that drifts. Realm.PrimaryDomain is a host name, and it has to stay one: it doubles as the WebAuthn RP ID and as the cookie domain, and neither permits a scheme or a port. So the origin had to be reconstructed as https://{PrimaryDomain} on every use. That reconstruction is a guess, and it is wrong in every deployment that is not a plain HTTPS reverse proxy on the default port — a development host on http://localhost:4300, an installation reached on a non-standard port, anything where the scheme is not https.

The failure mode is quiet and unpleasant: magic links, verification mails and installation links are all built against the origin, and a wrong origin produces a link that looks right and lands nowhere. Passkeys make it worse, because the accepted WebAuthn origin has to match the one the browser actually presents.

Decision

The public origin is declared as its own field.

  • Realm.PublicBaseUrl holds the absolute base URL users actually reach the realm at (https://auth.example.com, http://localhost:4300), without a trailing slash. Every outbound user-facing link is built against it, and it is an accepted WebAuthn origin.
  • PrimaryDomain stays a bare host name and keeps its two jobs, RP ID and cookie domain. The two values are no longer derived from one another.
  • First installation records the very origin its installation link was issued for, so the common case needs no configuration at all.
  • recover realm-set-public-url changes it afterwards, which is what a deployment that moves behind a different proxy needs.
  • PublicBaseUrl is nullable. Null — every realm created before the field existed — falls back to https://{PrimaryDomain}, the behaviour ADR 0002 specified. Nothing had to be migrated.

Consequences

  • The value can now be wrong, which was ADR 0002's objection. It is mitigated rather than dismissed: the value is captured automatically at installation from the origin that demonstrably worked, and there is one explicit command to correct it.
  • Non-HTTPS and non-default-port deployments are supported without special cases in link building.
  • The invariant "the WebAuthn RP ID is a registrable suffix of the origin's host" is now checkable, because both halves exist as separate, explicit values.
  • ADR 0002 is kept as the historical record rather than deleted. It documents an argument that was reasonable and that the deployment reality overturned; that is worth reading, not erasing.

Released under the Apache-2.0 License.