Technical

Building a Privacy-First Age Verification System with OpenEUDI

How to build an age verification system that stores zero personal data using the OpenEUDI SDK. A boolean result — not a birth date.

eIDAS Pro Team
March 10, 2026
7 min read

Corrected 20 October 2026: an earlier version of this post said the EUDI Wallet derives an age_over_18 boolean from the PID so the birth date never leaves the device. The PID has no age attribute: the verifier requests the birth date alone and derives the yes/no itself; age_over_18 exists only in the separate proof-of-age attestation. The code samples now use the real @openeudi/core API.

The Problem with Traditional Age Verification

Most age verification systems today work by collecting personal data:

  • Date of birth entry: Users type their birth date. No verification. Easy to lie.
  • Document upload: Users photograph their ID. You now have a document image with name, address, photo, document number — far more data than needed to answer "is this person over 18?"
  • Third-party age estimation: AI estimates age from a selfie. Biometric data processing with significant GDPR implications.

All of these approaches collect more data than necessary to answer a simple boolean question.

The Privacy-First Alternative

With EUDI Wallet verification, age checking works differently:

You ask:      "Is this person 18 or older?"
The wallet:   discloses one signed attribute, nothing else
You keep:     "Yes." (or "No.")

No name. No document. No selfie. No biometric data. From the PID — the identity credential every EUDI Wallet carries — that one attribute is the birth date: the verifier sees it once, derives the yes/no and discards it. Only the separate proof-of-age attestation answers age_over_18 without the birth date ever leaving the device. Either way, what you keep is a boolean backed by a government-trusted issuer's signature.

Implementation with OpenEUDI

Step 1: Install and Configure

import { VerificationService, DemoMode, VerificationType } from '@openeudi/core';

const service = new VerificationService({
  mode: new DemoMode(), // Simulates the wallet; a production mode implements IVerificationMode
});

Step 2: Create an Age Verification Session

const session = await service.createSession({
  type: VerificationType.AGE,
  // That's all. No name, no address, no document: an age check only.
});
// session.walletUrl: render it as a QR code for the wallet

The key insight: what the wallet discloses depends on the credential. The PID has no age attribute, so a verification mode requests the birth date alone (selective disclosure), computes the 18+ threshold and hands your code only the boolean. A proof-of-age attestation (eu.europa.ec.av.1) carries age_over_18 directly, so the birth date never leaves the device — but as of October 2026 only Denmark issues one in production.

Step 3: Handle the Result

service.on('session:verified', (session, result) => {
  if (result.ageVerified) {
    // Allow access to age-restricted content/product
    allowPurchase();
  }
});

service.on('session:rejected', (session, reason) => {
  // Block access
  showAgeRestrictionMessage();
});

Step 4: What You Store (Almost Nothing)

// Audit log, written inside the 'session:verified' handler — no PII
await db.verificationLogs.create({
  sessionId: session.id,
  verificationType: session.type, // VerificationType.AGE
  result: result.ageVerified, // true/false — never the birth date
  verifiedAt: session.completedAt,
  // No name. No DOB. No document number. No PII.
});

WooCommerce Integration

For WordPress/WooCommerce stores selling age-restricted products:

// The OpenEUDI WooCommerce plugin handles this automatically.
// In your WordPress admin:
// 1. Install the OpenEUDI Age Verification plugin
// 2. Go to WooCommerce → Settings → Age Verification
// 3. Select which product categories require verification
// 4. Choose verification threshold (18+, 21+)
// 5. Done.

The plugin adds a verification step before checkout for tagged products. The customer scans a QR code, approves in their wallet, and the order proceeds. No personal data is collected or stored by the plugin.

GDPR Analysis

Data Minimization (Article 5(1)(c))

Traditional age verification collects:

  • Full name, date of birth, document number, document images, selfie

OpenEUDI age verification receives:

  • One attribute — the PID birth date, or age_over_18 from a proof-of-age attestation — reduced at once to a boolean: true or false

The GDPR requires that personal data be "adequate, relevant and limited to what is necessary." A single attribute reduced to a boolean is close to the minimum needed to verify age; a proof-of-age attestation, which never discloses the birth date at all, is the minimum.

Storage Limitation (Article 5(1)(e))

With traditional verification, you must:

  • Define retention periods for identity documents
  • Implement secure deletion procedures
  • Respond to data subject access and erasure requests

With OpenEUDI:

  • The verification result (true/false) is not personal data under most interpretations
  • The session ID is a random identifier with no link to the individual
  • No retention policy for PII is needed because no PII is stored: the birth date is discarded once the boolean is derived

Data Protection Impact Assessment

A DPIA may not be required for OpenEUDI age verification because:

  • No systematic monitoring of individuals
  • No processing of special categories of data
  • No large-scale processing of personal data
  • The only attribute processed (the birth date) is held transiently, and what is kept (a boolean) poses minimal risk to individuals

Compare this with traditional KYC-based age verification, which almost certainly requires a DPIA due to document image processing and biometric data handling.

Available Age Attributes

Which age attribute a verifier can request depends on the credential in the wallet:

CredentialAttributeReturnsNotes
PID (eu.europa.ec.eudi.pid.1 / urn:eudi:pid:1)birth_date (mdoc) / birthdate (SD-JWT VC)DateMandatory in every PID; the verifier derives any threshold (16+, 18+, 21+)
Proof-of-age attestation (eu.europa.ec.av.1)age_over_18BooleanOther age_over_NN optional; one production issuer (Denmark) as of October 2026

The PID itself carries no age_over_NN attribute; age attributes were removed from the PID rulebook in 2025. For an age check eIDAS Pro requests the PID birth date and nothing else, derives an 18+ boolean, and never returns, stores or logs the date of birth; if a proof-of-age attestation is presented, it uses age_over_18 instead. You only receive the boolean result — never the underlying personal data. Country compliance is handled by your merchant whitelist/blacklist configuration.

Country-Specific Considerations

Age verification thresholds vary by country and product:

CountryAlcoholTobaccoGambling
Most EU18+18+18+
Germany16+ (beer/wine), 18+ (spirits)18+18+
Belgium16+ (beer/wine), 18+ (spirits)18+21+
Austria16+ (beer/wine), 18+ (spirits)18+18+

The OpenEUDI SDK does not ship a per-country compliance mapping: VerificationType.AGE is a single age check, documented as 18+. A different threshold — 16+ for beer in Germany, 21+ for gambling in Belgium — has to be applied by your verification mode against the birth date, because a proof-of-age attestation only guarantees age_over_18.


The OpenEUDI SDK is Apache-2.0-licensed and free. For managed production age verification (bank eID today, managed WRPAC once it can be obtained), see eIDAS Pro's managed plans.

Related Articles

Share this article

Help others learn about eIDAS verification