#imdavidmin-webhook-signatures

1 messages · Page 1 of 1 (latest)

vivid basin
#

hey there - which language do you use on your server?

arctic sable
#

Hi - I'm posting some data from the test webhook

#

language is js, but not node.js because it is run on a serverless function that doesn't support node

#

The header I received for validation is

#

t=1632336614,v1=ee778e5ca8ba6f527cd45c8c70a43db22a3e931d2ae2bc6eb51f0dc677af14ca,v0=6d952d477b272f4a88ec2e251d526df0f762661bbfb2b93f91baec1f9a4ecc57

#

and the body shown on the test webhook window is:

#

so to calculate the HMAC, I'm setting the signed_payload as: 1632336614.{....}

#

The HMAC I got is 4b1abccdf9861231eab66eecf401b250801ea88e24b5f4415cb6928116b7b51c which is not what's specified in v1=

#

The signing key is obtained from here

#

Oh to be sure I've tried both the body there with the line breaks and without in minified form, neither match the signature from the header

vivid basin
#

so most likely the issue is the raw request body here

arctic sable
#

the body is generated by Stripe - it's the one from test your webhook

#

I didn't generate the body

vivid basin
#

you say js but not node on a server, what is that exactly?

arctic sable
#

It's a cloudflare worker

#

It runs on v8 only and has no Node.js core modules

#

So more or less all the js that works in a browser works on this serverless architecture. I can do all the hashing etc required. The problem right now is just that I'm not sure how I'm hashing different from how Stripe does

vivid basin
#

can you share the code that's use to receive the events?

arctic sable
#

So some of the code is done in the Cloudflare Workers runtime API way:

  1. the script listens to the incoming request through the addEventListener fetch event
  2. if the event is using POST and is sending application/json , then the handleRequest method is called
  3. handleRequest reads the JSON payload, then passes this into the validateRequest method (shown in separate screenshot)
#
  1. validateRequest currently only accepts payment_intent.succeeded event types, and will then get the Stripe-Signature header, and finds the t= and v1= values.
#
  1. the t= value is checked against the current time to see if it's too stale
#
  1. signed_payload string is constructed with the t= value + . + stringifyed JSON payload
#
  1. HMAC generated using the secret key
vivid basin
#

THe problem is almost certainly with the .json() -- you must use the raw request body, not the parsed json, to calculate the signature

arctic sable
#

Would the raw body not just be the JSON but with line breaks?

vivid basin
#

no

#

before you invoke request.json() try preserving const rawbody = request.body and passing in rawbody for signature verification

arctic sable
#

so rawbody is going to be a ReadableStream, do i not eventually need to convert that back into a string?

vivid basin
#

yea you can read out the stream once to preserve it as the raw body

#

then separately parse the json if/when needed

#

the issue is that .json() mutates that stream so you have to get the raw body first

arctic sable
#

Ok will try this - will need some time to get back

vivid basin
#

sure thing, give it a whirl

vivid basin
#

any luck with this? i need to step away shortly, though a colleague will be available if you have further issues