The EU age-verification app is shipping (feature-ready 15 April 2026, open-source reference at ageverification.dev). If you're building the verifier side — the online service that consumes the proof — two things trip everyone up: the namespace eu.europa.ec.av.1, and the requirement to validate the attestation against the trusted list. This is the part that actually makes the age check trustworthy, and it's the part the marketing pages skip. Let's go through it properly.
1. The credential on the wire
The app issues a Proof of Age Attestation in ISO/IEC 18013-5 mso_mdoc (CBOR) form. Its docType and its only namespace are both eu.europa.ec.av.1, and it carries a minimal, fixed attribute set:
| Attribute | Type |
|---|---|
age_over_18 |
boolean |
age_over_21 |
boolean |
age_in_years |
integer |
age_birth_year |
integer |
expiry_date |
string |
The spec is explicit: a Proof of Age Attestation SHALL NOT include any other attribute. No name, no document number, no date of birth. It's an electronic attestation of attributes (Art. 3(44) EUDI Regulation) that asserts a threshold and nothing else.
2. Why the signature alone proves nothing
Your verifier gets a signed MSO asserting age_over_18: true. Verifying that signature tells you the bytes weren't modified. It does not tell you the signer is allowed to issue age attestations — anybody can mint a key and sign a boolean.
The trust decision is therefore:
Does the certificate that signed this attestation appear on the EU's list of authorised Proof of Age Attestation Providers?
That list is the Age Verification Trusted List.
3. The AV Trusted List format
It's an ETSI-style trusted list hosted by the Commission in the eIDAS Dashboard. Member States notify the providers established in their territory; the list publishes each provider's signing certificate. The identifiers you match on:
<TSLType>http://ec.europa.eu/tools/lotl/av/TrstSvc/TrustedList/TSLType/AVTL</TSLType>
...
<TSPService>
<ServiceInformation>
<ServiceTypeIdentifier>http://ec.europa.eu/tools/lotl/av/TrstSvc/Svctype/PAA</ServiceTypeIdentifier>
<ServiceDigitalIdentity>
<DigitalId><X509Certificate>MIIB...==</X509Certificate></DigitalId>
</ServiceDigitalIdentity>
</ServiceInformation>
</TSPService>
AVTL = the Age Verification Trusted List type; PAA = a Proof of Age Attestation Provider service; the X509Certificate is your trust anchor.
The reference verifier backend ships an example list and wires it up with a namespace pattern:
# src/main/resources/application.properties
verifier.trustSources[0].pattern=eu.europa.ec.av.*
verifier.trustSources[0].tl.location=classpath:av-etsi-trusted-list.xml
The example av-etsi-trusted-list.xml lives in the reference repo (eu-digital-identity-wallet/av-srv-web-verifier-endpoint-23220-4-kt) and includes the verifier.ageverification.dev test issuer.
4. The validation algorithm
Conceptually, per presented attestation:
async function validateProofOfAge(mdoc: Mdoc, trustedList: AvTrustedList) {
// 1. Scope: right docType, read only the claim you requested
assert(mdoc.docType === "eu.europa.ec.av.1");
const over18 = mdoc.getClaim("eu.europa.ec.av.1", "age_over_18");
// 2. Authenticity: verify the MSO issuer signature
const dsCert = mdoc.issuerAuth.documentSignerCert;
assert(verifyMsoSignature(mdoc.issuerAuth, dsCert));
// 3. Trust: the signer must chain to a PAA cert on the AV Trusted List
const anchors = trustedList
.services(ServiceType.PAA) // http://ec.europa.eu/tools/lotl/av/TrstSvc/Svctype/PAA
.flatMap((s) => s.x509Certificates);
assert(chainsToTrustedAnchor(dsCert, anchors)); // <-- the step that creates trust
// 4. Validity window
assert(within(mdoc.validityInfo) && notExpired(mdoc.expiry_date));
// 5. Holder/device binding: wallet proves control of the bound key
assert(await verifyDeviceBinding(mdoc.deviceAuth));
return over18 === true; // fail-closed everywhere above
}
Skip step 3 and you have an age gate any attacker forges in five minutes. Hard-code one issuer cert and you break when the next Member State notifies a provider — which is exactly why the trust source is a list you refresh, not a constant.
5. Transports and proof types
Transport: support both. DC API (Digital Credentials API) is the browser-integrated primary path; OID4VP is the fallback. Select best-available at runtime.
Proof type: both are engineered against cross-service tracking.
- ZKP — proves the threshold with no linkable identifier disclosed.
-
Standard mdoc — single-use, issued in batches (≈30); issuers coarsen the
ValidityInfotimestamps (identicalhh:mm:ssacross the batch, per ISO 18013-5) so the attestation carries no correlatable fingerprint.
So even with plain mdoc, a compliant flow won't hand you a tracking handle. (Device binding uses a key in a WSCD; the ZKP variant proves knowledge of a valid signature over that key without revealing the signature or public key — that's the unlinkability upgrade the ARF is still standardising, so treat the ZKP path as maturing rather than universally available.)
6. Testing before go-live
- Point your verifier at the acceptance trusted list (acceptance.eidas.ec.europa.eu) — one shared list with multiple national test issuers.
- The
ageverification.devtest issuer is listed there under Iceland. - Or run fully local with the reference backend's bundled
av-etsi-trusted-list.xml.
7. Gotchas checklist
- ✅ Validate the signer against the trusted list — not just the signature.
- ✅ Treat the trusted list as refreshable (new MS providers land over time); handle revocation.
- ✅ Fail closed on any failure — never allow-on-error.
- ✅ Enforce single-use: don't accept the same batched mdoc twice.
- ✅ Read only the claim you need; the namespace is minimal by design — keep it that way downstream.
- ✅ Support DC API and OID4VP; browser/wallet coverage is still evolving.
Getting this validation right is the entire job of an age verifier — everything else is UX. If you'd rather consume a clean boolean than own ETSI trusted-list parsing, certificate-chain validation, and revocation, that's the layer we run at eIDAS Pro — eu.europa.ec.av.1 in, boolean out, no personal data stored.
Work from the current EU Age Verification specifications; this is technical information, not legal advice.
Top comments (0)