Signature Claim
A Signature Claim is used to request a digital signature from the user's wallet. This is a fundamental operation for proving control over a Decentralized Identifier (DID). It can be used for various purposes, from signing a simple message to confirm an action, to authorizing a complex on-chain transaction.
DID Connect supports signing different types of data, including plain text, HTML, and pre-formatted blockchain transactions.
Request Flow#
The process of requesting and verifying a signature involves a clear sequence of interactions between your application, the DID Connect SDK, and the user's wallet.
Claim Parameters#
When defining a signature
claim, you can specify several parameters to control the signing process and the user experience in the wallet.
Parameter | Type | Description |
---|---|---|
|
| Required. Specifies the MIME type of the data to be signed. Common values include |
|
| The raw data to be signed, encoded in Base58. Either |
|
| A pre-computed hash of the data to be signed, encoded in Base58. This is useful for large data payloads where only the hash needs to be sent to the wallet. |
|
| The hashing algorithm the wallet should apply to the |
|
| A JSON string containing |
|
| A human-readable message explaining why the signature is being requested. |
|
| A unique value (e.g., a timestamp or random string) to prevent replay attacks. |
|
| An object specifying token or asset requirements the user must meet to be able to sign. See the Prepare Transaction Claim for details. |
Requesting a Signature#
You can request various types of signatures depending on your application's needs.
Example 1: Signing a Plain Text Message#
This is the most common use case, often used to verify user identity or consent.
// From: ocap-playground/api/routes/auth/claim-signature.js
const { toBase58 } = require('@ocap/util');
const { getRandomMessage } = require('../../libs/util');
const claims = {
signature: {
typeUrl: 'mime:text/plain',
origin: toBase58(Buffer.from(getRandomMessage())), // Data must be Base58 encoded
description: 'Please sign this message to log in.',
},
};
Example 2: Signing a Transaction#
For on-chain operations, you can construct a transaction, encode it, and ask the user to sign it. The origin
field should contain the Base58-encoded transaction buffer.
// From: ocap-playground/api/routes/auth/claim-signature.js
const { toBase58 } = require('@ocap/util');
const { fromPublicKey } = require('@ocap/wallet');
const { client, wallet } = require('../../libs/auth');
const env = require('../../libs/env');
// This function runs on the server to prepare the claim
const createTxSignatureClaim = async ({ userPk }) => {
const encoded = await client.encodeTransferV2Tx({
tx: {
itx: {
to: wallet.address, // The application's address
tokens: [
{
address: env.localTokenId,
value: (await client.fromTokenToUnit(1)).toString(),
},
],
},
},
wallet: fromPublicKey(userPk),
});
const origin = toBase58(encoded.buffer);
return {
typeUrl: 'fg:t:transaction',
description: 'Please sign to confirm the transfer of 1 TKN.',
origin: origin,
};
};
Example 3: Signing with a Nonce#
To prevent replay attacks, especially for authentication, include a nonce
. The nonce should be unique for each session or request.
// From: ocap-playground/api/routes/auth/claim-signature.js
const { toBase58 } = require('@ocap/util');
const claims = {
signature: () => {
const date = new Date();
return {
typeUrl: 'mime:text/plain',
origin: toBase58(Buffer.from('Login request')),
nonce: [date.getFullYear(), date.getMonth() + 1, date.getDate(), date.getHours()].join('-'),
description: `Please sign in for ${date.toDateString()}`,
};
},
};
Handling the Wallet Response#
After the user signs the data, the onAuth
callback on your server will receive the claim result, which now includes the signature.
Response Structure#
The claims
array in the onAuth
callback will contain an object for the signature claim with these additional fields:
Field | Type | Description |
---|---|---|
|
| The Base58-encoded signature generated by the user's wallet. |
|
| The hash method used by the wallet before signing. Matches the request. |
|
| The original data or digest that was signed. |
Verifying the Signature#
It is critical to verify the signature on your backend to confirm the user's identity and the integrity of the data. Use the user's public key (userPk
) provided in the onAuth
callback.
// From: ocap-playground/api/routes/auth/claim-signature.js
const { toTypeInfo } = require('@arcblock/did');
const { fromPublicKey } = require('@ocap/wallet');
const onAuth = async ({ userDid, userPk, claims }) => {
const type = toTypeInfo(userDid);
const userWallet = fromPublicKey(userPk, type);
const signatureClaim = claims.find(x => x.type === 'signature');
let isValid = false;
// If you sent 'origin' data
if (signatureClaim.origin) {
// The third argument indicates whether the data was hashed before signing.
// It should match the 'method' field. 'none' means it was not hashed.
isValid = await userWallet.verify(signatureClaim.origin, signatureClaim.sig, signatureClaim.method !== 'none');
}
// If you sent a 'digest'
else if (signatureClaim.digest) {
// When verifying a digest, the data was already hashed, so the third argument must be false.
isValid = await userWallet.verify(signatureClaim.digest, signatureClaim.sig, false);
}
if (!isValid) {
throw new Error('Signature verification failed.');
}
console.log('Signature is valid!');
// If it was a transaction, you can now broadcast it
if (signatureClaim.meta && signatureClaim.meta.origin) {
const tx = client.decodeTx(signatureClaim.meta.origin);
const hash = await client.sendTransferV2Tx({
tx,
wallet: userWallet,
signature: signatureClaim.sig,
});
console.log('Transaction sent, hash:', hash);
return { hash };
}
};
This onAuth
handler demonstrates the complete verification flow. It checks for either origin
or digest
in the response and uses the appropriate verification logic. If the signature is valid, the application can proceed with the intended action, such as logging the user in or broadcasting a transaction.
Next, learn how to request the wallet to assemble and sign a transaction when you only have partial information.
Next Step: Prepare Transaction Claim