Skip to main content

WalletHandlers

The WalletHandlers class is a high-level utility designed to seamlessly integrate the DID Connect workflow into an Express.js or similar server-side framework. It automates the setup of all required API endpoints for a given action, handling the communication between your application and the user's DID Wallet.

It works in tandem with the WalletAuthenticator, which manages the cryptographic signing and verification of messages. Since WalletHandlers is built on Node.js's EventEmitter, you can listen for various lifecycle events as the session state changes in your token storage.

Constructor

Creates an instance of the DID Connect handlers. This instance orchestrates the handling of HTTP requests for the DID Connect process.

DID Connect Handler Setup

javascript
const { WalletHandlers } = require('@did-connect/handler');
const { WalletAuthenticator } = require('@did-connect/authenticator');
const { FileStorage } = require('@did-connect/storage-file');

// 1. Initialize the authenticator with your app's wallet and info
const authenticator = new WalletAuthenticator({ wallet, appInfo, chainInfo });

// 2. Initialize a storage adapter to manage session state
const tokenStorage = new FileStorage({ dbPath: './sessions.json' });

// 3. Create the handlers instance
const handlers = new WalletHandlers({
  authenticator,
  tokenStorage,
});

Parameters

  • config object (required) — Configuration object for the WalletHandlers instance.
    • authenticator WalletAuthenticator (required) — An instance of WalletAuthenticator responsible for cryptographic operations.
    • tokenStorage object (required) — A token storage instance that handles session state (e.g., FileStorage, MongoStorage).
    • pathTransformer function — An optional function to transform the URL pathname for the wallet callback, useful in proxy or complex routing scenarios.
    • onConnect function — An optional global callback executed when a wallet scans the QR code, before the auth request is sent. It can be used for global permission checks. Throws an error to halt the process.
    • options object — An optional object to customize the behavior of the handlers.
      • prefix string (default: /api/did) — The URL prefix for all generated endpoints.
      • cleanupDelay number (default: 60000) — The time in milliseconds to wait before cleaning up a completed session from storage.
      • tokenKey string (default: _t_) — The query parameter key for the session token.
      • encKey string (default: _ek_) — The query parameter key for the encryption key.
      • versionKey string (default: _v_) — The query parameter key for the protocol version.

Methods

attach(config)

This is the primary method of the WalletHandlers class. It attaches the necessary DID Connect routes to your Express app for a specific user action (e.g., 'login', 'payment'). This method creates a full set of endpoints to handle the entire connection lifecycle, from token generation to wallet response handling.

Once attached, the following routes are created for the given action:

  • GET {prefix}/{action}/token: Creates a new session token.
  • POST {prefix}/{action}/token: Creates a new session token.
  • GET {prefix}/{action}/status: Checks the status of a session token (e.g., pending, scanned, approved).
  • GET {prefix}/{action}/timeout: Manually expires a session token.
  • GET {prefix}/{action}/auth: The endpoint for the wallet to fetch authentication requirements.
  • POST {prefix}/{action}/auth: The endpoint for the wallet to submit the signed response.

Parameters

  • config object (required) — Configuration object for attaching the routes.
    • app object (required) — The Express app instance.
    • action string (required) — A unique name for this workflow (e.g., 'login', 'claim-asset'). This becomes part of the URL.
    • onAuth function (required) — The callback executed after the user successfully completes the flow in their wallet. This is where you handle the verified data.
    • claims object[] — An optional array of claims to request from the user (e.g., profile, signature).
    • onStart function — An optional callback executed when a new session is started.
    • onConnect function — An optional callback executed when the wallet scans the code. It overrides the constructor's onConnect for this specific action.
    • onDecline function — An optional callback executed if the user declines the request in their wallet.
    • onComplete function — An optional callback executed after the entire workflow is finished and the session token is destroyed.
    • onExpire function — An optional callback for when the session token expires.
    • onError function (default: console.error) — An optional callback for handling any errors during the process.
    • authPrincipal boolean | string (default: true) — Whether to request an authPrincipal claim first to establish the user's DID.
    • persistentDynamicClaims boolean (default: false) — Whether to persist dynamically generated claims in the session storage.

Example

Attach Login Handler

javascript
const express = require('express');
// ... setup for authenticator and tokenStorage from the constructor example

const app = express();
const handlers = new WalletHandlers({ authenticator, tokenStorage });

handlers.attach({
  app,
  action: 'login',
  claims: [{ type: 'profile' }], // Request user's profile
  onAuth: async ({ userDid, claims }) => {
    // Core logic after a successful login.
    // Find or create a user in your database with userDid.
    const profile = claims.find(x => x.type === 'profile');
    console.log(`User ${userDid} logged in with name: ${profile.fullName}.`);
    // You can now set a cookie or JWT for the user's web session.
  },
  onComplete: ({ token }) => {
    console.log(`Session ${token} completed and cleaned up.`);
  },
  onError: ({ error }) => {
    console.error('An error occurred in the login flow:', error);
  }
});

app.listen(3000, () => {
  console.log('DID Connect server is running on port 3000');
});

Events

Since WalletHandlers extends EventEmitter and listens to the tokenStorage instance, you can subscribe to events to monitor the session lifecycle. This is particularly useful for providing real-time feedback to the user, for example, by using WebSockets to update the UI when a QR code is scanned.

EventPayloadDescription
createdobjectEmitted when a new session token is created. The payload contains the token and initial session data.
updatedobjectEmitted when a session is updated. The payload includes the updated session data. A common use is to check for status: 'scanned'.
deletedobjectEmitted when a session token is destroyed, either upon completion, expiration, or manual timeout. The payload contains the token that was removed.

Example

Listening to Session Events

javascript
// Assuming 'io' is a Socket.IO server instance attached to your Express app
handlers.on('updated', (payload) => {
  // Notify the specific web client that the QR code has been scanned
  if (payload.status === 'scanned') {
    console.log(`Wallet scanned token: ${payload.token}`);
    // The 'sid' (socket ID) should be stored in the session when it's created
    if (payload.sid) {
      io.to(payload.sid).emit('wallet_scanned');
    }
  }
});

handlers.on('deleted', ({ token }) => {
  console.log(`Token ${token} was deleted from storage.`);
});