eIDAS 2.0 Overview
EXPERIMENTAL — structural validation only, NOT cryptographic verification
The eIDAS module is experimental and off by default. The verifier
currently performs structural validation only: it checks the VP token’s
format, the nonce binding (against the original presentation request), the
credential type, the Level of Assurance, and expiry. It does NOT
cryptographically verify credentials — there is no SD-JWT issuer-signature
check, no holder key-binding check, no selective-disclosure digest check, and
the EU Trust List is not enforced (the issuer check is an advisory lookup
against a single reference issuer). Do not use the result for production
identity assurance. Every response self-declares this via
verification_level: "structural", experimental: true, a warning, and an
X-AnvilBase-eIDAS-Experimental: true header. See
Experimental status & roadmap below.
AnvilBase ships an optional eIDAS 2.0 module for EU Digital Identity. It lets your application run an OpenID4VP Relying Party flow against EUDI Wallets and, optionally, scaffold credential issuance (as an OpenID4VCI Issuer). The intent is to bring regulated, high-assurance identity to self-hosted apps — but the cryptographic core is not yet built (see below).
It’s built in Rust, configured per project. The EU Trust List integration is currently a best-effort, non-blocking lookup, not enforced trust validation.
What it does today (structural)
- Verifier (OpenID4VP Relying Party) — runs the OpenID4VP request/callback flow and structurally validates the returned VP token (format, nonce binding, type, LoA, expiry). It does not cryptographically verify the credential. See Verifier.
- Issuer (OpenID4VCI) — scaffolds credential offers/metadata. See Issuer.
- Level of Assurance (LoA) enforcement — require
Low,Substantial, orHighassurance (read from the token claims — not cryptographically attested).
What it does NOT do yet (deferred — see roadmap)
- SD-JWT-VC / mdoc signature verification — no issuer-signature check.
- Holder key binding — no key-binding-JWT verification.
- Selective-disclosure digests — disclosed claims are not proven against a signed credential.
- Enforced EU Trust List — issuer trust is an advisory, non-blocking lookup against one hardcoded reference issuer, not a fetched/enforced Trusted List.
- ISO mdoc (18013-5) — no COSE/CBOR mobile-document support.
EUDI Wallet → AnvilBase eIDAS module → STRUCTURAL result (experimental) │ │ ├── OpenID4VP flow ├── nonce binding (stored req) ├── VP token DECODE ├── LoA + expiry (from claims) │ (signature NOT verified) ├── advisory issuer lookup └── claims extraction └── audit loggingWhen to use it
Today, the module is suitable only for evaluation, prototyping, and protocol integration testing of the OpenID4VP request/callback flow. Because it does not cryptographically verify credentials, it must not be used for production KYC, account binding, age verification, or any flow that depends on the attributes being trustworthy. Revisit for production use once the cryptographic core lands.
Two surfaces
| Surface | Prefix | Purpose |
|---|---|---|
| Config (management) | /api/v1/projects/{id}/eidas | enable + configure per project |
| Flows (data plane) | /v1/eidas/{project_id}/* | run verify/issue, serve metadata |
Configure a project
Enable and configure eIDAS via the management API (or the console eIDAS tab):
curl -X PATCH http://localhost:39001/api/v1/projects/<id>/eidas \ -H "Authorization: Bearer $ANVILBASE_TOKEN" -H "Content-Type: application/json" \ -d '{ "enabled": true, "verifier": { "accepted_credentials": ["eu.europa.ec.eudi.pid"], "required_loa": "substantial", "trust_list_url": "https://ec.europa.eu/digital-trust-list" }, "issuer": { "enabled": false, "credential_types": [], "signing_key_id": null } }'Read it back:
curl http://localhost:39001/api/v1/projects/<id>/eidas -H "Authorization: Bearer $ANVILBASE_TOKEN"{ "enabled": true, "verifier": { "accepted_credentials": ["eu.europa.ec.eudi.pid"], "required_loa": "substantial", "trust_list_url": "https://ec.europa.eu/digital-trust-list", "callback_url": null }, "issuer": { "enabled": false, "credential_types": [], "signing_key_id": null }}| Field | Meaning |
|---|---|
enabled | experimental opt-in — turn the eIDAS data plane on for the project. Required: while false, all /v1/eidas/{project_id}/* routes return 403 eidas_experimental_disabled |
verifier.accepted_credentials | credential types you’ll accept (e.g. the EUDI PID) |
verifier.required_loa | minimum assurance: low, substantial, high (read from claims, not attested) |
verifier.trust_list_url | EU Trust List endpoint — currently advisory only, not fetched/enforced |
issuer.enabled | also scaffold credential issuance |
Experimental gate
The eIDAS data plane is gated behind enabled. Until a project explicitly
sets settings.eidas.enabled = true, every /v1/eidas/{project_id}/* route
(verify, verify/callback, issue, and both .well-known metadata endpoints)
returns HTTP 403 with a structured eidas_experimental_disabled message.
Once enabled, every response also carries:
- the response header
X-AnvilBase-eIDAS-Experimental: true, and - a body envelope
{"experimental": true, "warning": "EXPERIMENTAL: structural validation only — not cryptographic credential verification; do not use for production identity assurance", ...}.
This makes it impossible to use the verify/issue endpoints without the response self-declaring that it is experimental and structural-only.
Metadata endpoints
Wallets discover your capabilities through standard well-known endpoints, served per project:
GET /v1/eidas/<project_id>/.well-known/openid4vp # verifier metadataGET /v1/eidas/<project_id>/.well-known/openid-credential-issuer # issuer metadataExperimental status & roadmap
The module is experimental because its cryptographic core is intentionally not yet implemented. What runs today is structural validation (format, nonce binding, type, LoA, expiry). The deferred, tracked work to make eIDAS production-grade is:
| Deferred capability | What it adds |
|---|---|
| SD-JWT-VC issuer-signature verification | resolve the issuer key (x5c / JWKS / DID) and verify the SD-JWT signature |
| Holder key binding (KB-JWT) | prove the wallet holds the credential’s key, bound to the request nonce/audience |
| Selective-disclosure digests | recompute and match _sd digests so disclosed claims provably belong to the signed credential |
| Enforced EU Trust List | fetch + cache the official Trusted List and make issuer trust a hard, blocking requirement |
| ISO mdoc (18013-5) | COSE/CBOR mobile-document verification (e.g. mobile driving licence) |
Deadline driver: the EU mandate requiring the private sector to accept
EUDI Wallets takes effect in December 2027 — the cryptographic core must land
well before then. This roadmap is tracked in the crate module docs
(eidas/src/lib.rs) and in issue
#33.
Audit & compliance
Every verification and issuance is recorded in the audit log,
giving you an evidentiary trail for compliance reviews. Audit entries for
structural verifications carry "verification_level": "structural" and
"experimental": true so the trail itself reflects the (non-cryptographic)
nature of the check.
Read on:
- Verifier (OpenID4VP) — accept credentials from wallets.
- Issuer (OpenID4VCI) — issue credentials to wallets.