refactor(auth): land Session -> Principal, add MedewerkerAdapter (RB-13)

ADR-0002 SS3 models Zorgverlener/Medewerker as different Principal
variants with different login flows. Actor #2 (apps/behandelportal)
landed in WP-61/67 and the union never followed: grep -rn "Principal"
returned one hit, a comment. Both apps' auth/domain/session.ts stayed
byte-identical (`{ bsn, naam }`), so the backoffice's Behandelaar
carried a BSN and logged into the backoffice as a citizen, by DigiD,
under a fabricated citizen's name (login.page.ts). The divergence
ADR-0002 predicted took an orthogonal side door instead
(medewerker.interceptor.ts's X-Medewerker/X-Rollen stamp, which never
touches SessionStore) -- which is why ssp/auth and bhp/auth still
measured as 100%/84% duplicated after ADR-C-006 shared the route
guards. RB-09 (landed the day before) made the backend's
IIdentityProvider able to say "no identity" and fail closed; this
ticket is its named FE half.

Each app's auth/domain/session.ts becomes principal.ts, holding the
one Principal variant that app actually has an actor for: ssp keeps
`{ kind: 'zorgverlener', bsn, naam }` (G1 still strips the BSN before
persisting); behandelportal gets `{ kind: 'medewerker', medewerkerId,
naam, rollen }` (no BSN to strip -- G2 shape validation only). A new
MedewerkerAdapter replaces DigidAdapter in behandelportal, resolving
the existing MEDEWERKER_ID/currentRollen() dev stand-in into a
Principal; because there is no credential to check, it returns
Principal directly rather than a Result whose error variant could
never occur. login.page.ts stops being a BSN/wachtwoord form -- one
explainer line and an "Inloggen met SSO" button -- and its dead
error-handling branch goes with the Result wrapper that justified it.

Measured with tools/baseline-scan.mjs --dup: auth duplication drops
from 168/168 (ssp) and 168/200 (bhp) to 32/179 and 32/259 -- under the
backlog's <40 target. What remains is the ADR-C-006 route-guard
re-export (deliberately identical), generic test/story-file
boilerplate, and one shared fragment of the root-singleton-store
idiom -- not re-converged identity or login-flow logic. SS3's
prediction that the two actors would authenticate differently enough
to justify not sharing auth has now actually been tested, not just
asserted, and held.

Also: renamed Session.bsn to Principal.bsn in two doc comments
(libs/shared/src/infrastructure/subject.ts, subject.interceptor.ts)
that cited the old type name; regenerated
libs/shared/docs/behaviour-spec.mdx (generated file, per its own
banner); recorded the resolution in ADR-0002 as a new amendment,
replacing its "Known debt" section.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
eho
2026-08-27 16:54:26 +02:00
co-authored by Claude Opus 5
parent 988612cd7e
commit f19185ed81
22 changed files with 588 additions and 319 deletions
+11 -43
View File
@@ -26,65 +26,33 @@
<context context-type="linenumber">27</context>
</context-group>
</trans-unit>
<trans-unit id="form.verplichteVelden" datatype="html">
<source>* verplichte velden</source>
<target datatype="html">* required fields</target>
<trans-unit id="login.ssoExplainer" datatype="html">
<source>U meldt zich aan via de SSO van uw organisatie — er is geen wachtwoord nodig.</source>
<target datatype="html">You sign in through your organization's SSO — no password is needed.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/auth/ui/login-form/login-form.component.ts</context>
<context context-type="linenumber">15,18</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/registratie/ui/change-request-form/change-request-form.component.ts</context>
<context context-type="linenumber">44,46</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/shared/layout/wizard-shell/wizard-shell.component.ts</context>
<context context-type="linenumber">90,92</context>
</context-group>
</trans-unit>
<trans-unit id="login.bsnLabel" datatype="html">
<source>BSN</source>
<target datatype="html">BSN</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/auth/ui/login-form/login-form.component.ts</context>
<context context-type="linenumber">22,23</context>
</context-group>
</trans-unit>
<trans-unit id="login.bsnDescription" datatype="html">
<source>9-cijferig BSN, elfproef-geldig (demo: 123456782)</source>
<target datatype="html">9-digit BSN, valid eleven-test checksum (demo: 123456782)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/auth/ui/login-form/login-form.component.ts</context>
<context context-type="linenumber">25,28</context>
</context-group>
</trans-unit>
<trans-unit id="login.wachtwoordLabel" datatype="html">
<source>Wachtwoord</source>
<target datatype="html">Password</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/auth/ui/login-form/login-form.component.ts</context>
<context context-type="linenumber">36,37</context>
<context context-type="linenumber">17,19</context>
</context-group>
</trans-unit>
<trans-unit id="login.submit" datatype="html">
<source>Inloggen met DigiD</source>
<target datatype="html">Log in with DigiD</target>
<source>Inloggen met SSO</source>
<target datatype="html">Log in with SSO</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/auth/ui/login-form/login-form.component.ts</context>
<context context-type="linenumber">41,43</context>
<context context-type="linenumber">20,21</context>
</context-group>
</trans-unit>
<trans-unit id="login.heading" datatype="html">
<source>Inloggen</source>
<target datatype="html">Log in</target>
<source>Inloggen bij het behandelportal</source>
<target datatype="html">Log in to the treatment portal</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/auth/ui/login.page.ts</context>
<context context-type="linenumber">14,16</context>
</context-group>
</trans-unit>
<trans-unit id="login.intro" datatype="html">
<source>Log in op uw persoonlijke BIG-register omgeving.</source>
<target datatype="html">Log in to your personal BIG register environment.</target>
<source>Voor medewerkers die aanvragen beoordelen.</source>
<target datatype="html">For staff who assess applications.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/auth/ui/login.page.ts</context>
<context context-type="linenumber">17,19</context>