@empe/verifier-client
TypeScript icon, indicating that this package has built-in type declarations

2.4.0 • Public • Published

@empe/verifier-client

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.

Version License

Table of Contents

  1. Installation
  2. Quick Start
  3. Configuration Options
  4. Advanced Usage
  5. API Endpoints
  6. Callback Handling
  7. Rate Limiting
  8. SSE (Server-Sent Events)
  9. VP Query Authorization Requests
  10. Error Handling
  11. Security Considerations

Installation

npm install @empe/verifier-client
# or
yarn add @empe/verifier-client

Quick Start

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: [
                {
                    fields: [
                        {
                            path: ['$.type'],
                            filter: {
                                type: 'array',
                                contains: {
                                    const: 'BasicIdentityCredential',
                                },
                            },
                        },
                        {
                            path: ['$.credentialSubject.id'],
                            filter: {
                                type: 'string',
                                pattern: '^did:empe:.*$',
                            },
                        },
                    ],
                },
            ],
            handleVerificationResult: async data => {
                // Process the verification result
                console.log('Verification result:', data);

                // The verified presentation data is already parsed and available
                const vpData = data.vp;

                // Return data to be sent to the client via SSE
                return {
                    status: 'success',
                    message: 'Identity verified successfully',
                    userData: data.vp,
                };
            },
        },
    ],
});

// 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}`);
});

Configuration Options

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 verification query parameters defining credential requirements
    vpQuery?: VPQueryParams[];

    // Custom path for QR code endpoint
    // (default: '/api/verifier/{flow-name}/v1/authorize-qr-code')
    pathQRCode?: string;

    // Custom path for SSE connection endpoint
    // (default: '/api/verifier/{flow-name}/v1/connection')
    pathOpenConnection?: string;

    // Custom path for verifier redirect URI
    // (default: '/api/verifier/{flow-name}/v1/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;

    // Require an active SSE connection when processing verification results
    sseRequired?: boolean;

    // Rate limiting options for the endpoints
    rateLimitOptions?: Partial<Options>;
};

Advanced Usage

Multiple Verification Flows

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: [
                {
                    fields: [
                        {
                            path: ['$.type'],
                            filter: {
                                type: 'array',
                                contains: {
                                    const: 'BasicIdentityCredential',
                                },
                            },
                        },
                    ],
                },
            ],
            handleVerificationResult: async data => {
                // Handle basic identity verification
                return { status: 'success', type: 'basic-identity' };
            },
        },
        {
            name: 'membership',
            vpQuery: [
                {
                    fields: [
                        {
                            path: ['$.type'],
                            filter: {
                                type: 'array',
                                contains: {
                                    const: 'MembershipCredential',
                                },
                            },
                        },
                    ],
                },
            ],
            handleVerificationResult: async data => {
                // Handle membership verification
                return { status: 'success', type: 'membership' };
            },
        },
    ],
});

Dynamic VP Queries

The verifier-client supports dynamic VP queries, allowing you to create verification requests on-the-fly with custom credential requirements:

// Client code to create a dynamic VP query
const response = await axios.post(
    'http://localhost:3000/verifier/vp-query/v1/authorize-qr-code',
    {
        vpQuery: [
            {
                fields: [
                    {
                        path: ['$.type'],
                        filter: {
                            type: 'array',
                            contains: { const: 'CustomCredential' },
                        },
                    },
                    {
                        path: ['$.credentialSubject.age'],
                        filter: {
                            type: 'number',
                            minimum: 18,
                        },
                    },
                ],
            },
        ],
        validity: 300, // 5 minutes
    },
    {
        headers: {
            'x-client-secret': 'your-client-secret',
        },
    }
);

// The response contains the same data as a regular QR code endpoint
const { qr_code_url, state, request_uri } = response.data;

Frontend Integration

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.post(
            'http://localhost:3000/api/verifier/basic-identity/v1/authorize-qr-code',
            {},
            {
                headers: {
                    'x-client-secret': 'your-client-secret',
                },
            }
        );
        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(`/api/verifier/basic-identity/v1/connection/${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();
    };
}

Mobile Wallet Integration

You can integrate with mobile wallets using deeplinks:

// After getting the request_uri from the QR code endpoint
const deepLinkUrl = `empewallet://presentation?presentation_url=${encodeURIComponent(requestUri)}`;

// Create a button that opens the mobile wallet
document.getElementById('open-wallet-btn').addEventListener('click', () => {
    window.location.href = deepLinkUrl;
});

This allows users to open their Empe wallet directly from your application to complete the verification process.

API Endpoints

For each verification flow (e.g., 'basic-identity'), the following endpoints are created:

  • QR Code Generation: POST /api/verifier/{flow-name}/v1/authorize-qr-code

    • Returns data for QR code generation including state, nonce, request_uri, and qr_code_url
    • Requires client secret in header: x-client-secret
  • SSE Connection: GET /api/verifier/{flow-name}/v1/connection/:state

    • Opens a Server-Sent Events connection to receive verification results
    • The :state parameter must match the state from the QR code generation response
  • Verifier Callback: POST /api/verifier/{flow-name}/v1/callback

    • Receives verification results from the Empe verifier service
    • This endpoint is called by the verifier service, not your client application

Callback Handling

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
    vp: string;

    // Nonce used in the verification to prevent replay attacks
    nonce: string;
};

The result of handleVerificationResult is sent to the client via the SSE connection.

Rate Limiting

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
  }
}

SSE (Server-Sent Events)

The package uses Server-Sent Events to push verification results from the server to the client in real-time. 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
  sseRequired: true, // Require an active SSE connection when processing verification results
}

When sseRequired is set to true, the verification result will only be processed if there is an active SSE connection for the corresponding state parameter.

VP Query Authorization Requests

In addition to dynamic presentation definitions, the verifier client also supports pre-defined VP queries identified by a VP query ID:

// Frontend code
async function startVpQueryVerification() {
    // Request authorization using a pre-defined VP query
    const response = await fetch('/api/verifier/vp-query/v1/authorize-qr-code', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'x-client-secret': 'your-client-secret',
        },
        body: JSON.stringify({
            vp_query_id: 'my-predefined-query-id',
        }),
    });

    const data = await response.json();

    // Continue with QR code display and SSE connection as in standard flow
}

This approach allows for more complex verification requirements that are managed server-side.

Error Handling

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',
        };
    }
};

Security Considerations

  • Client Secret: Protect your client secret and never expose it in client-side code.
  • Timing-Safe Comparison: The client secret validation uses timing-safe comparison to prevent timing attacks.
  • Rate Limiting: Configure appropriate rate limits to prevent abuse.
  • Input Validation: All incoming data is validated using Joi schemas.
  • Error Messages: Error messages are designed to not leak sensitive information.

Integration with Other Empe Packages

The @empe/verifier-client package is designed to work seamlessly with other packages in the Empe ecosystem:

@empe/verifier-common

The @empe/verifier-common package provides the core functionality for credential verification, including presentation definitions and validation:

import { buildPresentationDefinition, VPQueryParams } from '@empe/verifier-common';

// Create a presentation definition for your verification flow
const presentationDefinition = buildPresentationDefinition([
    {
        fields: [
            {
                path: ['$.type'],
                filter: {
                    type: 'array',
                    contains: { const: 'IdentityCredential' },
                },
            },
            {
                path: ['$.credentialSubject.nationality'],
                filter: {
                    type: 'string',
                    const: 'Germany',
                },
            },
        ],
    },
]);

@empe/empe-did-resolver

The @empe/empe-did-resolver package is used internally to resolve and validate DIDs in the verification process:

import { DidResolver, VpValidator } from '@empe/empe-did-resolver';
import { VerifiablePresentation } from '@empe/identity';

// Create a resolver and validator
const resolver = new DidResolver(['empe']);
const validator = new VpValidator(resolver);

// Validate a presentation
const presentation = VerifiablePresentation.fromJSON(vpJson);
await validator.validateVp(presentation);

License

MIT

Readme

Keywords

none

Package Sidebar

Install

npm i @empe/verifier-client

Weekly Downloads

104

Version

2.4.0

License

MIT

Unpacked Size

3.61 MB

Total Files

124

Last publish

Collaborators

  • empe