Appearance
Authorization signatures
If your app has authorization keys enabled in the Dashboard, you must sign certain requests to the Privy API with an authorization key and include the produced signature (called an authorization signature) as a request header. The authorization signature ensures that your wallets only execute actions requested directly by your server.
Authorization signatures are always required on POST
requests to /api/v1/wallets/<wallet_id>/rpc
and are optionally required on POST
requests to /api/v1/wallets
if an authorization key has been added with wallet create/modify permissions.
Your app can choose not to register any authorization keys, in which case Privy will not require an authorization signature. However, authorization signatures are an important security measure and we strongly recommend having authorization keys for production apps.
Creating keys
You can create new authorization keys for your app in the Authorization keys page of the Privy Dashboard. When creating a new key, Privy will generate a P-256 keypair for your app directly on your device, and show you the private key.
- The private key is generated on your device, and is only ever known to your app. Neither Privy nor the TEE ever sees the P-256 private key, and cannot sign payloads with it.
- The public key is registered with the secure enclave that secures your wallets, and is used to verify signatures produced by your servers.
Generating signatures
TIP
If you're using Privy's Server SDK to use server wallets, simply pass your authorization key to the Privy client's constructor and the SDK will automatically include authorization signatures when required.
Generating an authorization signature for the request involves three steps:
- Generate a JSON payload containing the following fields. All fields are required unless otherwise specified.
Field | Type | Description |
---|---|---|
version | 1 | Authorization signature version. Currently, 1 is the only version. |
method | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | HTTP method for the request. Signatures are not required on 'GET' requests. |
url | string | The full URL for the request. Should not include a trailing slash. |
body | JSON | JSON body for the request. |
headers | JSON | JSON object containing any Privy-specific headers, e.g. those that are prefixed with 'privy-' . This should not include any other headers, such as authentication headers, content-type , or trace headers. |
headers['privy-app-id'] | string | Privy app ID header (required). |
headers['privy-idempotency-key'] | string | Privy idempotency key header (optional). If the request does not contain an idempotency key, leave this field out of the payload. |
- Canonicalize the payload per RFC8785 and serialize it to a string. This GitHub repository links to various libraries for JSON canonicalization in different languages.
- Sign the serialized JSON with ECDSA P-256 using your app's private key and serialize it to a base64-encoded string.
See the code snippets below to serialize and sign requests with your app's private key.
ts
import canonicalize from 'canonicalize'; // Support JSON canonicalization
import crypto from 'crypto'; // Support P-256 signing
// Replace this with your private key from the Dashboard
const PRIVY_AUTHORIZATION_KEY = 'wallet-auth:insert-your-private-key-here';
...
function getAuthorizationSignature({url, body}: {url: string; body: object}) {
const payload = {
version: 1,
method: 'POST',
url,
body,
headers: {
'privy-app-id': 'insert-your-app-id'
// If your request includes an idempotency key, include that header here as well
}
};
// JSON-canonicalize the payload and convert it to a buffer
const serializedPayload = canonicalize(payload) as string;
const serializedPayloadBuffer = Buffer.from(serializedPayload);
// Replace this with your app's authorization key. We remove the 'wallet-auth:' prefix
// from the key before using it to sign requests
const privateKeyAsString = PRIVY_AUTHORIZATION_KEY.replace('wallet-auth:', '');
// Convert your private key to PEM format, and instantiate a node crypto KeyObject for it
const privateKeyAsPem = `-----BEGIN PRIVATE KEY-----\n${privateKeyAsString}\n-----END PRIVATE KEY-----`;
const privateKey = crypto.createPrivateKey({
key: privateKeyAsPem,
format: 'pem',
});
// Sign the payload buffer with your private key and serialize the signature to a base64 string
const signatureBuffer = crypto.sign('sha256', serializedPayloadBuffer, privateKey);
const signature = signatureBuffer.toString('base64');
return signature;
}
const authorizationSignature = getAuthorizationSignature({
// Replace with your desired path, e.g. '/v1/wallets/<wallet_id>/rpc'
url: 'https://api.privy.io/v1/wallets',
// Replace with your desired body
body: {
chain_type: 'ethereum',
},
});
Setting headers
Once you have signed your request with your authorization keypair, include the produced signature
as the 'privy-authorization-signature'
header for your request.