Verifier SDK
Complete OpenID4VP Draft-24 verification with presentation definitions, token exchange, and signature validation.
npm install @blockialabs/ssi-verifier-sdk
Building Blocks Overview
The VerifierSDK
is built from these components:
VerifierSDK
├── Storage Layer (sessions, tokens, definitions)
├── CredentialProcessor (verifies VC signatures & schemas)
│ ├── DIDResolver (resolves holder & issuer DIDs)
│ ├── SchemaValidator (validates credential schemas)
│ ├── FormatHandlers (handles JSON-LD, JWT formats)
│ └── SignatureProviders (verifies signatures)
└── HolderDIDResolver (specifically resolves holder DIDs)
Let's build each component step by step:
Step 1: Storage Infrastructure
What it does: Manages verification sessions, OAuth tokens, and presentation definitions.
import { InMemoryStorage } from '@blockialabs/ssi-storage';
const storage = {
transactions: new InMemoryStorage(), // Track verification sessions
tokens: new InMemoryStorage(), // OAuth access/refresh tokens
presentations: new InMemoryStorage() // Cached presentation definitions
};
Why you need it: OpenID4VP Draft-24 requires session state management between request and response.
Step 2: DID Resolution
What it does: Resolves DIDs to get public keys for signature verification.
import { KeyDIDResolver, KeyDIDMethod } from '@blockialabs/ssi-did-key';
const holderDidResolver = new KeyDIDResolver(new KeyDIDMethod());
Why you need it: To verify that credentials and proofs are signed by the claimed holder DID.
Step 3: Credential Processor
What it does: The core engine that verifies credential signatures and validates schemas.
import { CredentialProcessor, JsonLdHandler } from '@blockialabs/ssi-credentials';
const credentialProcessor = new CredentialProcessor({
didResolver: holderDidResolver, // From Step 2
schemaValidator, // Validates credential structure
formatHandlers: {
'ldp_vc': new JsonLdHandler()
},
signatureProviders // Verifies cryptographic signatures
});
Why you need it: This handles the complex W3C VC verification and ensures credentials are authentic.
Step 4: Create Verifier SDK
What it does: Combines all components into a working OpenID4VP verifier.
import { VerifierSDK } from '@blockialabs/ssi-verifier-sdk';
const verifier = VerifierSDK.create({
baseUrl: 'https://acme-corp.com/verify',
clientId: 'acme-corp-verifier',
credentialProcessor, // From Step 3
holderDidResolver, // From Step 2
storage // From Step 1
});
Why these are needed: Each component handles a specific part of the verification process.
Step 5: Define Requirements
What it does: Specifies exactly what credentials you want from the user.
const presentationDefinition = await verifier.createPresentationDefinition({
credentialTypes: ['UniversityDegree'],
constraints: [{
fields: [{
path: ['$.credentialSubject.degree'],
purpose: 'We need to verify your degree field'
}]
}]
});
Why this matters: Clear requirements help users understand what they need to share.
Step 6: Verification Flow
What happens: Create request → User scans QR → Verify presentation → Issue tokens.
// Create authorization request with QR code
const authRequest = await verifier.createAuthorizationRequest({
presentationDefinition,
nonce: crypto.randomUUID()
});
console.log('QR Code URL:', authRequest.openId4VPUrl);
// Verify when user responds
const result = await verifier.verifyPresentation({
vpToken: presentationFromWallet,
presentationSubmission,
state: authRequest.state
});
console.log('Verified:', result.verified);
Behind the scenes:
- Verifier creates secure challenge nonce
- User scans QR code with wallet app
- Wallet presents requested credentials
- Verifier validates signatures & schemas
- Returns success/failure with verified claims
Advanced Verification
Complex Presentation Requirements
// Define multiple credential requirements
const presentationDefinition = await verifier.createPresentationDefinition({
credentialTypes: ['IdentityCredential', 'AgeCredential'],
constraints: [
{
fields: [
{
path: ['$.credentialSubject.given_name'],
purpose: 'Legal name verification'
},
{
path: ['$.credentialSubject.birth_date'],
purpose: 'Age verification (18+)',
filter: { type: 'string', format: 'date' }
}
]
}
]
});
Authorization Request with Metadata
const authRequest = await verifier.createAuthorizationRequest({
presentationDefinition,
nonce: crypto.randomUUID(),
state: crypto.randomUUID(),
clientMetadata: {
client_name: 'Acme Corp Verifier',
logo_uri: 'https://acme-corp.com/logo.png'
}
});
console.log('Authorization URL:', authRequest.openId4VPUrl);
Token Exchange
// After successful verification, exchange for tokens
const tokenResponse = await verifier.exchangeCodeForToken({
responseCode: verificationResult.responseCode,
clientId: 'acme-corp-verifier'
});
console.log('Access token received:', tokenResponse.access_token);
Token Refresh
// Refresh expired tokens
const refreshedTokens = await verifier.refreshTokens({
refreshToken: tokenResponse.refresh_token,
clientId: 'acme-corp-verifier'
});