docs(sharing): document handing a record over, including through single sign-on

This commit is contained in:
Marc-André Bombeck committed 2026-10-15 23:39:39 +02:00
1 parent 9fd0cf264d
commit 8c406c3f8b
2 files changed
+62 -5

No files matched your search

+52 -5
View File
@@ -231,11 +231,58 @@ as something to log in as.
person it was sent to, so the admin notification diagnostic can answer
"who was this for" and "whose record was it about" separately.
There is no emancipation flow: no route, no control and no copy suggests
a managed profile can be turned into an ordinary account. The data model
leaves room for it (the credentials are nullable and the managed marker
can be cleared) and clearing that marker stops the notification fan-out,
but nothing in v1.37.0 exercises any of that.
### Handing a record over to its person
Since v1.42 a guardian can hand a managed profile over to the person it
describes, so the record becomes their own account with its whole history.
Nothing is copied or moved; the profile's row gets credentials, loses the
managed marker, and stops fanning reminders out to its guardians.
- A guardian with a fresh second factor creates a one-time link (valid for
one, seven or fourteen days, shown once) and proposes, for each guardian,
no access, view only or manage. Only a keyed hash of the link is stored,
at most one link per profile is open, and any guardian can withdraw it
without a second factor.
- The person opens the link, sees the record's name, the expiry and who
keeps which access, and chooses how they sign in. The claim runs as one
transaction: the link is consumed, the proposals are applied, open
invitations the record offered are withdrawn, and the disclaimer,
onboarding and AI consent are reset so the person gives them themselves.
After the first sign-in they confirm or change each guardian's access.
- How the person signs in depends on the instance:
| Instance | Ways in |
| --------------------------------- | -------------------------- |
| No SSO configured | Password |
| SSO configured, `OIDC_ONLY` unset | Password or single sign-on |
| `OIDC_ONLY="true"` | Single sign-on only |
The guardian's panel and the claim page both show which ways are open.
- **Through single sign-on** (v1.43) the person picks a username and
continues to the identity provider. When they come back, the provider's
`(issuer, sub)` pair is bound to the record in the same transaction, the
address is taken from the provider only when it asserts
`email_verified: true` (otherwise the account has no address), and no
password is set. Refused, with the record left exactly as it was:
a provider identity that already signs in to an account on this instance
(one identity, one account; the refusal is audited on that account as
`auth.oidc.claim_conflict`), a verified address another account holds,
a username taken in the meantime, a link that was used, withdrawn,
replaced or expired while the person was at the provider, and a browser
that is signed in to another account.
- The link never appears in a URL the server builds: the claim page sends it
in request bodies only, and the single-sign-on leg carries a sealed,
ten-minute, HttpOnly cookie that names the link by its id. The `/claim/`
path and `hlp_` tokens are scrubbed from logs.
- The iOS app does not claim. The person claims in a browser and then signs
in to the app as usual (with SSO, through the app's own SSO sign-in).
Audit rows: `managed_profile.handover.created` and `.revoked` on the record,
`managed_profile.claim_sso.started` when a single-sign-on claim starts,
`managed_profile.claimed` and `auth.claim` (with `method: "password"` or
`"sso"`) when it lands, and `managed_profile.handed_over` on each guardian's
own account.
## Seeing what happened
+10
View File
@@ -112,6 +112,12 @@ Client registration essentials only — the IdP's own docs cover the rest.
MFA in the IdP's policy engine — that is the point of delegating
authentication. HealthLog-side 2FA remains available on top for the
step-up gates above.
- **Claiming a managed profile binds a fresh identity only.** When a
person claims a managed profile through SSO, their `(issuer, sub)` is
bound to that record, and only if no account on the instance already
holds it, and the provider's address is used only when it is verified
and not already taken. A claim can therefore never attach somebody's
existing sign-in to a second record or take over an existing account.
- **Linking is once.** There is no unlink/relink flow in this cut; the
pin is deliberately sticky. Moving an account to a new IdP identity is
an operator decision (a manual `users.oidc_issuer` / `users.oidc_sub`
@@ -134,6 +140,10 @@ Consequences to accept before turning it on:
- **Self-disabling on half-config.** The flag only takes effect while
the three provider vars are fully set, so a typo in the provider group
can never lock every user out.
- **Managed profiles are still handed over** (v1.43). The person claims the
record through the identity provider instead of setting a password; the
password claim is refused. See
[Handing a record over to its person](account-sharing.md#handing-a-record-over-to-its-person).
### Break-glass (IdP down or misconfigured)