In May 2025, W3C Verifiable Credentials 2.0 officially reached “Recommendation” status - the highest level of standardization. This is a big deal, and here’s what it means for us as developers.
What Changed from VC 1.1
The jump from 1.1 to 2.0 brings significant improvements:
1. Better Cryptographic Agility
VC 2.0 separates the data model from the proof mechanism. You can now use:
- Data Integrity Proofs (JSON-LD based)
- JOSE/COSE (JWT/CWT based)
- BBS+ Signatures (selective disclosure)
This means you pick the proof format that fits your use case rather than being locked into one approach.
2. Improved JSON-LD Processing
The @context handling is now more explicit:
{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://www.w3.org/ns/credentials/examples/v2"
],
"type": ["VerifiableCredential", "ExampleDegreeCredential"],
"issuer": "did:example:123",
"validFrom": "2025-01-01T00:00:00Z",
"credentialSubject": {
"degree": {
"type": "BachelorDegree",
"name": "Computer Science"
}
}
}
Note: issuanceDate is now validFrom, and there’s a new validUntil property.
3. Status List 2021 → Bitstring Status List
Revocation/suspension checking got an upgrade:
"credentialStatus": {
"type": "BitstringStatusListEntry",
"statusPurpose": "revocation",
"statusListIndex": "94567",
"statusListCredential": "https://example.com/status/3"
}
The new bitstring approach is more efficient and privacy-preserving.
Proof Formats Comparison
| Format | Size | Selective Disclosure | Enterprise Familiarity | Best For |
|---|---|---|---|---|
| JWT (jose) | Large | No | High | OIDC integration |
| Data Integrity | Medium | With BBS+ | Low | Semantic interop |
| BBS+ | Medium | Yes (native) | Low | Privacy-first apps |
| SD-JWT | Large | Yes | Medium | OIDC + privacy |
Implementation Guide
Step 1: Choose Your Stack
For Web3-native apps:
npm install @veramo/core @veramo/did-provider-ethr @veramo/credential-ld
For enterprise integration:
npm install @sphereon/ssi-sdk
# or
gradle: implementation 'id.walt:waltid-ssikit:1.x'
Step 2: DID Setup
// Create an Ethereum-based DID
const identifier = await agent.didManagerCreate({
provider: 'did:ethr',
options: { network: 'polygon' } // L2 for lower costs
})
Step 3: Issue Credentials
const vc = await agent.createVerifiableCredential({
credential: {
'@context': ['https://www.w3.org/ns/credentials/v2'],
issuer: identifier.did,
validFrom: new Date().toISOString(),
credentialSubject: {
id: holderDid,
// your claims here
}
},
proofFormat: 'jwt' // or 'lds' for JSON-LD signatures
})
Step 4: Verify Credentials
const result = await agent.verifyCredential({ credential: vc })
if (result.verified) {
// Credential is valid and untampered
// Check issuer trust separately!
}
The Standards Landscape
W3C VC 2.0 (Data Model)
|
+-- Proof Formats
| |
| +-- JOSE/COSE (JWT, CWT)
| +-- Data Integrity (LD Proofs)
| +-- BBS+ (Selective Disclosure)
|
+-- Transport
|
+-- OID4VCI (Issuance)
+-- OID4VP (Presentation)
+-- DIDComm (P2P messaging)
My Recommendations
- Start with JWT format if you need enterprise buy-in (they understand OIDC)
- Add BBS+ later when you need selective disclosure
- Use did:web for enterprises, did:ethr for Web3
- Implement status lists from day one - retrofitting revocation is painful
The standard is stable. The tooling is ready. The only question now is what you’re going to build.
What’s your experience with the VC 2.0 migration? Any gotchas to share?