Athena — valideerleeftijd/business-plan.md

ValideerLeeftijd — Age Verification API

By Mahmoud Consultancy

Overview

ValideerLeeftijd is a PSD2-powered age verification API that allows companies to reliably verify a user's age using their verified bank identity. Because banks perform KYC (Know Your Customer) checks including birth date, PSD2 gives us a trusted, privacy-friendly way to confirm age — no document uploads, no manual review.


Architecture

┌─────────────────────────────────────────────────────────────┐
│                    ValideerLeeftijd Platform                 │
├─────────────────┬───────────────────┬───────────────────────┤
│   Website       │  Developer Portal │  REST API             │
│  (marketing)    │  (docs + keys)    │  (age verification)   │
│  website/       │  frontend/        │  backend/             │
│  (vanilla HTML) │  (Angular 20)     │  (Spring Boot 3)      │
└─────────────────┴───────────────────┴───────────────────────┘

Stack

| Component | Technology | Why | |-----------|-----------|-----| | API | Spring Boot 3, Java 17, Maven | Production-grade, matches mahmoud-consultancy | | Auth | API Keys (X-API-Key header) | Simple, stateless, easy to rotate | | PSD2 | OAuth2 PKCE + AISP | Standard PSD2 account info access | | Sessions | Redis (TTL 15 min) | Fast, no persistent storage of PII | | Developer Portal | Angular 20, standalone components | Matches mahmoud-consultancy frontend stack | | Rate Limiting | Bucket4j | In-memory, 100 req/min per IP |


User Flow (PSD2 Age Verification)

Company (Client)                    ValideerLeeftijd API              Bank (PSD2)
      │                                     │                              │
      │  POST /api/v1/verify/initiate       │                              │
      │  { minimumAge: 18,                  │                              │
      │    redirectUri: "..." }             │                              │
      │────────────────────────────────────►│                              │
      │                                     │                              │
      │  ◄── { sessionId, bankRedirect }    │                              │
      │                                     │                              │
      │  Redirect end-user to bankRedirect  │                              │
      │────────────────────────────────────►│                              │
      │                                     │  OAuth2 PKCE Auth Request    │
      │                                     │─────────────────────────────►│
      │                                     │                              │
      │              End-user logs in with bank (SCA — Strong Customer Auth)
      │                                     │                              │
      │                                     │◄── Authorization Code        │
      │                                     │                              │
      │                                     │  Exchange code for token     │
      │                                     │─────────────────────────────►│
      │                                     │◄── Access Token              │
      │                                     │                              │
      │                                     │  GET /aisp/accounts (birthdate)
      │                                     │─────────────────────────────►│
      │                                     │◄── Account holder birthdate  │
      │                                     │                              │
      │                                     │  Calculate age, store boolean│
      │                                     │  Birth date NEVER stored     │
      │                                     │                              │
      │  Redirect to client redirectUri     │                              │
      │◄────────────────────────────────────│                              │
      │  ?sessionId=xxx&status=completed    │                              │
      │                                     │                              │
      │  GET /api/v1/verify/{sessionId}     │                              │
      │────────────────────────────────────►│                              │
      │                                     │                              │
      │  ◄── { ageVerified: true/false }    │                              │
      │                                     │                              │

API Endpoints

| Method | Path | Auth | Description | |--------|------|------|-------------| | POST | /api/v1/verify/initiate | X-API-Key | Start age verification session | | GET | /api/v1/verify/{sessionId} | X-API-Key | Get verification result | | GET | /api/v1/bank/authorize | public | Redirect to bank OAuth2 | | GET/POST | /api/v1/bank/simulate | public | Demo bank login form | | GET | /api/v1/bank/callback | public | Bank OAuth2 callback | | GET | /actuator/health | public | Health check |


Project Structure

valideerleeftijd/
├── PLAN.md                              ← This file
├── CLAUDE.md                            ← Project rules for Claude Code
├── Makefile                             ← Dev/test/build/deploy targets
├── docker-compose.yml                   ← Local dev environment
├── backend/                             ← Spring Boot 3 REST API
│   ├── pom.xml
│   ├── Dockerfile
│   └── src/main/java/nl/mahmoudconsultancy/valideerleeftijd/
│       ├── ValideerleeftijdApplication.java
│       ├── config/
│       │   ├── AppProperties.java       ← @ConfigurationProperties
│       │   ├── CorsConfig.java
│       │   ├── RedisConfig.java
│       │   └── SecurityConfig.java
│       ├── controller/
│       │   ├── VerificationController.java
│       │   └── BankController.java
│       ├── dto/
│       │   ├── InitiateRequest.java
│       │   ├── InitiateResponse.java
│       │   └── VerificationStatusResponse.java
│       ├── filter/
│       │   └── ApiKeyFilter.java        ← API key + rate limiting
│       ├── model/
│       │   └── VerificationSession.java ← Redis-serializable session
│       └── service/
│           ├── AgeCalculationService.java
│           └── SessionService.java
├── frontend/
│   └── valideerleeftijd-portal/         ← Angular 20 developer portal
│       ├── angular.json                 ← Port 4305
│       └── src/app/
│           ├── components/
│           │   ├── layout/              ← Shell with nav + footer
│           │   ├── nav/                 ← Navigation bar
│           │   ├── dashboard/           ← Session stats overview
│           │   ├── api-keys/            ← API key management
│           │   ├── documentation/       ← Integration guide
│           │   └── flow-demo/           ← Interactive PSD2 flow demo
│           ├── models/session.model.ts
│           └── services/verification.service.ts
├── k8s/                                 ← Helm chart
│   ├── Chart.yaml
│   ├── values.yaml
│   ├── templates/
│   └── sealed-secrets/                  ← Bitnami Sealed Secrets
└── .github/workflows/
    ├── ci-backend.yml
    ├── ci-frontend.yml
    ├── deploy-backend.yml
    └── deploy-frontend.yml

Security Considerations

  • All API calls require a valid API key (except bank/* and actuator/*)
  • PSD2 sessions expire after 15 minutes
  • Birth dates are never stored — only the boolean result
  • HTTPS enforced in production
  • Rate limiting: 100 requests/min per IP (Bucket4j)
  • CORS configured
  • API keys must be kept server-side

Roadmap

  • [ ] v1.0 — Basic PSD2 age verification with simulated bank
  • [ ] v1.1 — Real bank integration (ING, ABN AMRO)
  • [ ] v1.2 — Webhook notifications when verification completes
  • [ ] v1.3 — Multi-bank support
  • [ ] v2.0 — Embedded widget (drop-in JavaScript)

Reacties

Nog geen reacties