eIDAS Issuer (OpenID4VCI)
EXPERIMENTAL
Like the verifier, the issuer is experimental and gated behind
settings.eidas.enabled (routes are 403 until you opt in). It currently
scaffolds OpenID4VCI offers and metadata; the credential-signing path is part
of the deferred cryptographic core.
Responses carry the X-AnvilBase-eIDAS-Experimental: true header and an
experimental: true envelope. Do not use for production issuance.
In addition to verifying credentials, an AnvilBase project can issue Verifiable Credentials into users’ EUDI Wallets using OpenID4VCI. This lets you attest facts your application is authoritative about — membership, employment, qualifications, entitlements — as portable credentials the user controls.
Issuance is optional and off by default; enable it in the project’s eIDAS config (which is also the experimental opt-in).
Enable the issuer
curl -X PATCH http://localhost:39001/api/v1/projects/<id>/eidas \ -H "Authorization: Bearer $ANVILBASE_TOKEN" -H "Content-Type: application/json" \ -d '{ "enabled": true, "issuer": { "enabled": true, "credential_types": ["org.example.membership"], "signing_key_id": "<key-id>" } }'| Field | Meaning |
|---|---|
issuer.enabled | turn issuance on |
issuer.credential_types | the credential types you will issue |
issuer.signing_key_id | the key used to sign issued credentials |
Issuer metadata
Wallets discover what you can issue from the standard well-known endpoint:
GET /v1/eidas/<project_id>/.well-known/openid-credential-issuerIt advertises the supported credential types, formats, and the issuer’s signing keys.
The issuance flow
1. App ──POST /v1/eidas/{id}/issue──► AnvilBase (create a credential offer)2. AnvilBase ──► credential offer (offer URI / QR)3. User's wallet ──► redeems the offer at the issuer endpoint4. AnvilBase ──► signed Verifiable Credential (SD-JWT-VC or mdoc)5. Wallet stores the credential; user can later present it anywhere1. Create a credential offer
curl -X POST "http://localhost:39001/v1/eidas/<project_id>/issue" \ -H "apikey: $SERVICE_KEY" -H "Content-Type: application/json" \ -d '{ "credential_type": "org.example.membership", "subject_id": "<user-uuid>", "claims": { "membership_level": "gold", "valid_until": "2027-01-01" } }'Issue from trusted server-side code (service_role) — you’re asserting facts about a subject, so the call must be authorized by your application’s logic.
The response is a credential offer (an offer URI you present to the user as a QR code or deep link). Their wallet redeems it and receives the signed credential.
2. Deliver the offer
const { offer_uri } = await createOffer()showQrCode(offer_uri) // desktop → wallet scans// orwindow.location.href = offer_uri // mobile → opens the walletFormats & signing
- Issued credentials are SD-JWT-VC (supporting selective disclosure) or mdoc, depending on the type.
- Credentials are signed with the project’s configured
signing_key_id. Protect that key as a high-value secret (Encryption → Key management). - Because issued credentials carry selective disclosure, the holder can later reveal only specific claims to a verifier.
Lifecycle considerations
- Expiry — set a sensible validity (
valid_until) so credentials don’t outlive the fact they attest. - Revocation — design for it (status lists) if you issue long-lived credentials whose validity can change.
- Audit — every issuance is recorded in the audit log.
Issuer + Verifier together
A project can be both: issue a credential to your users, and elsewhere accept credentials (yours or third parties’) as a verifier. The two halves share the per-project eIDAS config and the same Trust List integration.
Next: Self-Hosting → Overview.