Chaining Workflows
For complex user interactions that require multiple steps, such as a phased registration process or a sequential transaction approval, DID Connect allows you to chain multiple sessions together. This creates a continuous, multi-step workflow for the user, all within their wallet, providing a smooth and uninterrupted experience.
The mechanism for chaining is controlled from the onAuth
callback of a DID Connect session. By returning a specific object from this function, you can direct the user's wallet to immediately start a new session.
How It Works#
When a user successfully completes a DID Connect session, the onAuth
callback on your server is executed. To initiate the next step in a workflow, this callback should return an object containing the nextWorkflow
property.
Property | Type | Description |
---|---|---|
|
| The full DID Connect URL for the next session. The wallet will automatically navigate to this URL. |
|
| Optional. The session token for the next workflow. Useful if you want to manage the token on the client side. |
|
| Optional. A JSON object containing data you want to pass from the current session to the next one. |
This process is illustrated below:
Basic Chaining Example#
In this example, after a user provides their profile, the application immediately initiates a second workflow to have them receive a token. The onAuth
handler fetches the URL for the next session and returns it.
const axios = require('axios');
const env = require('../../libs/env');
module.exports = {
action: 'claim_multiple_workflow',
claims: {
profile: () => ({
description: 'Please provide your full profile',
fields: ['email'],
}),
},
onAuth: async ({ extraParams: { locale } }) => {
// After the profile is submitted, get the URL for the next step
const { data } = await axios.get(
`${env.appUrl}/api/did/receive_token/token?locale=${locale}&chain=local&amount=random`
);
// Return the URL to the wallet to start the next workflow
return { nextWorkflow: data.url };
},
};
When the wallet receives this response, it will seamlessly start the new session defined by data.url
without requiring further user action.
Passing State Between Workflows#
Often, you need to pass context or state from one step to the next, such as carrying over a user ID or an order confirmation. This is achieved using the nextWorkflowData
property.
Data passed in nextWorkflowData
from Session A will be available in the callbacks of Session B inside the extraParams.previousWorkflowData
object. If Session B then chains to Session C, the previousWorkflowData
from A is merged with the new nextWorkflowData
from B, making all accumulated data available to C.
Example: Multi-Step Registration#
Step 1: Collect Profile
In the first step, we collect the user's profile and pass their DID to the next step.
handlers.attach({
action: 'registration-step-1',
claims: {
profile: () => ({
fields: ['fullName', 'email'],
description: 'Please provide your name and email to start registration.',
}),
},
onAuth: async ({ userDid, claims }) => {
const profile = claims.find((x) => x.type === 'profile');
// Save the user profile to the database
await db.users.create({ did: userDid, ...profile });
// Get URL for the next step
const { data } = await axios.get(`${server.url}/api/did/registration-step-2/token`);
return {
nextWorkflow: data.url,
// Pass the user's DID to the next workflow
nextWorkflowData: { userDid: userDid, step: 1 },
};
},
});
Step 2: Agree to Terms
In the second step, we use the userDid
from the previous step to link the agreement to the correct user. The extraParams.previousWorkflowData
object contains the data from the first step.
handlers.attach({
action: 'registration-step-2',
// The onConnect callback receives data from the previous step
onConnect: ({ userDid, extraParams }) => {
const previousData = extraParams.previousWorkflowData;
// previousData is { userDid: '...', step: 1 }
if (userDid !== previousData.userDid) {
throw new Error('Please use the same DID you registered with.');
}
// Return the agreement claim for the user to sign
return {
agreement: () => ({
uri: 'https://example.com/terms-of-service.txt',
description: 'Please agree to our Terms of Service.',
}),
};
},
onAuth: async ({ userDid, claims }) => {
// ... Save the agreement confirmation for the user ...
return { successMessage: 'Registration complete!' };
},
});
This pattern allows you to build complex, stateful workflows that feel like a single, cohesive process to the end-user.
Now that you understand how to chain workflows, you can explore more advanced connection patterns. Continue to the next guide to learn about Delegated Connect.