Sign messages

Prompt users to sign a message to prove they control an address or authorize an in-app action.

The ability for a user to sign a cryptographic message to prove ownership of a particular address is a key feature provided by Stacks Connect.

In this guide, you will learn how to:

  1. Setup and install necessary packages.
  2. Prompt users to sign a message.
  3. Process and handle the results.
  4. Verify the signature.

Setup and installation

Using your preferred package manager, setup and install a new project with the following packages:

  • @stacks/network: Used to interact with the Stacks blockchain network.
  • @stacks/encryption: Used to sign and verify messages.
  • @stacks/connect: Used to authenticate users and broadcast the transactions.
npm install @stacks/network @stacks/encryption @stacks/connect

Initiate a session

Users must authenticate to an app before you request message signing. Users can install an authenticator like the Leather wallet.

If you haven't already, refer to the authentication guide before proceeding to integrate the following message signing capabilities.

To prompt users to log in, use the showConnect function from the @stacks/connect package:

import { AppConfig, UserSession, showConnect } from '@stacks/connect';

const appConfig = new AppConfig(['store_write', 'publish_data']);
const userSession = new UserSession({ appConfig });

function authenticate() {
  showConnect({
    appDetails: {
      name: 'My App',
      icon: window.location.origin + '/my-app-logo.svg',
    },
    redirectTo: '/',
    onFinish: () => {
      let userData = userSession.loadUserData();
    },
    userSession: userSession,
  });
}

Prompt users to sign a message

Call the openSignatureRequestPopup function provided by the @stacks/connect package to trigger the display of the message signing prompt:

import { openSignatureRequestPopup } from '@stacks/connect';
import { StacksTestnet } from '@stacks/network';

const message = 'Hello World';

openSignatureRequestPopup({
  message,
  network: new StacksTestnet(), // for mainnet, `new StacksMainnet()`
  appDetails: {
    name: 'My App',
    icon: window.location.origin + '/my-app-logo.svg',
  },
  onFinish(data) {
    console.log('Signature of the message', data.signature);
    console.log('Use public key:', data.publicKey);
  },
});

All of the methods included on this page accept a network option. By default, Connect uses a testnet network option. You can import a network configuration from the @stacks/network package:

import { StacksTestnet, StacksMainnet } from '@stacks/network';

const testnet = new StacksTestnet();
const mainnet = new StacksMainnet();

// use this in your messe signing method:

openSignatureRequestPopup({
  network: mainnet,
  // other relevant options
});

Several parameters are available for calling openSignatureRequestPopup:

interface SignatureRequestOptions {
  message: string;
  onFinish?: (data: SignatureData) => void;
  onCancel?: (data: SignatureData) => void;
  appDetails: {
    name: string;
    icon: string;
  };
  authOrigin?: string;
  stxAddress?: string;
  userSession?: UserSession;
}

Process the signed message

The openSignatureRequestPopup method from @stacks/connect allows you to specify an onFinish callback. This callback will be triggered after the user has successfully signed the message.

You can get the signature of the message via the arguments passed to onFinish. Your callback will be fired with a single data argument:

const onFinish = (data: SignatureData) => {
  const { signature, publicKey } = data;

  console.log('Signature', signature);
  console.log('PublicKey', publicKey);
};
export interface SignatureData {
  /* Hex encoded DER signature */
  signature: string;
  /* Hex encoded private string taken from privateKey */
  publicKey: string;
}

Verify the signature

When you verify a signature, you're confirming that the message was indeed created by the claimed sender and that it hasn't been altered since it was signed. To do this, use the verifyMessageSignatureRsv function from the @stacks/encryption package:

import type { SignatureData } from '@stacks/connect';
import { verifyMessageSignatureRsv } from '@stacks/encryption';

const message = 'Hello World';

openSignatureRequestPopup({
  message,
  network: new StacksTestnet(),
  appDetails: {
    name: 'My App',
    icon: window.location.origin + '/my-app-logo.svg',
  },
  onFinish(data: SignatureData) {
    const { signature, publicKey } = data;
    const verified = verifyMessageSignatureRsv({ message, publicKey, signature });
    if (verified) {
      /* your logic here */
    }
  },
});

Next steps