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>
63 lines
2.7 KiB
C#
63 lines
2.7 KiB
C#
using System.Security.Claims;
|
|
using Bff.Api;
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
var keycloakAuthority = builder.Configuration["Keycloak:Authority"]
|
|
?? throw new InvalidOperationException("Missing configuration 'Keycloak:Authority'");
|
|
var domainBaseUrl = builder.Configuration["Downstream:Domain:BaseUrl"]
|
|
?? throw new InvalidOperationException("Missing configuration 'Downstream:Domain:BaseUrl'");
|
|
var projectionBaseUrl = builder.Configuration["Downstream:Projection:BaseUrl"]
|
|
?? throw new InvalidOperationException("Missing configuration 'Downstream:Projection:BaseUrl'");
|
|
|
|
// Validate Keycloak-issued tokens (ADR-0010). Audience validation is off for the walking skeleton —
|
|
// Keycloak's audience mapping is a later hardening; signature/issuer/expiry are validated.
|
|
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
.AddJwtBearer(options =>
|
|
{
|
|
options.Authority = keycloakAuthority;
|
|
options.RequireHttpsMetadata = false;
|
|
options.TokenValidationParameters.ValidateAudience = false;
|
|
});
|
|
builder.Services.AddAuthorization();
|
|
|
|
// The BFF is the portals' only backend; it fans out to the domain and projection (§8.3).
|
|
builder.Services.AddHttpClient<IDomainClient, DomainClient>(c => c.BaseAddress = new Uri(domainBaseUrl));
|
|
builder.Services.AddHttpClient<IProjectionClient, ProjectionClient>(c => c.BaseAddress = new Uri(projectionBaseUrl));
|
|
|
|
builder.Services.AddHealthChecks();
|
|
builder.Services.AddOpenApi();
|
|
|
|
var app = builder.Build();
|
|
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
|
|
app.MapHealthChecks("/health");
|
|
app.MapOpenApi();
|
|
|
|
// 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();
|
|
|
|
// Exposed so the test host (WebApplicationFactory<Program>) can boot the app.
|
|
public partial class Program;
|