Your starting point for integrating verifiable, user-centric identity into your applications.
Property | W3C VC | SD-JWT | ISO mDoc | OpenID4VP |
---|---|---|---|---|
Credential Format | JSON-LD, JWT | JWT (with disclosures) | CBOR, COSE | Protocol (format-agnostic) |
Signature/Proof Mechanism | JWS, Linked Data Proofs | JWS (ES256, EdDSA) | COSE_Sign1 | Depends on credential format |
Supported Libraries/SDKs | jsonld-signatures, did-jwt-vc | sd-jwt-python (Python), sd-jwt-js (TypeScript) | spruceid/isomdl, OWF identity-credential | OpenID4VP, spruceid/openid4vp |
Example Issuance Flow | Issuer creates VC, signs with private key, delivers to wallet | Issuer creates SD-JWT, signs, delivers with disclosures | Issuer encodes data in mDoc, signs with COSE, delivers to wallet | Issuer issues credential, wallet presents via OpenID4VP |
Example Verification Flow | Verifier checks signature, validates claims, resolves DID | Verifier checks signature, validates disclosures, holder binding | Verifier parses mDoc, checks COSE signature, validates data | Verifier requests credential via OpenID4VP, validates response |
DID Support | Yes (core to spec) | Optional | Optional (mDL may use DIDs) | Yes (for subject/issuer identification) |
Selective Disclosure Support | Limited (ZKP extensions) | Yes (Data Integrity BBS+) | Yes (via namespaces) | Depends on credential format |
Mobile/Browser Support | High (many wallets, browser libs) | Growing (wallets, libs emerging) | Mobile-first (iOS, Android) | High (browser, mobile, cross-platform) |
Interoperability Notes | Widely adopted, many wallet/libs | Privacy by design, growing support | Strong binding, mobile optimized | Protocol for cross-ecosystem flows |
Reference Spec/Docs | VC Data Model | SD-JWT IETF Draft | ISO 18013-5 | OpenID4VP Spec |
The digital credential ecosystem comprises several key components: Digital Wallets for users to store and manage their credentials, Issuer Services that create and sign credentials, and Verifier Tools used by applications to request and validate credentials. Choosing the right standards (like W3C Verifiable Credentials, SD-JWT, mdoc) depends on your specific use case, regulatory requirements, and desired level of interoperability.
Consider factors like the type of claims, security needs, and the target platforms when making your decision.
W3C Verifiable Credentials (VC) are a flexible, extensible standard for expressing credentials in JSON-LD or JWT format. Widely supported by wallets and libraries.
// Pseudocode for Issuing a W3C VC (JWT)
function issueCredential(subjectData, issuerPrivateKey) {
const claims = {
"@context": ["https://www.w3.org/2018/credentials/v1"],
type: ["VerifiableCredential", "YourCustomCredential"],
issuer: "did:example:issuer123",
issuanceDate: new Date().toISOString(),
credentialSubject: {
id: subjectData.id,
...subjectData.attributes
}
};
const signedVcJwt = signJwt(claims, issuerPrivateKey);
return signedVcJwt;
}
// Pseudocode for Verifying a VC-JWT
async function verifyVcJwt(vcJwt, issuerPublicKey) {
// 1. Decode JWT (don't trust payload yet)
// 2. Verify signature using issuer's public key
// 3. Validate claims (e.g. expiration, structure)
const isValid = await jwt.verify(vcJwt, issuerPublicKey, {
algorithms: ['ES256']
});
return isValid;
}
Apple
WWDC 25 • Session 232 • 32 min
Apple's Wallet & WebKit teams have introduced a standards-based way to prove identity online without the friction of photo uploads. By combining ISO 18013-5/7 mobile documents (mdocs), the W3C Digital Credentials API, and Apple Wallet's secure enclave, the flow gives users a one-tap, privacy-respecting alternative to legacy KYC checks. The demo and engineering walkthrough are captured in WWDC 25 Session 232
Component | Primary Role |
---|---|
Server backend | Builds & signs ISO 18013-7 request; stores HPKE private key; decrypts & validates the returned mdoc. |
Website frontend | Calls navigator.credentials.get() on a user gesture and forwards the encrypted response to the backend. |
Browser (Safari/WebKit) | Implements the Digital Credentials API, shows the picker & consent sheet, and relays encrypted payloads. |
Operating system (iOS/iPadOS/macOS) | Checks signatures in a secure sandbox, displays system UI, and routes the full request to Wallet or another credential app. |
Document source (Wallet or credential app) | Supplies only the requested fields, HPKE-encrypted & signed, back to the website. |
This is everything that happens before the user taps "Allow." It originates on your backend, surfaces in your frontend, and finishes when iOS passes the full, signed request to the credential source.
This diagram shows the journey of a digital credential request from your backend to the user's device.
┌────────┐ Build & sign ┌────────────┐ Digital Credentials ┌───────────┐ route ┌────────────────┐
│Server │──────────────▶│ Website │────────────────────▶│ Browser │──────────▶│ iOS │
│backend │ │ frontend │ │ (Safari) │ │ (System UI) │
└────────┘ └────────────┘ └───────────┘ ▼
Full request & consent ┌────────────────┐
release────────────────────────────────▶│ Document Source│
└────────────────┘
This table summarizes the main responsibilities of each component in the request flow.
Component | Responsibility |
---|---|
Backend (server) | Assemble the JSON payload: document type, requested elements, encryptionInfo (nonce + HPKE recipient key), and one or more readerAuthenticationAll signatures. |
Frontend (browser JS) | On a user gesture, call navigator.credentials.get() with the payload. |
Safari / iOS | Display picker ▶︎ consent sheet ▶︎ forward full request to Wallet or another provider. |
This is a Node-style pseudo-code example for building and signing the credential request on the server side.
// ① Generate HPKE keys + nonce
const { publicKey, privateKey } = hpke.generateKeyPair();
const nonce = crypto.randomBytes(16);
// ② Compose Device Request
const deviceRequest = {
docType: "org.iso.18013.5.1.mDL",
nameSpaces: {
"org.iso.18013.5.1": {
given_name: false,
family_name: false,
age_over_21: false,
portrait: false,
driving_privileges: false
}
},
encryptionInfo: {
recipientKey: publicKey,
nonce
}
};
// ③ Sign for Wallet & any other providers
const signedPayload = signWithABC(deviceRequest); // ABC = Apple Business Connect
// Optionally add more signatures for local apps and push into readerAuthenticationAll
return signedPayload;
This is a frontend JavaScript example for invoking the Digital Credentials API and handling the response.
// ④ Kick off the flow on click
async function verifyIdentity() {
try {
const requestData = await fetch("/build-mdoc-request").then(r => r.json());
const credResponse = await navigator.credentials.get({
mediation: "required",
digital: {
requests: [{ protocol: "org-iso-mdoc", data: requestData }]
}
});
await fetch("/validate-mdoc", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(credResponse)
});
} catch (e) {
console.error("MDOC flow failed, falling back", e);
}
}
Before the user even sees a consent sheet, iOS parses a partial request inside a strict sandbox. This stripped-down object holds only safe-to-display data: what is being requested, who is requesting, and the authentication certificates. Parsing it early blocks malformed or malicious payloads from ever touching sensitive OS components. Only after the user taps Allow does the full, raw ISO 18013 request reach Wallet or the credential app.
Android
Android • Digital Credentials • Coming Soon
Many open-source libraries and SDKs can help you get started. Look for tools that support:
Check platforms like W3C VC Test Suite & Implementations, and search for libraries specific to your programming language (e.g., for Node.js, Python, Java, Rust).
Join the conversation, ask questions, and contribute to the ecosystem: