twendy1
twendy1•3w ago

Crypto createHmac support

Hi, does Convex env supports createHmac? Im trying to use it in http action in order to verify webhook but it seems like there is no support.
11 Replies
Convex Bot
Convex Bot•3w ago
Thanks for posting in <#1088161997662724167>. Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets. - Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.) - Use search.convex.dev to search Docs, Stack, and Discord all at once. - Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI. - Avoid tagging staff unless specifically instructed. Thank you!
twendy1
twendy1OP•3w ago
If it only supported inside NodeJS action, does it mean I need to create the whole separate action just for signature verification?
erquhart
erquhart•3w ago
Yes, I do this for verifying certain webhooks. Keep in mind the Convex runtime being lean is super beneficial, so sometimes you need to make a node action to do something that requires it. Convex runtime also keeps getting more things, so this could eventually be unecessary for createHmac (probably if subtle crypto gets an implementation)
twendy1
twendy1OP•3w ago
Hey! I see. Just seems pretty sad to start the whole node action just for 1-3 functions. But yeah, it's okay, already implemented the logic) Thank you for the response!
sshader
sshader•3w ago
Can you share the code you're using that's not working in HTTP actions? Nearly all of the crypto methods are available in the Convex runtime, so curious what's missing here
twendy1
twendy1OP•3w ago
@sshader Hey, sure
generateSignature(payload: string, secret: string): string {
const computedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return computedSignature;
}
generateSignature(payload: string, secret: string): string {
const computedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return computedSignature;
}
createHmac doesn't exist, so I cant verify webhooks.
sshader
sshader•3w ago
Ah yeah the crypto that exists in Node and the one that exists in the Convex runtime (and other environment, like browsers) have different APIs, but you can usually do a lot of the same stuff. I asked Claude to write a version of this using the web crypto and crypto.subtle and here's what it came up with:
/**
* Generates an HMAC SHA-256 signature using Web Crypto API
* @param {string} payload - The data to sign
* @param {string} secret - The secret key used for signing
* @returns {Promise<string>} - Promise resolving to hex-encoded signature
*/
async function generateSignature(payload, secret) {
// Convert the string inputs to ArrayBuffers
const encoder = new TextEncoder();
const payloadBuffer = encoder.encode(payload);
const secretBuffer = encoder.encode(secret);

// Import the secret key
const key = await crypto.subtle.importKey(
'raw',
secretBuffer,
{
name: 'HMAC',
hash: { name: 'SHA-256' }
},
false, // not extractable
['sign'] // can only use it for signing
);

// Create the signature
const signatureBuffer = await crypto.subtle.sign(
'HMAC',
key,
payloadBuffer
);

// Convert the signature to hex
return arrayBufferToHex(signatureBuffer);
}

/**
* Converts an ArrayBuffer to a hex string
* @param {ArrayBuffer} buffer - The buffer to convert
* @returns {string} - Hex-encoded string
*/
function arrayBufferToHex(buffer) {
return Array.from(new Uint8Array(buffer))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
/**
* Generates an HMAC SHA-256 signature using Web Crypto API
* @param {string} payload - The data to sign
* @param {string} secret - The secret key used for signing
* @returns {Promise<string>} - Promise resolving to hex-encoded signature
*/
async function generateSignature(payload, secret) {
// Convert the string inputs to ArrayBuffers
const encoder = new TextEncoder();
const payloadBuffer = encoder.encode(payload);
const secretBuffer = encoder.encode(secret);

// Import the secret key
const key = await crypto.subtle.importKey(
'raw',
secretBuffer,
{
name: 'HMAC',
hash: { name: 'SHA-256' }
},
false, // not extractable
['sign'] // can only use it for signing
);

// Create the signature
const signatureBuffer = await crypto.subtle.sign(
'HMAC',
key,
payloadBuffer
);

// Convert the signature to hex
return arrayBufferToHex(signatureBuffer);
}

/**
* Converts an ArrayBuffer to a hex string
* @param {ArrayBuffer} buffer - The buffer to convert
* @returns {string} - Hex-encoded string
*/
function arrayBufferToHex(buffer) {
return Array.from(new Uint8Array(buffer))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
twendy1
twendy1OP•3w ago
I was also thinking yesterday to rewrite the function on web crypto but wasnt sure if Convex supports all needed pieces
sshader
sshader•3w ago
GitHub
stripe-node/src/crypto/SubtleCryptoProvider.ts at master · stripe/...
Node.js library for the Stripe API. . Contribute to stripe/stripe-node development by creating an account on GitHub.
GitHub
stripe-node/src/crypto/NodeCryptoProvider.ts at master · stripe/st...
Node.js library for the Stripe API. . Contribute to stripe/stripe-node development by creating an account on GitHub.
twendy1
twendy1OP•3w ago
Thank you for code example, gonna rewrite and remove nodejs action! 🙂
sshader
sshader•3w ago
I'm fairly sure the one stripe uses works, but let me know if it doesn't

Did you find this page helpful?