The first step to transact with a wallet via a user signer is to generate a SPKI-formatted ECDH P-256 keypair. The public key will be used to encrypt the session key and the private key will be used to decrypt the encapsulated key.
import * as crypto from 'crypto';async function generateEcdhP256KeyPair(): Promise<{ privateKey: CryptoKey; recipientPublicKey: string;}> { // Generate a P-256 key pair const keyPair = await crypto.subtle.generateKey( { name: 'ECDH', namedCurve: 'P-256' }, true, ['deriveBits'] ); // The privateKey will be used later to decrypt the encapsulatedKey data returned from the /v1/user_signers/authenticate endpoint. const privateKey = keyPair.privateKey; // The publicKey will be used to encrypt the session key and will be sent to the /v1/user_signers/authenticate endpoint. // The publicKey must be a base64-encoded, SPKI-format string const publicKeyInSpkiFormat = await crypto.subtle.exportKey('spki', keyPair.publicKey); const recipientPublicKey = Buffer.from(publicKeyInSpkiFormat).toString('base64'); return {privateKey, recipientPublicKey};}
Next, request a time-bound session key via the /v1/user_signers/authenticate endpoint. This key will be used to sign the request before it is submitted to the Privy Wallet API. The expiration time of the key is returned in the response.
The /v1/user_signers/authenticate endpoint integrates directly with the JWT-based authentication settings configured in the Privy dashboard. In particular, the JWT is verified according to the registered JWKS.json endpoint. This endpoint uniquely identifies users based on the subject ID (the sub claim) within the JWT and verifies the JWT is authorized to transact on the wallet.
The public key of your ECDH keypair, in base64-encoded, SPKI-format, whose private key will be
able to decrypt the session key. This keypair must be generated securely and the private key must
be kept confidential. The public key sent should be in DER or PEM format. It is recommended to use
DER format.
The encrypted authorization key, once decrypted, can be used to sign transactions on the wallet, acting as a temporary AuthorizationPrivateKey.
Once decrypted, you will need to generate an authorization signature and pass it as a header under privy-authorization-signature.
The fastest way to start sending transactions with an authorization signature is via the Server SDK.
import {PrivyClient} from '@privy-io/server-auth';const resp = await authenticateUserWallet();const {encrypted_authorization_key, wallets} = resp;const decryptedKey = await decryptEncapsulatedKey(encrypted_authorization_key, privateKey);const walletId = wallets[0].id;const client = new PrivyClient($PRIVY_APP_ID, $PRIVY_APP_SECRET, { walletApi: { // PrivyClient will automatically inject an authorization signature into the privy-authorization-signature header using this key. authorizationPrivateKey: decryptedKey }});// This request will automatically include the privy-authorization-signature header.const res = await client.walletApi.ethereum.signMessage({ walletId: walletId, message: 'Hello world'});