Corrected 20 October 2026: an earlier version of this post said the EUDI Wallet derives an
age_over_18boolean 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_18exists only in the separate proof-of-age attestation. The code samples now use the real@openeudi/coreAPI.
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_18from a proof-of-age attestation — reduced at once to a boolean:trueorfalse
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:
| Credential | Attribute | Returns | Notes |
|---|---|---|---|
PID (eu.europa.ec.eudi.pid.1 / urn:eudi:pid:1) | birth_date (mdoc) / birthdate (SD-JWT VC) | Date | Mandatory in every PID; the verifier derives any threshold (16+, 18+, 21+) |
Proof-of-age attestation (eu.europa.ec.av.1) | age_over_18 | Boolean | Other 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:
| Country | Alcohol | Tobacco | Gambling |
|---|---|---|---|
| Most EU | 18+ | 18+ | 18+ |
| Germany | 16+ (beer/wine), 18+ (spirits) | 18+ | 18+ |
| Belgium | 16+ (beer/wine), 18+ (spirits) | 18+ | 21+ |
| Austria | 16+ (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
Implementing Age Verification in WooCommerce with eIDAS
Complete guide to adding eIDAS-based age verification to your WooCommerce store. Learn how to implement compliant, frictionless verification for alcohol, tobacco, and age-restricted products.
11 min read
GDPR Compliance Made Easy: How eIDAS Minimizes Data Collection
Learn how eIDAS-based identity verification achieves perfect GDPR compliance through data minimization, privacy-by-design architecture, and user control. Reduce your data liability while improving security.
11 min read
Share this article
Help others learn about eIDAS verification