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

Handling Wallet Responses


Once a DID Connect session is initiated, it progresses through a series of states, from the moment a user scans a QR code to the final submission of their response. The @arcblock/did-connect-js library allows you to hook into this lifecycle using a set of callback functions. These callbacks are your primary tool for defining the business logic of your application, whether it's checking permissions, processing user data, or handling errors.

Each callback is passed as an argument to the handlers.attach method, giving you precise control over the session flow. For a high-level view of the entire process, you may want to review The DID Connect Workflow first.

The Session Lifecycle#

The interaction between your application, the SDK, and the user's wallet can be visualized as a sequence of events. Each key event in the DID Connect protocol triggers a corresponding callback in your code, allowing you to react accordingly.


Lifecycle Callbacks#

You can provide any of the following callbacks to handlers.attach to manage the session. The onAuth callback is the only one that is required.

Callback

Description

onStart

Called when a new session starts and a challenge token is generated. Useful for logging or initial setup.

onConnect

Called after the user scans the QR code and connects their wallet, but before claims are sent. Ideal for permission checks or defining dynamic claims.

onAuth

Required. Called after the user approves the request in their wallet and submits the requested claims. This is where your primary business logic should reside.

onDecline

Called if the user explicitly rejects the request in their wallet.

onComplete

Called when the session has concluded, either through successful authentication or decline. Good for cleanup tasks.

onExpire

Called if the session token expires before the user completes the action.

onError

Called when an unexpected error occurs during the session. Defaults to console.error.

onConnect: Pre-Approval Logic and Dynamic Claims#

The onConnect callback is your first opportunity to interact with the user's DID. It's triggered as soon as the wallet establishes a connection. You can use this step to perform checks based on the user's DID or to dynamically determine which claims to request.

For example, you could check if userDid is in an allowlist. If the check fails, throwing an error from this callback will halt the session. You can also return a claims object to dynamically request information.

handlers.attach({
action: 'dynamic-claims',

// This function can be async
onConnect: ({ userDid }) => {
// Here you can check userDid against a database
console.log(`User connected: ${userDid}`);

// You can also return a claims object to dynamically request information
return {
profile: () => ({
fields: ['fullName', 'email'],
description: 'Please provide your name and email to continue',
}),
};
},

onAuth: async ({ claims, userDid }) => {
// `claims` will contain the result for the dynamically requested profile
const profile = claims.find((x) => x.type === 'profile');
console.log('Auth success', { userDid, profile });
},
});

onAuth: Handling Submitted Data#

The onAuth callback is the core of your DID Connect logic. It executes only after the user has approved the request and submitted the required claims. The claims parameter is an array containing the data you requested.

handlers.attach({
action: 'profile-and-asset',
claims: {
profile: () => ({
fields: ['fullName', 'email'],
description: 'Please provide your name and email to continue',
}),
asset: () => ({
description: 'Please provide a valid NFT',
trustedIssuers: ['z8iZhf5aT4C9g4aP35m2J5Yoe1g4i5J2E4o2'],
}),
},

onAuth: async ({ userDid, claims }) => {
// `claims` contains what the user has submitted
try {
const profile = claims.find((x) => x.type === 'profile');
const asset = claims.find((x) => x.type === 'asset');
console.info('Login success:', { userDid, profile, asset });
// TODO: Add your business logic here, e.g., create a user session, grant access, etc.
} catch (err) {
console.error('Login error:', err);
}
},
});

onDecline: Handling Rejection#

If a user chooses to decline the request in their wallet, the onDecline callback is triggered. This allows you to gracefully handle the rejection, for instance, by logging the event or displaying an appropriate message on the frontend.

handlers.attach({
action: 'profile-login',
claims: {
profile: () => ({
fields: ['fullName'],
description: 'Please provide your name to continue',
}),
},
onAuth: async ({ userDid, claims }) => {
// ... handle success
},
onDecline: ({ userDid }) => {
console.log(`User ${userDid} declined the connection request.`);
// You might update the UI to show a 'request declined' message
},
});

updateSession: Persisting Data for the Frontend#

Within callbacks like onAuth or onConnect, you may need to persist data that the frontend can later retrieve, such as a login token or a transaction status. The updateSession function, available as a parameter to most callbacks, allows you to save data to the session store.

handlers.attach({
action: 'complex-flow',
onAuth: async ({ userDid, updateSession }) => {
const loginToken = 'user-login-token-generated-on-backend';

// Persist non-sensitive info
await updateSession({ status: 'completed' });

// To persist sensitive info, set the second argument to true to encrypt it
await updateSession({ authToken: loginToken }, true);
},
});

The frontend can then poll the session status endpoint to retrieve this data once the wallet interaction is complete.

Next Steps#

Now that you know how to manage wallet responses, you can build more sophisticated user journeys. Consider learning how to link multiple DID Connect requests together or diving deeper into the API.