S-07: the BFF (Backend-for-Frontend) — the single backend the portals talk to (§8.3). For the
walking skeleton it exposes:
POST /self-service/registrations — requires a valid Keycloak digid JWT; extracts the bsn
claim and forwards it to the Domain Service (#6), returning 202. Missing/invalid/expired token → 401.
GET /openbaar/register?q=… — anonymous public lookup (S-09) → reads the projection (#7) and
returns only public-safe fields (id + status; the bsn never leaves the BFF).
Auth is testable without infra:WebApplicationFactory tests reconfigure JWT bearer with a
local test signing key, so valid/invalid/expired/wrong-key tokens are exercised in-process. A
Reqnroll acceptance scenario drives the same over HTTP. Real Keycloak validation is the live-stack verify-bff check (mints a real digid token via direct grant against keycloak:8080).
Issuer consistency: Keycloak start-dev derives the issuer from the request host, so the BFF
authority and the verify token request both use keycloak:8080 — no issuer mismatch (ADR-0010).
OpenAPI:services/bff/openapi.json is generated (typed 202/400/401 + 200 schemas; the
volatile servers block is cleared for determinism) and guarded by a drift test — S-08's client is
generated from it.
Mutation: 100% on the BFF's pure logic (OpenbaarProjection, break 90). Program.cs (wiring)
and DownstreamClients.cs (HTTP adapters) are excluded — covered by the endpoint tests + verify-bff,
consistent with how the other services scope Stryker.
Scope: behandelaar/beheer endpoints and downstream service-to-service auth are out of scope
(later slices), per the issue and ADR-0010.
## What & why
S-07: the **BFF (Backend-for-Frontend)** — the single backend the portals talk to (§8.3). For the
walking skeleton it exposes:
- `POST /self-service/registrations` — requires a valid Keycloak `digid` JWT; extracts the `bsn`
claim and forwards it to the Domain Service (#6), returning 202. Missing/invalid/expired token → 401.
- `GET /openbaar/register?q=…` — **anonymous** public lookup (S-09) → reads the projection (#7) and
returns only public-safe fields (`id` + `status`; the bsn never leaves the BFF).
Closes #8
Closes #63
## Definition of Done
- [x] Linked Gitea issue (above).
- [x] Failing test committed before the implementation (red→green for endpoints + pure logic).
- [x] Conventional Commits referencing the issue (`refs #8`).
- [x] CI green — lint, build, unit + acceptance (80 tests), mutation, verify-stack (incl. verify-bff).
- [x] `docker compose up` reaches green health checks (bff health-checked; depends_on domain/projection/keycloak).
- [x] Docs — ADR-0010 + demo-script note.
- [x] ADR added — `docs/architecture/adr-0010-bff-oidc.md`.
- [x] Demo note in `docs/demo-script.md`.
## Notes for reviewers
- **Auth is testable without infra:** `WebApplicationFactory` tests reconfigure JWT bearer with a
local test signing key, so valid/invalid/expired/wrong-key tokens are exercised in-process. A
Reqnroll acceptance scenario drives the same over HTTP. Real Keycloak validation is the live-stack
**`verify-bff`** check (mints a real `digid` token via direct grant against `keycloak:8080`).
- **Issuer consistency:** Keycloak `start-dev` derives the issuer from the request host, so the BFF
authority and the verify token request both use `keycloak:8080` — no issuer mismatch (ADR-0010).
- **OpenAPI:** `services/bff/openapi.json` is generated (typed 202/400/401 + 200 schemas; the
volatile `servers` block is cleared for determinism) and guarded by a drift test — S-08's client is
generated from it.
- **Mutation:** 100% on the BFF's pure logic (`OpenbaarProjection`, break 90). `Program.cs` (wiring)
and `DownstreamClients.cs` (HTTP adapters) are excluded — covered by the endpoint tests + verify-bff,
consistent with how the other services scope Stryker.
- **Scope:** behandelaar/beheer endpoints and downstream service-to-service auth are out of scope
(later slices), per the issue and ADR-0010.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The BFF is the portals' only backend (§8.3): it validates Keycloak digid-realm
JWTs on POST /self-service/registrations (extracting bsn → domain), leaves
GET /openbaar/register anonymous (public lookup, S-09), and fans out to the
domain and projection over typed HTTP clients. Tests mint tokens with a test
signing key; real Keycloak validation is a live-stack verify-bff check. Records
the container OIDC issuer-mismatch wrinkle. OpenAPI is generated + committed for
the S-08 client.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Failing tests for the BFF walking-skeleton endpoints:
- POST /self-service/registrations rejects missing/malformed/wrong-key/expired
tokens (401) and, with a valid digid token, forwards the bsn to the domain and
returns 202 (WebApplicationFactory + a local test signing key, ADR-0010).
- GET /openbaar/register serves public-safe rows anonymously (never the bsn) and
filters by q.
- OpenbaarProjection.PublicView (pure) filters by id and maps to id+status only.
Endpoints and PublicView are stubs so the tests compile and fail on their
assertions; the green commit implements them.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
POST /self-service/registrations requires a valid digid JWT, reads the bsn claim
and forwards it to the domain, returning 202. GET /openbaar/register is anonymous
and returns OpenbaarProjection.PublicView — rows filtered by q and mapped to the
public-safe id+status only (bsn/naam never exposed). JwtBearer validates
signature/issuer/expiry against the Keycloak digid authority (§8.3, ADR-0010).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Use-case-level BDD (Reqnroll) driving the real BFF over HTTP with fake downstreams
and locally-minted tokens: a valid DigiD token is accepted and the bsn forwarded
to the domain; a tokenless submit is 401; the openbaar register is anonymous and
never exposes the bsn (ADR-0010). Real Keycloak validation is the verify-bff check.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document typed responses (202 SubmitAccepted / 400 / 401 on self-service; 200
OpenbaarEntry[] on openbaar) so the generated spec carries real schemas for S-08's
client. A document transformer clears the auto-populated servers block so the spec
is host-independent and deterministic. Commit services/bff/openapi.json and add a
test asserting it matches the served /openapi/v1.json (fails on drift).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire the bff service in compose (Keycloak authority + downstream domain/projection
URLs, depends_on domain/projection healthy + keycloak started). run-bff-check.sh
verifies the BFF end-to-end against the up stack: 401 without a token, 202 with a
real digid token minted via direct grant against keycloak:8080 (host-consistent
issuer, ADR-0010), and an anonymous public-safe openbaar register (never a bsn).
Wired as verify-bff (Makefile + verify chain + CI step). Stryker baseline for the
BFF's pure logic (OpenbaarProjection) at 100% (break 90); Program/HTTP adapters are
covered by the endpoint tests + verify-bff. CI uploads the bff mutation report.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What & why
S-07: the BFF (Backend-for-Frontend) — the single backend the portals talk to (§8.3). For the
walking skeleton it exposes:
POST /self-service/registrations— requires a valid KeycloakdigidJWT; extracts thebsnclaim and forwards it to the Domain Service (#6), returning 202. Missing/invalid/expired token → 401.
GET /openbaar/register?q=…— anonymous public lookup (S-09) → reads the projection (#7) andreturns only public-safe fields (
id+status; the bsn never leaves the BFF).Closes #8
Closes #63
Definition of Done
refs #8).docker compose upreaches green health checks (bff health-checked; depends_on domain/projection/keycloak).docs/architecture/adr-0010-bff-oidc.md.docs/demo-script.md.Notes for reviewers
WebApplicationFactorytests reconfigure JWT bearer with alocal test signing key, so valid/invalid/expired/wrong-key tokens are exercised in-process. A
Reqnroll acceptance scenario drives the same over HTTP. Real Keycloak validation is the live-stack
verify-bffcheck (mints a realdigidtoken via direct grant againstkeycloak:8080).start-devderives the issuer from the request host, so the BFFauthority and the verify token request both use
keycloak:8080— no issuer mismatch (ADR-0010).services/bff/openapi.jsonis generated (typed 202/400/401 + 200 schemas; thevolatile
serversblock is cleared for determinism) and guarded by a drift test — S-08's client isgenerated from it.
OpenbaarProjection, break 90).Program.cs(wiring)and
DownstreamClients.cs(HTTP adapters) are excluded — covered by the endpoint tests + verify-bff,consistent with how the other services scope Stryker.
(later slices), per the issue and ADR-0010.
🤖 Generated with Claude Code