feat(#13): S-12c-1 — behandel BFF auth + werkbak (ADR-0013) #85

Merged
not merged 7 commits from feat/13-behandel-bff-auth-werkbak into main 2026-07-15 09:54:03 +00:00
Contributor

What & why

First half of S-12c (behandel-portal backend), per ADR-0013 (decisions recorded in #84):

  • BFF multi-realm auth. A second JWT bearer scheme (medewerker) alongside the default digid scheme. On validation it lifts Keycloak's realm_access.roles onto the principal, and a behandelaar policy (medewerker scheme + behandelaar role) gates /behandel/*. Self-service keeps the digid scheme.
  • Werkbak = Flowable tasks. The domain Werkbak query reads the open Beoordelen tasks (§8.2, S-12b's IUserTaskClient) and enriches each with its aggregate's bsn + status; GET /behandel/werkbak (domain) is proxied by the BFF GET /behandel/werkbak behind the behandelaar policy. The read projection stays the anonymous openbaar model (no premature IN_BEHANDELING/personal-data plumbing — deferred in ADR-0008).

Behavior: /behandel/werkbak is 401 without a token, 403 for a medewerker lacking the role, 200 + werkbak for a behandelaar.

S-12c-2 (next): POST /behandel/registrations/{id}/decide → domain decision + complete the Flowable task.

Definition of Done

  • Linked issue: #13 (umbrella, refs); closes the adr-proposal #84
  • Tests first; red → green per layer
  • Unit + acceptance green (make unit): domain 78, bff 23, acceptance 9 (+ acl/event-subscriber unaffected)
  • api-client test green; openapi.json regenerated (drift guard passes)
  • Mutation ≥ break(90): domain 100%, bff 100%
  • ADR-0013 added; Keycloak__MedewerkerAuthority wired into compose
  • CI green (pending)

Part of #13. closes #84

## What & why First half of **S-12c** (behandel-portal backend), per **ADR-0013** (decisions recorded in #84): - **BFF multi-realm auth.** A second JWT bearer scheme (`medewerker`) alongside the default `digid` scheme. On validation it lifts Keycloak's `realm_access.roles` onto the principal, and a `behandelaar` policy (medewerker scheme + `behandelaar` role) gates `/behandel/*`. Self-service keeps the digid scheme. - **Werkbak = Flowable tasks.** The domain `Werkbak` query reads the open `Beoordelen` tasks (§8.2, S-12b's `IUserTaskClient`) and enriches each with its aggregate's bsn + status; `GET /behandel/werkbak` (domain) is proxied by the BFF `GET /behandel/werkbak` behind the behandelaar policy. The read projection stays the anonymous openbaar model (no premature `IN_BEHANDELING`/personal-data plumbing — deferred in ADR-0008). Behavior: `/behandel/werkbak` is **401** without a token, **403** for a medewerker lacking the role, **200 + werkbak** for a behandelaar. **S-12c-2** (next): `POST /behandel/registrations/{id}/decide` → domain decision + complete the Flowable task. ## Definition of Done - [x] Linked issue: #13 (umbrella, `refs`); closes the adr-proposal #84 - [x] Tests first; red → green per layer - [x] Unit + acceptance green (`make unit`): domain 78, bff 23, acceptance 9 (+ acl/event-subscriber unaffected) - [x] api-client `test` green; openapi.json regenerated (drift guard passes) - [x] Mutation ≥ break(90): **domain 100%, bff 100%** - [x] ADR-0013 added; `Keycloak__MedewerkerAuthority` wired into compose - [x] CI green (pending) Part of #13. closes #84
not added 6 commits 2026-07-15 09:10:35 +00:00
Red — the Werkbak use-case and WerkbakItem do not exist yet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Werkbak use-case reads the open Beoordelen tasks (§8.2) and enriches each with
its aggregate's bsn + status; exposed as GET /behandel/werkbak for the BFF to proxy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Red — the medewerker JWT scheme, the behandelaar policy, and the werkbak endpoint
do not exist yet (endpoint 404s).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a second JWT bearer scheme for the medewerker realm; on validation it lifts
Keycloak's realm_access.roles onto the principal so the behandelaar policy can
require the role. /behandel/werkbak proxies the domain werkbak behind that policy
(401 without a token, 403 without the role). openapi.json + api-client regenerated;
Keycloak__MedewerkerAuthority wired into compose.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
test(acceptance): implement GetWerkbakAsync on the acceptance domain stub (refs #13)
Some checks failed
CI / lint (pull_request) Successful in 1m25s
CI / build (pull_request) Successful in 1m13s
CI / unit (pull_request) Failing after 1m17s
CI / frontend (pull_request) Successful in 2m44s
CI / mutation (pull_request) Successful in 5m18s
CI / verify-stack (pull_request) Has been cancelled
e0db3ace6e
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
not added the type:slicearea:domainarea:portal-behandelarea:bff labels 2026-07-15 09:10:36 +00:00
not added 1 commit 2026-07-15 09:24:40 +00:00
fix(bff): pin test JWT schemes to a static config so no OIDC metadata fetch (refs #13)
All checks were successful
CI / lint (pull_request) Successful in 1m28s
CI / build (pull_request) Successful in 1m18s
CI / unit (pull_request) Successful in 1m40s
CI / frontend (pull_request) Successful in 3m2s
CI / mutation (pull_request) Successful in 6m16s
CI / verify-stack (pull_request) Successful in 5m14s
6e65738c04
The medewerker scheme intermittently hung ~2s fetching OIDC metadata from its
(unreachable) test authority and then 401'd — clearing Authority alone left
JwtBearer's PostConfigure free to build a ConfigurationManager under CI timing. Give
both test schemes a StaticConfigurationManager so metadata is never fetched, making
token validation deterministic. Also harden AddRealmRoles to never throw (a throw in
OnTokenValidated surfaces as a 401) — a malformed realm_access yields no roles (403).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
not merged commit 9c3da48d8e into main 2026-07-15 09:54:03 +00:00
Sign in to join this conversation.