From d767430ad77d28918a1bc2b12d712ebe9b7e7710 Mon Sep 17 00:00:00 2001 From: Niek Otten Date: Wed, 1 Jul 2026 11:00:56 +0200 Subject: [PATCH] feat(bff): implement self-service submit and openbaar lookup (refs #8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- services/bff/Bff.Api/OpenbaarProjection.cs | 7 +++++-- services/bff/Bff.Api/Program.cs | 22 +++++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/services/bff/Bff.Api/OpenbaarProjection.cs b/services/bff/Bff.Api/OpenbaarProjection.cs index 48d9576..7f1ae33 100644 --- a/services/bff/Bff.Api/OpenbaarProjection.cs +++ b/services/bff/Bff.Api/OpenbaarProjection.cs @@ -9,7 +9,10 @@ public static class OpenbaarProjection { public static IReadOnlyList PublicView(IReadOnlyList entries, string? q) { - // STUB (red): returns nothing until the green commit implements filter + public-safe mapping. - return []; + var filtered = string.IsNullOrWhiteSpace(q) + ? entries + : entries.Where(e => e.Id.Contains(q, StringComparison.OrdinalIgnoreCase)); + + return [.. filtered.Select(e => new OpenbaarEntry(e.Id, e.Status))]; } } diff --git a/services/bff/Bff.Api/Program.cs b/services/bff/Bff.Api/Program.cs index 36a44fc..1a91d42 100644 --- a/services/bff/Bff.Api/Program.cs +++ b/services/bff/Bff.Api/Program.cs @@ -1,3 +1,4 @@ +using System.Security.Claims; using Bff.Api; using Microsoft.AspNetCore.Authentication.JwtBearer; @@ -36,9 +37,24 @@ app.UseAuthorization(); app.MapHealthChecks("/health"); app.MapOpenApi(); -// STUB (red): no auth requirement, no downstream call, no filtering. -app.MapPost("/self-service/registrations", () => Results.Ok()); -app.MapGet("/openbaar/register", (string? q) => Results.Ok(Array.Empty())); +// Self-service submit: requires a valid digid token; the bsn comes from the token, not the body, +// and is forwarded to the domain (ADR-0010). Returns 202 — the zaak is opened asynchronously (S-05). +app.MapPost("/self-service/registrations", async (ClaimsPrincipal user, IDomainClient domain, CancellationToken ct) => +{ + var bsn = user.FindFirstValue("bsn"); + if (string.IsNullOrWhiteSpace(bsn)) + return Results.BadRequest("The token carries no bsn claim."); + + var accepted = await domain.SubmitRegistrationAsync(bsn, ct); + return Results.Accepted($"/self-service/registrations/{accepted.RegistrationId}", accepted); +}).RequireAuthorization(); + +// Openbaar register: an anonymous public lookup that exposes only public-safe fields (S-09). +app.MapGet("/openbaar/register", async (string? q, IProjectionClient projection, CancellationToken ct) => +{ + var entries = await projection.GetRegisterAsync(ct); + return Results.Ok(OpenbaarProjection.PublicView(entries, q)); +}); app.Run();