Used to check for browser translation.
用于检测浏览器翻译。
ブラウザの翻訳を検出する
How-to Guides

Chain Multiple Workflows


DID Connect allows you to chain multiple interactions into a single, continuous user experience. Instead of a single request that asks for everything at once, you can create multi-step workflows, such as logging a user in, then asking for a verifiable credential, and finally requesting a signature for a transaction. This is accomplished using the nextWorkflow property in the onAuth callback.

This guide will walk you through creating a multi-step workflow where data is passed from one step to the next.

The Concept: nextWorkflow#

The core of this feature lies in the onAuth callback. When a user successfully completes a step in your workflow, your onAuth function can return an object containing a nextWorkflow URL and a nextToken. The DID Connect library then instructs the wallet to automatically proceed to this new URL, starting the next step in the chain without requiring the user to scan another QR code.

The library manages the session state, ensuring that the initial session is only marked as fully complete after the final workflow in the chain has succeeded.

Here is a diagram illustrating the sequence of events in a two-step workflow:

DID WalletApp ServerUserDID WalletApp ServerUseronAuth for A is called.Generate token/URL for Workflow B.onAuth for B is called.No `nextWorkflow` returned.Initiate Workflow A (e.g., Login)Return claims for Workflow ADisplay prompt for Workflow AApprove Workflow ASubmit signed response for AReturn `nextWorkflow` URL for BAutomatically request `nextWorkflow` URLReturn claims for Workflow BDisplay prompt for Workflow BApprove Workflow BSubmit signed response for BReturn final success message

Passing Data Between Steps#

To make workflows powerful, you often need to pass context from one step to the next. You can achieve this using nextWorkflowData and previousWorkflowData.

  1. Sending Data (nextWorkflowData): In your onAuth callback, return a JSON object in the nextWorkflowData property. This object will be passed to the next step.
  2. Receiving Data (previousWorkflowData): The library automatically encodes this data and appends it to the nextWorkflow URL. In the subsequent step, this data becomes available in the onStart or onAuth callback via extraParams.previousWorkflowData.

The library intelligently merges data at each step. If workflow A passes { a: 'a' } to workflow B, and workflow B passes { b: 'b' }, the data received by workflow C will be a merged object: { a: 'a', b: 'b' }.

Example: Two-Step User Onboarding#

Let's implement a two-step process: first, the user connects their wallet (login), and second, we ask for their profile information. We'll also pass the user's DID from the first step to the second.

Step 1: Set up the DID Connect Handler#

We'll define a single action endpoint /api/did/onboarding that handles both steps. The logic inside the onAuth callback will determine what to do next.

// lib/index.js

const { WalletHandlers, WalletAuthenticator } = require('@did-connect/server');
const MemoryAuthStorage = require('@did-connect/storage-memory');
const axios = require('axios');

// ... (setup for express app, wallet, etc.)

const authenticator = new WalletAuthenticator({ wallet, appInfo });
const tokenStorage = new MemoryAuthStorage();
const handlers = new WalletHandlers({ authenticator, tokenStorage });

let serverUrl = ''; // Will be set when server starts

handlers.attach({
app,
action: 'onboarding',
claims: {
profile: () => ({
fields: ['fullName', 'email'],
description: 'Please provide your profile information for onboarding.',
}),
},
onAuth: async ({ userDid, claims, step, extraParams }) => {
// Step 0 is the initial authPrincipal (login) claim
if (step === 0) {
console.log(`User ${userDid} just logged in. Proceeding to profile step.`);

// Generate the token for the next step in the workflow
const { data } = await axios.get(`${serverUrl}/api/did/onboarding/token`);

return {
nextWorkflow: data.url,
nextToken: data.token,
nextWorkflowData: {
didFromLogin: userDid
},
};
}

// Step 1 is the profile claim
if (step === 1) {
const profile = claims.find(x => x.type === 'profile');
console.log('Onboarding complete for user:', userDid);
console.log('Received profile:', profile);
console.log('DID from login step:', extraParams.previousWorkflowData.didFromLogin);
return { successMessage: 'Onboarding complete!' };
}

// Fallback for any other steps
return {};
},
onStart: ({ extraParams }) => {
if (extraParams.previousWorkflowData) {
console.log('Starting next step with data from previous workflow:');
console.log(extraParams.previousWorkflowData);
}
}
});

// ... (start express server and set serverUrl)
// server.listen(port, () => { serverUrl = `http://localhost:${port}`; });

Step 2: Understand the Flow#

  1. Initiation: Your application generates a DID Connect QR code for the /api/did/onboarding/token endpoint.
  2. User Login: The user scans the code. The default authPrincipal claim is processed. The onAuth callback is triggered for step: 0.
  3. Chaining Logic: Inside onAuth for step: 0, we detect it's the first step. We programmatically call our own /token endpoint again to get a new URL (nextWorkflow) and session ID (nextToken) for the next step. We also include nextWorkflowData.
  4. Wallet Redirect: The wallet receives the nextWorkflow and automatically navigates to it. This triggers the second part of the flow: the profile claim.
  5. Data Received: The onStart callback for the second step logs the previousWorkflowData that was passed from the first step.
  6. User Provides Profile: The user approves the request for their profile information.
  7. Completion: The onAuth callback is triggered for step: 1. Since we don't return another nextWorkflow, the chain ends. The library marks both the initial session and the second session as succeed.

By returning nextWorkflow and nextToken from the onAuth callback, you can create sophisticated, user-friendly, multi-step interactions without friction.

To explore more advanced patterns, see how to implement Delegated Connect or use Dynamic Claims.