Request a Signature
One of the most common tasks in a decentralized application is asking a user to sign a piece of data. This could be to authorize a transaction, confirm terms of service, or simply to prove control over their DID. The did-auth library simplifies this process using the signature claim.
This guide will walk you through how to request a signature for both a simple message and an on-chain transaction.
How It Works#
The signature process involves your application, the did-auth SDK, and the user's DID Wallet. The wallet acts as a remote signer, ensuring the user's private key never leaves their device.
Here is the typical flow:
Requesting a Message Signature#
Let's start with the simplest case: asking a user to sign a plain text message. You'll define this request within the onConnect handler of your WalletHandlers configuration.
const { WalletAuthenticator, WalletHandlers } = require('@arcblock/did-auth');
const authenticator = new WalletAuthenticator({
wallet: 'z1....',
appInfo: { /* ... */ },
});
const handlers = new WalletHandlers({
authenticator,
tokenStorage,
onConnect: async ({ claims }) => {
// Request a signature on a simple text message
claims.push({
type: 'signature',
description: 'Please sign this message to confirm your identity.',
typeUrl: 'mime:text/plain',
data: 'This is a test message from our application.',
});
},
// ... other handlers
});
// In your Express app
// app.use('/api/did/auth', handlers);In this example, when a user scans the QR code, their wallet will prompt them to sign the string "This is a test message from our application.". After they approve, the signature will be sent back to your application for verification.
Requesting a Transaction Signature#
Requesting a signature for an on-chain transaction follows the same pattern but requires more specific information, such as the transaction type and its payload. You have two primary ways to do this.
Option 1: Provide a Raw Transaction Object (Recommended)#
You can provide a standard JavaScript object representing the transaction. The did-auth library will automatically encode it for you based on the chainInfo you provided to the WalletAuthenticator.
const handlers = new WalletHandlers({
authenticator,
tokenStorage,
onConnect: async ({ claims, userDid, userPk }) => {
// Request signature for a raw transaction object
claims.push({
type: 'signature',
description: 'Please sign this transaction to transfer 100 tokens.',
typeUrl: 'TransferV2Tx', // Specific transaction type for the target chain
data: {
from: userDid, // The user's DID
pk: userPk, // The user's public key
itx: {
to: 'z1d...',
tokens: [
{
address: 'z35nB2SA6xxBuoaiuUXUw6Gah2SNU3UzqdgEt',
value: '100',
},
],
},
},
// Optionally, specify requirements for the signature
requirement: {
tokens: [
{
address: 'z35nB2SA6xxBuoaiuUXUw6Gah2SNU3UzqdgEt',
value: '100',
},
],
},
});
},
// ... other handlers
});Option 2: Provide a Pre-Encoded Transaction#
For more control, you can encode the transaction yourself using a library like @ocap/client and provide the resulting buffer as a Base58 string. This is useful if your encoding logic is complex or managed elsewhere.
const Client = require('@ocap/client');
const { toBase58 } = require('@ocap/util');
const { fromPublicKey } = require('@ocap/wallet');
const client = new Client('https://beta.abtnetwork.io/api');
const handlers = new WalletHandlers({
authenticator,
tokenStorage,
onConnect: async ({ claims, userDid, userPk }) => {
// 1. Encode the transaction first
const { buffer: encodedTx } = await client.encodeTransferV2Tx({
tx: { /* ... transaction data from Option 1 ... */ },
wallet: fromPublicKey(userPk),
});
// 2. Request signature for the encoded transaction buffer
claims.push({
type: 'signature',
description: 'Please sign this pre-encoded transaction.',
typeUrl: 'fg:t:transaction', // Generic type for any encoded transaction
origin: toBase58(encodedTx), // Pass the encoded buffer as a Base58 string
requirement: { /* ... */ },
});
},
// ... other handlers
});Key Parameters for Signature Claims#
The signature claim has several configurable parameters to meet different needs.
Parameter | Type | Description |
|---|---|---|
|
| Required. The type of data to be signed. E.g., |
|
| A human-readable description of the signature request, shown to the user in the wallet. Defaults to a standard message. |
|
| The content to be signed. For |
|
| Used for pre-encoded data. For |
|
| A JSON string for displaying rich content in the wallet's UI, such as a URL or structured text. |
|
| Hashing algorithm to use before signing. Defaults to |
|
| An optional random string to associate with the signature request, useful for preventing replay attacks. |
|
| An optional object specifying tokens or assets the user must possess to complete the signature. |
Now that you know how to request signatures, you can build more complex workflows. To learn how to chain multiple requests together, proceed to the Handle Multiple Workflows guide. For a complete list of all claim types and their parameters, refer to the Schemas documentation.