#!/usr/bin/env bash # # Verify the BFF end-to-end (S-07) against an ALREADY-RUNNING full stack. The BFF is the portals' # only backend (§8.3): it validates Keycloak digid tokens on the self-service submit and serves the # openbaar register anonymously with only public-safe fields (ADR-0010). # # Checks, in-network (services reached by container IP; Keycloak by its service name so the token's # host-derived issuer matches the BFF's authority — see the compose bff env and ADR-0010): # 1. POST /self-service/registrations without a token -> 401 # 2. mint a real digid access token (direct grant) and POST it -> 202 (forwarded to the domain) # 3. GET /openbaar/register (anonymous) -> 200 JSON array, never a bsn # # Does NOT manage the stack lifecycle (the caller owns bring-up + teardown). Plain docker primitives. set -euo pipefail ip() { docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$1"; } bff="$(docker ps -q --filter 'name=[-_]bff[-_]' | head -1)" kc="$(docker ps -q --filter 'name=keycloak' | head -1)" [ -n "$bff" ] || { echo "ERROR: no running bff container — bring the stack up first" >&2; exit 1; } [ -n "$kc" ] || { echo "ERROR: no running keycloak container — bring the stack up first" >&2; exit 1; } net="$(docker inspect -f '{{range $k,$_ := .NetworkSettings.Networks}}{{$k}}{{"\n"}}{{end}}' "$bff" | head -1)" bff_ip="$(ip "$bff")" echo ">> bff=$bff_ip network=$net" # Helper: run curl inside a throwaway container on the stack network (reaches services by name/IP). net_curl() { docker run --rm --network "$net" curlimages/curl:latest "$@"; } echo ">> 1. self-service submit without a token must be 401" code="$(net_curl -s -o /dev/null -w '%{http_code}' -X POST "http://$bff_ip:8080/self-service/registrations" \ -H 'Content-Type: application/json' -d '{}')" echo " -> $code"; [ "$code" = "401" ] || { echo "FAIL: expected 401, got $code" >&2; exit 1; } echo ">> 2. minting a digid token (direct grant) via keycloak:8080 (host-consistent issuer)" token="" for _ in $(seq 1 20); do token="$(net_curl -s -X POST "http://keycloak:8080/realms/digid/protocol/openid-connect/token" \ -H 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=password' --data-urlencode 'client_id=big-portal' \ --data-urlencode 'username=jan-burger' --data-urlencode 'password=test123' \ | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p')" [ -n "$token" ] && break sleep 3 done [ -n "$token" ] || { echo "FAIL: could not obtain a digid access token" >&2; exit 1; } echo " -> got a token" echo ">> 2b. self-service submit with the token must be 202" code="$(net_curl -s -o /dev/null -w '%{http_code}' -X POST "http://$bff_ip:8080/self-service/registrations" \ -H "Authorization: Bearer $token" -H 'Content-Type: application/json' -d '{}')" echo " -> $code"; [ "$code" = "202" ] || { echo "FAIL: expected 202, got $code" >&2; docker logs "$bff" 2>&1 | tail -15 >&2; exit 1; } echo ">> 3. openbaar register (anonymous) must be 200 JSON, never a bsn" body="$(net_curl -s "http://$bff_ip:8080/openbaar/register")" echo "$body" | grep -q '^\[' || { echo "FAIL: openbaar did not return a JSON array: $body" >&2; exit 1; } if echo "$body" | grep -q '"bsn"'; then echo "FAIL: openbaar leaked a bsn field" >&2; exit 1; fi echo "OK — BFF: 401 without token, 202 with a digid token, anonymous public-safe openbaar register"