System Overview
FIDO4VC’s architecture is a deliberate split: authority lives on the user’s device (a FIDO authenticator holding the private key), and custody lives in the cloud (a wallet service holding credentials at rest). Neither can stand alone — credentials cannot be presented without a fresh FIDO assertion, and FIDO assertions are not credentials without the cloud wallet’s stored VCs.
High-level architecture
Section titled “High-level architecture”
Layers
Section titled “Layers”The system is organized into two physical layers and two functional concerns.
Client-Side Layer
Section titled “Client-Side Layer”Lives entirely on the user’s device. Two components:
- User Key Device (FIDO Signer) — the FIDO2/WebAuthn authenticator. A platform authenticator (Touch ID, Windows Hello), a Passkey synced via iCloud / Google / Microsoft, or an external token (YubiKey, smart card). The private key is generated inside this device’s secure element and never leaves it. Cryptographic operations are gated on user presence (biometric or PIN).
- Wallet UI — a browser-based application that initiates flows, displays credentials, and orchestrates the WebAuthn calls. No private key material is stored here. No service worker is doing crypto. The UI is a thin shell over WebAuthn and the server-side APIs.
Server-Side Layer
Section titled “Server-Side Layer”The wallet’s classic responsibilities, deliberately split into two services:
- FIDO Middleware — implements FIDO2/WebAuthn registration and authentication ceremonies, manages the user-to-DID binding (registered FIDO public keys converted to JWK and then to
did:jwk), and orchestrates the multi-step flows where the wallet prepares an unsigned VP and the FIDO authenticator signs it. Stateless between requests; sessions are short-lived to span the prepare-then-sign sequence. - Wallet Infrastructure — the cloud SSI wallet (in our reference deployment, walt.id). Holds the user’s DIDs and credentials. Talks to issuers (OpenID4VCI) and verifiers (OpenID4VP). Owns the external-signature exchange flow that allows the FIDO authenticator to provide signatures for VPs the wallet prepares.
Trust boundaries and the security claim
Section titled “Trust boundaries and the security claim”The architecture’s load-bearing security property is:
A cloud wallet operator with full read/write access to credential storage cannot present a credential. Presentation requires a fresh FIDO assertion from the user.
This holds because the verifier validates the cryptosuite proof on the VP, and that proof embeds a WebAuthn assertion bound to a challenge derived from the canonicalized VP contents. The cloud wallet possesses no key that satisfies this proof. Even with full database access, an attacker gets inert artifacts.
Compare against alternative architectures:
| Architecture | Storage | Signer | Operator can present without user? |
|---|---|---|---|
| Traditional cloud IdP | Server | Server (HSM or password) | ✅ Yes — server holds secrets |
| On-device SSI wallet | Device | Device (private key on phone) | ❌ No, but device theft / malware = full impersonation |
| FIDO4VC | Cloud | User’s FIDO authenticator | ❌ No — operator cannot impersonate; device theft alone insufficient because authenticator requires UV |
The trust required of the FIDO4VC cloud-wallet operator is bounded:
- Availability — the operator can deny service by refusing to prepare unsigned VPs, but cannot forge presentations
- Metadata — the operator sees flow metadata (which issuer, which verifier, when). If credential bodies are client-side encrypted under a key derivable only via FIDO assertion, the operator’s view is reduced to opaque blobs plus timing.
- Continuity — if the operator disappears, credentials must be portable. A standardized export format is a deployment prerequisite.
What’s “wallet-less” actually mean?
Section titled “What’s “wallet-less” actually mean?”The framing in marketing material is “no wallet app.” Be precise: there is a wallet — just not on the user’s device, and not custodial in the traditional sense.
What the user does not do:
- Install a wallet application
- Manage a seed phrase or backup file
- Transfer credentials when changing devices
- Hold any cryptographic material outside their normal OS account / authenticators
What still exists:
- A cloud wallet service (the user logs into via Passkey)
- A user account with that service
- Credentials stored server-side, addressable by the user’s DID
The cleanest summary: “Your Passkey is the signing power; the cloud holds only the receipts.”
Component-by-component
Section titled “Component-by-component”Wallet UI
Section titled “Wallet UI”A Next.js / React web application. All user interaction happens here:
- Account creation, login (via Passkey)
- DID management (add/remove FIDO keys, view bound DIDs)
- Credential receipt (paste an OpenID4VCI offer URL, complete the FIDO flow, see the credential in the wallet)
- Credential presentation (paste an OpenID4VP request URL, select credential, complete the FIDO flow)
The UI calls two backends: walt.id Wallet API (for credential storage and SSI flows) and the FIDO Middleware (for FIDO ceremonies that bridge to the wallet).
Reference repo: fido-vc-wallet-ui.
FIDO Middleware
Section titled “FIDO Middleware”A Node.js / Express service. Three responsibilities:
- Registration ceremony. Generates WebAuthn registration options, validates the attestation, converts the COSE public key to JWK, and creates a
did:jwkfrom it. The DID is registered with the wallet so credentials can be issued to it. - Signing ceremony for issuance. When a credential is being issued, the wallet prepares an unsigned
ldp_vpproof of possession. The middleware canonicalizes the VP, computes the SHA-256 challenge, invokes WebAuthn on the user’s authenticator, and folds the resulting assertion into the proof. - Signing ceremony for presentation. Same shape as issuance, but for VP token submission to a verifier.
Reference repo: fido-vc-middleware.
Wallet Infrastructure (walt.id)
Section titled “Wallet Infrastructure (walt.id)”The cloud SSI wallet. In our reference deployment, this is walt.id:
- Wallet API — manages DIDs and credential storage; exposes external-signature flows (
prepareExternalSignatureOffer/submitExternalSignatureOfferand the presentation counterparts) so the FIDO middleware can hand back signed VPs - Issuer API — issues VCs; we configure
ldp_vpproof type withfido4vc-jcs-2026cryptosuite - Verifier API — verifies VPs; we register the
signature_ld-vppolicy that delegates LD-VP verification to the sidecar
See Integration Guide for walt.id-specific wiring.
Verifier Sidecar
Section titled “Verifier Sidecar”A thin Node.js / Express service exposing the cryptosuite verification over HTTP, so non-Node verifier stacks (notably walt.id JVM) can verify fido4vc-jcs-2026-signed VPs without porting the cryptosuite.
Single endpoint: POST /verify accepts a VP JSON, returns {verified, error}.
Reference repo: fido-vc-verifier-sidecar.
Cryptosuite (reference implementations)
Section titled “Cryptosuite (reference implementations)”The fido4vc-jcs-2026 W3C VC Data Integrity cryptosuite. The TypeScript implementation is used by the middleware and the sidecar. A Kotlin port exists for direct in-process integration into JVM verifier stacks.
- TypeScript: fido-vc-cryptosuite-ts
- Kotlin (planned public release): integrated locally for now; see Integration Guide
Three flows make the system work end-to-end. Each gets its own page:
- Registration — creating a FIDO-bound DID
- Issuance — receiving a credential bound to that DID
- Presentation — proving possession of a credential to a verifier