ADR-0063: User Identity Binds on Subject Only (Issuer Becomes Provenance)¶
- Status: accepted
- Date: 2026-07-12
- Deciders: Nicolas Burri ("I agree with A"), after the LAN-testing incident: switching
LQMS_PUBLIC_HOSTre-keyed every identity — login worked, but the user arrived as a freshly-provisioned stranger (empty scope dropdown, zero documents). His constraint: "I cannot guarantee that URLs never change." - Relates to: ADR-0011 (OIDC BFF — token validation unchanged), ADR-0012 (bootstrap admin — matcher simplified), ADR-0060 (Keycloak state rides every backup — the continuity this ADR depends on).
Context¶
Users were bound on the identity key (issuer, subject). The issuer string derives from the
deployment's public URL (KC_HOSTNAME → token iss), so any hostname change, http→https
migration, or reverse-proxy move silently orphans every identity — a class of operational
failure whose symptom (valid login, empty system) is indistinguishable from data loss. The
OIDC-pure key is only necessary under multi-issuer federation, which LQMS structurally does
not have: each instance ships its own Keycloak, and corporate IdPs integrate by brokering
through that Keycloak — LQMS faces exactly one issuer by construction.
Decision¶
- Identity key =
subjectalone (Keycloak user UUID; unique within the single realm). Provisioning, invite binding, and the ADR-0012 bootstrap matcher all look up by subject only. - Token validation is NOT weakened:
quarkus.oidc.token.issuerstays pinned — tokens from any foreign issuer are rejected before identity even enters the picture. The DB key stops conflating who the person is with what URL Keycloak was on that day. issuercolumn stays as PROVENANCE (recorded at bind, refreshed on login) — audit value and forensics, no lookup semantics. The unique constraint moves from (issuer, subject) to (subject) (partial: subject not null — INVITED users bind later).- Multi-issuer federation, if ever real, re-enters the key via its own ADR — the column and the audit trail make that migration well-defined.
- Deployment consequence: changing the public host is a config-only change (env var +
pinned issuer follow
LQMS_PUBLIC_HOSTautomatically); identities persist. The demo-scale "reseed on host change" rule in deploy/README dies.
Consequences¶
- Identity continuity now rests solely on Keycloak state continuity (subjects are Keycloak UUIDs): the realm-with-users export in every backup (ADR-0060) is the guarantee; losing the Keycloak volume orphans users under ANY key design.
- Pre-production: no data migration — constraint change ships as an additive Flyway migration; dev/demo instances reseed as usual (ADR-0058 culture).
- The hosting-arc "canonical URL + issuer migration path" consideration (STATUS) collapses to: pick a URL, change it freely later.