- NodeJS
- NodeJS (server-auth)
Once you have created a wallet (or any other resource) with a user as an owner or signer, you can transact on that wallet with a valid user JWT. To do so using the NodeJS / Typescript SDK, the SDK will:Sending a transaction will look like this:
- Generate an ECDH P-256 keypair.
- Request a time-bound session key from the
/v1/wallets/authenticateendpoint using the user’s JWT and the public key of the ECDH keypair. - Send a transaction to the Wallet API, signed with the time-bound session key.
user_jwt on the authorization context when making requests to a resource.Send a transaction via the Wallet API
With the user’s JWT, your application can request a transaction via the Wallet API by setting up the authorization context like so:Report incorrect code
Copy
Ask AI
const authorizationContext = {
user_jwts: ['insert-user-jwt']
};
Report incorrect code
Copy
Ask AI
import {PrivyClient} from '@privy-io/node';
const userJwt = 'insert-user-jwt';
const walletId = 'insert-wallet-id';
const privy = new PrivyClient({
appId: 'insert-your-app-id',
appSecret: 'insert-your-app-secret'
});
// This request will automatically include the privy-authorization-signature header.
const res = await privy
.wallets()
.ethereum()
.sendTransaction(walletId, {
caip2: `eip155:8453`,
params: {
transaction: {
// Transaction details go here...
}
},
authorization_context: {user_jwts: [userJwt]}
});
The
@privy-io/server-auth library is deprecated. We recommend integrating @privy-io/node for
the latest features and support.- Generate a SPKI-formatted ECDH P-256 keypair.
- Request a time-bound session key from the
/v1/wallets/authenticateendpoint using the user’s JWT and the public key of the ECDH keypair. - Send a transaction to the Wallet API, signed with the time-bound session key.
Generate an ECDH P-256 keypair
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.Report incorrect code
Copy
Ask AI
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};
}
POST /v1/wallets/authenticate
Next, request a time-bound session key via the/v1/wallets/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/wallets/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.Report incorrect code
Copy
Ask AI
https://api.privy.io/v1/wallets/authenticate
Body
A request body to/v1/wallets/authenticate contains the following parameters.The user’s JWT, to be used to authenticate the user.
The encryption type for the authentication response. Currently only supports HPKE.
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 base64-encoded DER format.
Response
A successful response will contain the following fields.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.Hide child attributes
Hide child attributes
The encryption type used. Currently only supports HPKE.
Base64-encoded ephemeral public key used in the HPKE encryption process. Required for decryption.
The encrypted authorization key corresponding to the user’s current authentication session.
The expiration time of the authorization key in seconds since the epoch.
The wallets that the signer has access to.
Example
For example, your application may make a request to the/v1/wallets/authenticate endpoint with the following parameters.Report incorrect code
Copy
Ask AI
async function authenticateUserWallet() {
const jwt = 'your-user-jwt';
const appId = 'your-app-id';
const appSecret = 'your-app-secret';
// SPKI-formatted ECDH P-256 public key
const {recipientPublicKey} = await generateEcdhP256KeyPair();
const basicAuth = Buffer.from(`${appId}:${appSecret}`).toString('base64');
const response = await fetch(`https://api.privy.io/v1/wallets/authenticate`, {
method: 'POST',
headers: {
Authorization: `Basic ${basicAuth}`,
'Content-Type': 'application/json',
'privy-app-id': appId
},
body: JSON.stringify({
user_jwt: jwt,
encryption_type: 'HPKE',
recipient_public_key: recipientPublicKey
})
});
return await response.json();
}
Report incorrect code
Copy
Ask AI
{
"encrypted_authorization_key": {
"encryption_type": "HPKE",
"encapsulated_key": "<encapsulated-key>",
"ciphertext": "<ciphertext>"
},
"expires_at": 1715270400,
"wallets": [
{
"id": "<wallet-id>",
"chain_type": "ethereum",
"address": "<wallet-address>"
}
]
}
Decrypt /v1/wallets/authenticate response
In order to send a transaction, decrypt the encapsulatedKey with theprivateKey of your ECDH keypair.Report incorrect code
Copy
Ask AI
import {CipherSuite, DhkemP256HkdfSha256, HkdfSha256, Chacha20Poly1305} from '@hpke/core';
const resp = await authenticateUserWallet();
const {encrypted_authorization_key} = resp;
const privateKey: CryptoKey = 'private-key-from-generateEcdhP256KeyPair';
async function decryptEncapsulatedKey(encrypted_authorization_key, privateKey): Promise<string> {
const {encapsulated_key: encapsulatedKey, ciphertext} = encrypted_authorization_key;
// Step 1: Create the HPKE cipher suite
const suite = new CipherSuite({
kem: new DhkemP256HkdfSha256(),
kdf: new HkdfSha256(),
aead: new Chacha20Poly1305()
});
// Step 2: Initialize the recipient context using the privateKey and the encapsulated_key
const context = await suite.createRecipientContext({
recipientKey: privateKey,
enc: Buffer.from(encapsulatedKey, 'base64')
});
// Step 3: Decrypt the ciphertext (which contains a base64-encoded PKCS8 private key)
const decrypted = await context.open(Buffer.from(ciphertext, 'base64'));
// Step 4: Return the decrypted key as a base64-encoded PKCS8 string
return Buffer.from(decrypted).toString('utf8');
}
authorizationPrivateKey to sign requests to the Wallet API.Send a transaction via the Wallet API
With the decryptedauthorizationPrivateKey, your application can request a transaction via the Wallet API.Learn how to generate an authorization signature and send a transaction via the Wallet API.The fastest way to start sending transactions with an authorization signature is via the Server SDK.Report incorrect code
Copy
Ask AI
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'
});

