Athena — valideerleeftijd/compliance/processes/data-flow.md

Process Documentation

Process: Age Verification Data Flow Project: valideerleeftijd Owner: GloryLabs Date: 2026-03-27


1. Purpose

This process describes how personal data moves through the valideerleeftijd age verification flow — from the initial client API request through the PSD2 bank OAuth2 PKCE redirect, birth date retrieval, in-memory age calculation, and result delivery — with explicit documentation of where data is created, used, and discarded.

2. Scope

Systems involved:

  • Client application (third-party, API consumer)
  • valideerleeftijd API (Node.js, migrating to Spring Boot 3 + Angular 20)
  • Redis (session store, 15-minute TTL)
  • PSD2 bank provider OAuth2 authorisation server (Tink / Klarna Open Banking)
  • PSD2 bank AISP API (birth date endpoint)
  • User's browser / app (redirect target)

Data involved:

  • Session ID (UUID)
  • Minimum age requirement (integer)
  • Client redirect URI
  • OAuth2 PKCE code challenge / verifier
  • Bank access token (ephemeral, used once)
  • Birth date (in-memory only, never stored)
  • Boolean verification result

People involved:

  • End user (consumer)
  • Client company (API key holder)
  • GloryLabs (operator)

3. Trigger

A client company sends an authenticated POST request to the valideerleeftijd API, supplying a minimum age threshold and a redirect URI, initiating an age verification for one of their users.

4. Process Steps

| Step | Actor | Action | System | Output | |------|-------|--------|--------|--------| | 1 | Client application | Sends POST /verify with API key, minimum_age, redirect_uri | valideerleeftijd API | Request authenticated; rate limit checked (100 req/min) | | 2 | valideerleeftijd API | Generates session UUID; stores {session_id, minimum_age, redirect_uri, pkce_code_verifier} in Redis with 15-min TTL | Redis | Session record created (no PII) | | 3 | valideerleeftijd API | Returns redirect URL to bank OAuth2 PKCE authorisation endpoint, including session_id as state parameter | Client application | Redirect URL containing session state | | 4 | Client application | Redirects user's browser to bank OAuth2 authorisation URL | User browser | User sees bank login screen | | 5 | End user | Authenticates with bank; grants consent for AISP access (birth date scope) | Bank OAuth2 server (Tink/Klarna) | Bank issues authorisation code | | 6 | Bank OAuth2 server | Redirects browser to valideerleeftijd callback URL with authorisation code and state (session_id) | valideerleeftijd API | Callback received | | 7 | valideerleeftijd API | Validates state (session_id) exists in Redis; exchanges authorisation code for access token via PKCE | Bank OAuth2 server | Bank access token (ephemeral) | | 8 | valideerleeftijd API | Calls bank AISP API with access token to retrieve birth date | Bank AISP API | Birth date returned in memory | | 9 | valideerleeftijd API | Calculates age from birth date; compares to minimum_age from session; produces boolean result | In-memory only | Boolean: age_requirement_met = true/false | | 10 | valideerleeftijd API | Discards birth date and bank access token from memory; deletes Redis session | Redis | No personal data remains | | 11 | valideerleeftijd API | Redirects user's browser to client redirect_uri with boolean result as query parameter | Client application | Client receives age_verified=true/false | | 12 | Client application | Acts on the boolean result (allow/deny access) | Client application | User granted or denied access |

5. Data Flow

Client App                valideerleeftijd API          Redis           Bank OAuth2 / AISP
    |                            |                        |                    |
    |-- POST /verify ----------->|                        |                    |
    |   {api_key, min_age,       |                        |                    |
    |    redirect_uri}           |                        |                    |
    |                            |-- STORE session ------>|                    |
    |                            |   {uuid, min_age,      |                    |
    |                            |    redirect_uri,        |                    |
    |                            |    pkce_verifier}       |                    |
    |                            |   TTL=15min             |                    |
    |<-- redirect URL -----------|                        |                    |
    |    (bank auth + state)     |                        |                    |
    |                            |                        |                    |
   [User browser redirected to bank]                      |                    |
    |                            |                        |                    |
    |                            |                     [User authenticates at bank]
    |                            |                        |                    |
    |                            |<-- callback -----------|                    |
    |                            |    {code, state=uuid}  |                    |
    |                            |                        |                    |
    |                            |-- GET session -------->|                    |
    |                            |<-- session data -------|                    |
    |                            |                        |                    |
    |                            |-- exchange code -------------------------------->|
    |                            |<-- access_token ---------------------------------|
    |                            |                        |                    |
    |                            |-- GET /birth-date -------------------------------->|
    |                            |<-- birth_date [IN MEMORY ONLY] ------------------|
    |                            |                        |                    |
    |                            | calculate age          |                    |
    |                            | discard birth_date     |                    |
    |                            | boolean result         |                    |
    |                            |                        |                    |
    |                            |-- DELETE session ----->|                    |
    |                            |                        |                    |
    |<-- redirect to client -----|                        |                    |
    |    {age_verified=true/false}|                        |                    |

Key data protection properties:

  • Birth date travels only over TLS and exists only in application memory for the duration of step 9
  • Redis stores no birth dates — only UUIDs, minimum age thresholds, and redirect URIs
  • The boolean result contains no personal data
  • Session auto-expires after 15 minutes if the flow is not completed

6. Error Handling

| Failure scenario | Response | |-----------------|----------| | Bank OAuth2 server unavailable | API returns 503 to client; session expires after 15 min; user must restart flow | | User denies bank consent | Bank redirects with error=access_denied; API redirects client with age_verified=error; session deleted | | PKCE validation failure / state mismatch | API returns 400; session deleted; potential replay attack logged | | Bank AISP API returns no birth date | API returns age_verified=error to client; no data stored; session deleted | | Redis session expired before callback | API returns 400 (session not found); user must restart flow | | Rate limit exceeded (>100 req/min) | API returns 429 Too Many Requests to client; no session created | | API key invalid or revoked | API returns 401 Unauthorized; no session created |

7. Related Documents

  • ../dpia.md — Data Protection Impact Assessment for valideerleeftijd
  • ../verwerkingsregister.md — Art. 30 GDPR processing register
  • ../nis2.md — NIS2 security assessment
  • ../bio.md — BIO controls assessment

Reacties

Nog geen reacties