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

Dynamic Claims


In many scenarios, the information you need from a user isn't static. You might want to request different data based on who the user is, what device they're using, or other contextual information. DID Connect allows you to define claims dynamically using the onConnect callback.

This approach gives you the flexibility to tailor the authentication experience for each user interaction, rather than relying on a fixed set of claims for every session.

How It Works: The onConnect Callback#

The onConnect callback is a function executed right after the user scans the QR code and their wallet connects to your application, but before any claim requests are sent. This is the ideal moment to inspect the connection context and decide which claims to request.

You can provide this callback at two levels:

  • Globally: In the WalletHandlers constructor, applying to all configured actions.
  • Locally: In the handlers.attach() method, specific to a single action.

If both are defined, the global onConnect runs first, followed by the local one.

The onConnect Flow#

Here is the sequence of events when using a dynamic claim:

User's WalletApp ServerUser's BrowserUser's WalletApp ServerUser's BrowserLogic determines which claims to request based on context.Request login sessionReturn session token and QR code URLDisplay QR CodeScan QR and send initial connection (with user DID)Execute onConnect(context) callbackSend signed, dynamically generated claimsPresent claims to user for approvalSubmit user's response to claimsNotify browser of successful login

Implementing Dynamic Claims#

To implement dynamic claims, pass an onConnect function when you attach your authentication action.

Example: Requesting a Profile#

In this example, we use onConnect to dynamically request the user's full name and email address. This code would be part of your Express.js server setup.

const { WalletHandlers, WalletAuthenticator } = require('@arcblock/did-connect');
const MemoryAuthStorage = require('@arcblock/did-connect-storage-memory');
// ... other imports and server setup

const authenticator = new WalletAuthenticator({ wallet, appInfo }); // Configure as needed
const handlers = new WalletHandlers({ authenticator, tokenStorage: new MemoryAuthStorage() });

handlers.attach({
app: server, // Your express app instance
action: 'login',
onConnect: ({ userDid, didwallet }) => {
console.log(`User ${userDid} connected with wallet:`, didwallet);

// Return an array of claim definitions
return [
{
profile: () => ({
fields: ['fullName', 'email'],
description: 'Please provide your name and email to sign up.',
}),
},
];
},
onAuth: async ({ claims }) => {
// Process the claims received from the wallet
const profile = claims.find((x) => x.type === 'profile');
console.log('User profile:', profile);
return { successMessage: 'Login successful!' };
},
// ... other callbacks like onComplete, onError
});

Explanation#

  1. onConnect Trigger: The function is called as soon as the user's wallet scans the QR code.
  2. Context Parameters: It receives an object containing contextual information about the session. The most common properties are:

Parameter

Type

Description

userDid

string

The DID of the user connecting from the wallet.

didwallet

object

Information about the user's wallet, such as os ('ios', 'android') and version.

req

object

The Express request object, giving you access to headers, cookies, etc.

  1. Return Value: The function should return an array of claim definition objects. Each object's key is the claim type (e.g., profile), and its value is a function that returns the specific claim configuration. This structure is identical to how you would define static claims.

Advanced Use Case: Halting a Connection#

Besides returning claims, the onConnect callback can be used for access control. If you determine that a user should not proceed, you can throw an error from within the callback. This will halt the authentication process and send an error back to the wallet.

// ...
handlers.attach({
app: server,
action: 'login',
onConnect: ({ userDid }) => {
if (isBanned(userDid)) { // Your custom logic to check if a user is banned
throw new Error('This account has been suspended.');
}

// If not banned, proceed with the claim request
return [
{
profile: () => ({ fields: ['fullName'], description: 'Login to your account.' }),
},
];
},
onAuth: async ({ claims }) => {
// ...
},
});

By using onConnect, you can build more intelligent and secure authentication flows that adapt to the context of each connection.


Next, you might want to learn how to link multiple requests together. See our guide on Chaining Workflows to create multi-step user interactions.