RISE Logo-Light

Register Signer

How to register a signer for API authentication

Authentication: Register Signer

Before making any authenticated API calls, you need to register a signer. The signer is a private key that will be used to sign transactions on behalf of your account.

Step 1: Generate Signer Key

// Generate a new private key for the signer
const signingKey = generatePrivateKey();
const signerAccount = privateKeyToAccount(signingKey);
const signerAddress = signerAccount.address; // This is your signer address

Step 2: Create Signer Signature

const createSignerSignature = async (
  account: `0x${string}`,
  authContractAddress: `0x${string}`,
) => {
  // Generate a new private key for the signer
  const signingKey = generatePrivateKey();
  const signerAccount = privateKeyToAccount(signingKey);
  const signerAddress = signerAccount.address;
  
  // Create nonce
  const nonce = createClientNonce(account);
  const domain = getRISExDomain(authContractAddress);

  // Sign with the signer's private key
  const signingKeySignature = await signerAccount.signTypedData({
    domain,
    types: { VerifySigner: REGISTER_TYPES.VerifySigner },
    message: {
      account,
      nonce, // Note: nonce is a string, not BigInt
    },
    primaryType: 'VerifySigner',
  });

  return {
    signerAddress,
    signingKeySignature,
    signingKey, // Store this securely for future use
    nonce,
  };
};

Step 3: Create Account Signature (User Wallet Signature)

const createAccountSignature = async (
  signerAddress: `0x${string}`,
  nonce: string,
  authContractAddress: `0x${string}`,
  // You'll need a wallet provider (like wagmi, ethers, etc.)
  signTypedDataAsync: (data: any) => Promise<string>,
) => {
  const registerMessage = {
    signer: signerAddress,
    message: 'Please sign in with your wallet to access RISEx.',
    nonce,
    expiration: dayjs().add(7, 'day').unix(), // 7 days from now
  };

  const domain = getRISExDomain(authContractAddress);

  // User signs with their wallet
  const accountSignature = await signTypedDataAsync({
    types: REGISTER_TYPES,
    message: {
      signer: registerMessage.signer,
      message: registerMessage.message,
      nonce: BigInt(registerMessage.nonce),
      expiration: BigInt(registerMessage.expiration),
    },
    primaryType: 'RegisterSigner',
    domain: domain as any,
  });

  // Viem returns properly formatted signatures - no additional formatting needed
  return {
    accountSignature,
    registerMessage,
  };
};

Step 4: Register Signer via API

const registerSigner = async (
  account: `0x${string}`,
  authContractAddress: `0x${string}`,
  signTypedDataAsync: (data: any) => Promise<string>,
) => {
  try {
    // Step 1: Generate signer and create signer signature
    const { signerAddress, signingKeySignature, signingKey, nonce } =
      await createSignerSignature(account, authContractAddress);

    // Step 2: Get user wallet signature
    const { accountSignature, registerMessage } = await createAccountSignature(
      signerAddress,
      nonce,
      authContractAddress,
      signTypedDataAsync,
    );

    // Step 3: Register via API
    const response = await apiClient.post('/v1/auth/register-signer', {
      account: account,
      signer: registerMessage.signer,
      message: registerMessage.message,
      nonce: registerMessage.nonce,
      expiration: registerMessage.expiration.toString(),
      account_signature: accountSignature, // Note: uses account_signature, not signature
      signer_signature: signingKeySignature, // Note: uses signer_signature, not signature
    });

    if (response.data) {
      console.log('Signer registered successfully:', {
        account,
        signer: signerAddress,
        signingKey, // Store this securely!
        expiration: registerMessage.expiration,
      });
      
      // IMPORTANT: Store signingKey securely for future API calls
      return {
        signer: signerAddress,
        signingKey, // Keep this secret!
      };
    }
  } catch (error) {
    if (axios.isAxiosError(error)) {
      console.error('Failed to register signer:', error.response?.data?.message || error.message);
    } else {
      console.error('Failed to register signer:', error);
    }
    throw error;
  }
};

Important Notes

  • One-time setup: Register signer once per account
  • Store securely: Keep the signingKey private and secure - it authorizes all transactions
  • Expiration: Signers expire after the expiration time (typically 7 days)
  • Re-registration: You may need to re-register if the signer expires