WalletHandlers
The WalletHandlers
class is a server-side component designed to integrate DID Connect into web frameworks like Express.js. It automates the creation of the required API endpoints, simplifying the backend implementation of a DID Connect session. This class works in tandem with the WalletAuthenticator
, which handles the cryptographic signing and verification of messages.
WalletHandlers
extends Node.js's EventEmitter
, allowing you to listen for key events during the authentication lifecycle.
How it Works#
The WalletHandlers
class sets up a series of HTTP endpoints for a given action
(e.g., 'login'). These endpoints manage the entire DID Connect flow, from generating a session token to receiving the final wallet response.
Constructor#
new WalletHandlers(config)
Creates an instance of the DID Connect handlers. This instance can then be used to attach multiple workflows (actions) to your application.
Parameter | Type | Description |
---|---|---|
|
| The configuration object for the handler instance. |
|
| Required. An object responsible for creating, reading, and destroying session tokens. You can use |
|
| Required. An instance of |
|
| Optional. A function to transform the URL path included in the DID Connect deep link. Defaults to an identity function. |
|
| Optional. A global function called just before a claims request is sent to the wallet. It can be used for session validation or permission checks. Throwing an error will halt the process. |
|
| Optional. An object for customizing endpoint behavior and parameters. |
|
| The URL prefix for all created endpoints. Defaults to |
|
| The delay in milliseconds before a completed session is cleaned up from storage. Defaults to |
|
| The query parameter key for the session token. Defaults to |
|
| The query parameter key for the encryption key. Defaults to |
|
| The query parameter key for the protocol version. Defaults to |
Example#
const { WalletAuthenticator, WalletHandlers } = require('@did-connect/server');
const { fromRandom } = require('@ocap/wallet');
const TokenStorage = require('@did-connect/storage-memory');
// 1. Configure the authenticator with your app's wallet and info
const appWallet = fromRandom();
const authenticator = new WalletAuthenticator({
wallet: appWallet,
appInfo: {
name: 'My App',
description: 'My App Description',
icon: 'https://example.com/icon.png',
link: 'https://example.com',
},
});
// 2. Create a handler instance
const tokenStorage = new TokenStorage();
const handlers = new WalletHandlers({
authenticator,
tokenStorage,
});
// Now, the 'handlers' instance is ready to have actions attached to it.
Methods#
attach(config)
#
The .attach()
method is the primary way to configure and enable a specific DID Connect workflow, known as an "action," on your web server. It registers a set of standard routes with your Express app instance.
For an action named {action}
, the following routes are created under the configured prefix
:
GET /{prefix}/{action}/token
: Creates a new session token and a deep link URL for the QR code.POST /{prefix}/{action}/token
: Also creates a new session token.GET /{prefix}/{action}/status
: Allows the web client to poll for the session status.GET /{prefix}/{action}/timeout
: Allows the web client to manually expire a token.GET /{prefix}/{action}/auth
: Endpoint for the wallet to fetch the claims request.POST /{prefix}/{action}/auth
: Endpoint for the wallet to submit the signed response.GET /{prefix}/{action}/auth/submit
: Alternative submission endpoint for web wallets.
Parameter | Type | Description |
---|---|---|
|
| Required. The Express application instance to attach the routes to. |
|
| Required. A unique name for this workflow (e.g., 'login', 'payment'). This becomes part of the URL path. |
|
| An array of claim objects that your application is requesting from the user. For example, |
|
| Required. A callback that executes when the user successfully authenticates in their wallet and the signed response is verified. This is where you handle the received claims. Signature: |
|
| Optional. A callback that runs when a new session token is generated. |
|
| Optional. A callback that runs when the wallet scans the QR code and requests the claims. This overrides the global |
|
| Optional. A callback for when the user explicitly declines the request in their wallet. Signature: |
|
| Optional. A callback that runs after the entire process is finished for a session (either success or decline). Useful for final cleanup. |
|
| Optional. A callback for when the session token expires before completion. |
|
| Optional. A callback for handling any errors that occur during the process. Defaults to |
|
| Optional. Specifies if an |
|
| Optional. If set to |
Example#
This example sets up a simple login flow on an Express server.
const express = require('express');
const { WalletAuthenticator, WalletHandlers } = require('@did-connect/server');
const { fromRandom } = require('@ocap/wallet');
const TokenStorage = require('@did-connect/storage-memory');
const app = express();
const port = 3000;
// 1. Configure Authenticator
const appWallet = fromRandom();
const authenticator = new WalletAuthenticator({
wallet: appWallet,
appInfo: {
name: 'My Express App',
description: 'A simple DID Connect login example',
icon: 'https://arcblock.oss-cn-beijing.aliyuncs.com/images/wallet-round.png',
link: `http://localhost:${port}`
},
});
// 2. Configure Handlers
const tokenStorage = new TokenStorage();
const handlers = new WalletHandlers({
authenticator,
tokenStorage,
});
// 3. Attach a "login" action to the Express app
handlers.attach({
app,
action: 'login',
claims: [
{ type: 'profile', fields: ['fullName', 'email', 'avatar'] }
],
onAuth: (req, res, claims) => {
// This function is called upon successful authentication
const userProfile = claims.find(x => x.type === 'profile').fields;
console.log('User authenticated:', userProfile.fullName);
// Here, you would typically create a user session or JWT for your app
// The session status in tokenStorage is automatically updated to 'succeed'.
},
onDecline: (req, res, token) => {
console.log(`User declined session: ${token}`);
},
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
console.log(`To start a login session, open: http://localhost:${port}/api/did/login/token`);
});
Events#
Since WalletHandlers
is an event emitter, you can listen for changes in the session store.
created
: Emitted when a new session token is created.(data)
updated
: Emitted when a session's state changes (e.g., from 'created' to 'scanned', then to 'succeed').(payload)
deleted
: Emitted when a session token is destroyed after completion or expiration.({ token })
Example#
handlers.on('updated', (payload) => {
console.log('Session updated:', payload.token, 'Status:', payload.status);
// You could use this event to push updates to the client via WebSockets.
});
With WalletHandlers
, you can quickly set up a robust backend for any DID Connect workflow. For more complete, end-to-end implementations, please see the Simple Login example.