A powerful client package for implementing OIDC4VP (OpenID Connect for Verifiable Presentations) verifier functionality in Node.js applications. This library simplifies the process of verifying decentralized identities (DIDs) and verifiable credentials (VCs) through integration with the Empe SSI (Self-Sovereign Identity) ecosystem.
- Installation
- Quick Start
- Configuration Options
- Advanced Usage
- API Endpoints
- Callback Handling
- Rate Limiting
- SSE (Server-Sent Events)
- Error Handling
npm install @empe/verifier-client
# or
yarn add @empe/verifier-client
Set up a basic verifier service with Express in just a few steps:
import express from 'express';
import { VerifierClient } from '@empe/verifier-client';
// Create Express app
const app = express();
// Configure the verifier client
const verifierClient = new VerifierClient(app, {
baseUrl: 'https://your-app-domain.com', // Your application's base URL
verifierServiceUrl: 'https://verifier-service.empe.io', // Empe verifier service URL
clientSecret: 'your-client-secret', // Your client secret for authentication
verificationFlows: [
{
name: 'basic-identity', // A unique name for this verification flow
vpQuery: [
{
type: 'BasicIdentityCredential', // The type of credential to verify
required: true,
}
],
handleVerificationResult: async (data) => {
// Process the verification result
console.log('Verification result:', data);
// Extract data from the verified presentation
const vpData = JSON.parse(data.vp);
// Return data to be sent to the client via SSE
return {
status: 'success',
message: 'Identity verified successfully',
userData: vpData
};
}
}
]
});
// Initialize the verifier client
verifierClient.initialize();
// Start the server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Verifier server running on port ${PORT}`);
});
The VerifierClient
constructor accepts the following configuration options:
type VerifierConfiguration = {
// Your application's base URL - used to construct callback URLs
baseUrl: string;
// The URL of the Empe verifier service
verifierServiceUrl: string;
// Client secret for authentication with the verifier service
clientSecret: string;
// Array of verification flow configurations
verificationFlows?: VerifierEndpoint[];
};
type VerifierEndpoint = {
// A unique name for the verification flow
name: string;
// Callback function to process verification results
handleVerificationResult: (data: VerifierCallbackRequest) => Promise<any>;
// Array of credential types and requirements to verify
vpQuery?: VPQueryParams[];
// Custom path for QR code endpoint (default: 'qr-code')
pathQRCode?: string;
// Custom path for SSE connection endpoint (default: 'connect')
pathOpenConnection?: string;
// Custom path for verifier redirect URI (default: 'callback')
verifierRedirectUri?: string;
// Validity period of the verification request in seconds (default: 300)
validity?: number;
// Timeout for SSE connections in milliseconds (default: 15 minutes)
sseTimeout?: number;
// Rate limiting options for the endpoints
rateLimitOptions?: Partial<Options>;
};
You can define multiple verification flows with different credential requirements:
const verifierClient = new VerifierClient(app, {
baseUrl: 'https://your-app-domain.com',
verifierServiceUrl: 'https://verifier-service.empe.io',
clientSecret: 'your-client-secret',
verificationFlows: [
{
name: 'basic-identity',
vpQuery: [{ type: 'BasicIdentityCredential', required: true }],
handleVerificationResult: async (data) => {
// Handle basic identity verification
return { status: 'success', type: 'basic-identity' };
}
},
{
name: 'membership',
vpQuery: [{ type: 'MembershipCredential', required: true }],
handleVerificationResult: async (data) => {
// Handle membership verification
return { status: 'success', type: 'membership' };
}
}
]
});
You can integrate the verifier service with a frontend application using the following approach:
// Backend (Express)
app.get('/api/start-verification', async (req, res) => {
try {
// Fetch QR code data from your verifier endpoint
const response = await axios.get(
'http://localhost:3000/verifier/basic-identity/v1/qr-code'
);
res.json(response.data);
} catch (error) {
res.status(500).json({ error: 'Failed to start verification' });
}
});
// Frontend (JavaScript)
async function startVerification() {
// 1. Fetch verification data including QR code URL
const response = await fetch('/api/start-verification');
const data = await response.json();
// 2. Display QR code to the user
displayQRCode(data.qr_code_url);
// 3. Open SSE connection to receive verification result
const eventSource = new EventSource(
`/verifier/basic-identity/v1/connect/${data.state}`
);
// 4. Handle verification result
eventSource.onmessage = (event) => {
const result = JSON.parse(event.data);
// Process verification result
console.log('Verification completed:', result);
eventSource.close();
};
eventSource.onerror = () => {
console.error('SSE connection error');
eventSource.close();
};
}
For each verification flow (e.g., 'basic-identity'), the following endpoints are created:
-
QR Code Generation:
GET /verifier/{flow-name}/v1/qr-code
- Returns data for QR code generation including state, nonce, and request_uri
-
SSE Connection:
GET /verifier/{flow-name}/v1/connect/:state
- Opens a Server-Sent Events connection to receive verification results
-
Verifier Callback:
POST /verifier/{flow-name}/v1/callback
- Receives verification results from the Empe verifier service
The handleVerificationResult
function receives the following data:
type VerifierCallbackRequest = {
// Unique identifier for this verification session
state: string;
// Verification status: 'verified' or 'failed'
verification_status: string;
// Additional information about the verification
additional_info: string;
// Unique identifier for the verification query
query_id: string;
// Verifiable Presentation (JSON string)
vp: string;
// Nonce used in the verification to prevent replay attacks
nonce: string;
};
The package includes built-in rate limiting protection. You can customize rate limits for each verification flow:
{
name: 'basic-identity',
// ... other configuration ...
rateLimitOptions: {
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
}
}
The package uses Server-Sent Events to push verification results from the server to the client. SSE connections time out after 15 minutes by default, but you can customize this:
{
name: 'basic-identity',
// ... other configuration ...
sseTimeout: 30 * 60 * 1000, // 30 minutes
}
The package provides built-in error handling for API endpoints. Errors are properly formatted and returned with appropriate status codes. For custom error handling, you can catch errors in your handleVerificationResult
function:
handleVerificationResult: async (data) => {
try {
// Process verification
return { status: 'success' };
} catch (error) {
console.error('Verification error:', error);
return {
status: 'error',
message: error.message || 'Verification processing failed'
};
}
}
MIT