Third sub-slice of S-11 · Withdrawal (Flow 3) (#12) — the owner-scoped BFF withdraw endpoint (backend). S-11a/b made a withdrawal transition the aggregate and cancel the workflow; this adds the citizen-facing entry point through the BFF, gated to the registration's owner.
Domain: WithdrawRegistrationCommand carries the caller's bsn; the handler returns a WithdrawOutcome and refuses a bsn that doesn't own the registration. Unknown and not-owned are both 404 (indistinguishable — ownership isn't revealed). POST /registrations/{id}/withdraw takes {bsn} and maps the outcome (204/404).
BFF: POST /self-service/registrations/{id}/withdraw (DigiD-authenticated) forwards the token's bsn to the domain and relays 204/404. The BFF authenticates; the domain owner-scopes (an aggregate invariant, not the domain doing auth).
OpenAPI spec + Angular client regenerated for the new endpoint.
run-domain-check.sh withdrawal step now sends the owner bsn (verify-stack).
Refs #12 — the self-service "trek aanvraag in" button + e2e (S-11c-2) closes it.
Failing tests committed before the implementation.
Implementation makes the tests pass.
Conventional Commits referencing the issue (refs #12).
CI green — all Gitea Actions jobs.
docker compose up unaffected.
No ADR needed (owner-scoping is an aggregate invariant; no boundary change).
Docs — the user-visible demo note lands with S-11c-2.
Notes for reviewers
Full local gate run before pushing this time (lessons from #89): dotnet format --verify-no-changes clean; make unit green — Acl 27, EventSubscriber 19, BFF 30, Acceptance 9, Big 95; api-client lint+test green.
Owner mismatch returns 404 (not 403) so the portal can't be used to probe which references exist.
## What & why
Third sub-slice of **S-11 · Withdrawal (Flow 3)** (#12) — the **owner-scoped BFF withdraw endpoint** (backend). S-11a/b made a withdrawal transition the aggregate and cancel the workflow; this adds the citizen-facing entry point through the BFF, gated to the registration's owner.
- **Domain**: `WithdrawRegistrationCommand` carries the caller's `bsn`; the handler returns a `WithdrawOutcome` and refuses a bsn that doesn't own the registration. Unknown and not-owned are **both 404** (indistinguishable — ownership isn't revealed). `POST /registrations/{id}/withdraw` takes `{bsn}` and maps the outcome (204/404).
- **BFF**: `POST /self-service/registrations/{id}/withdraw` (DigiD-authenticated) forwards the token's `bsn` to the domain and relays 204/404. The BFF authenticates; the domain owner-scopes (an aggregate invariant, not the domain doing auth).
- OpenAPI spec + Angular client regenerated for the new endpoint.
- `run-domain-check.sh` withdrawal step now sends the owner `bsn` (verify-stack).
Refs #12 — the self-service "trek aanvraag in" button + e2e (S-11c-2) closes it.
## Definition of Done
- [x] Linked Gitea issue (#12).
- [x] Failing tests committed before the implementation.
- [x] Implementation makes the tests pass.
- [x] Conventional Commits referencing the issue (`refs #12`).
- [x] CI green — all Gitea Actions jobs.
- [x] `docker compose up` unaffected.
- [x] No ADR needed (owner-scoping is an aggregate invariant; no boundary change).
- [x] Docs — the user-visible demo note lands with S-11c-2.
## Notes for reviewers
- **Full local gate run before pushing this time** (lessons from #89): `dotnet format --verify-no-changes` clean; `make unit` green — Acl 27, EventSubscriber 19, BFF 30, Acceptance 9, Big 95; `api-client` lint+test green.
- Owner mismatch returns 404 (not 403) so the portal can't be used to probe which references exist.
The withdraw handler returns an outcome and refuses a bsn that doesn't own the registration; the
BFF endpoint requires a digid token, forwards id+bsn, and relays the domain's 404.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The domain withdraw command carries the caller's bsn and owner-scopes the aggregate; unknown or
not-owned is 404 (indistinguishable). The BFF forwards the DigiD token's bsn and relays 204/404.
Regenerate the OpenAPI spec + Angular client for the new endpoint.
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
Third sub-slice of S-11 · Withdrawal (Flow 3) (#12) — the owner-scoped BFF withdraw endpoint (backend). S-11a/b made a withdrawal transition the aggregate and cancel the workflow; this adds the citizen-facing entry point through the BFF, gated to the registration's owner.
WithdrawRegistrationCommandcarries the caller'sbsn; the handler returns aWithdrawOutcomeand refuses a bsn that doesn't own the registration. Unknown and not-owned are both 404 (indistinguishable — ownership isn't revealed).POST /registrations/{id}/withdrawtakes{bsn}and maps the outcome (204/404).POST /self-service/registrations/{id}/withdraw(DigiD-authenticated) forwards the token'sbsnto the domain and relays 204/404. The BFF authenticates; the domain owner-scopes (an aggregate invariant, not the domain doing auth).run-domain-check.shwithdrawal step now sends the ownerbsn(verify-stack).Refs #12 — the self-service "trek aanvraag in" button + e2e (S-11c-2) closes it.
Definition of Done
refs #12).docker compose upunaffected.Notes for reviewers
dotnet format --verify-no-changesclean;make unitgreen — Acl 27, EventSubscriber 19, BFF 30, Acceptance 9, Big 95;api-clientlint+test green.