Verify Stripe web-hook manually - javascript

I am trying to manually verify web-hook:
const stripeSecret = createHmac('sha256', STRIPE_SIGNING_SECRET)
.update(event.body)
.digest('hex');
if(stripeSecret !== keyFromHeader) {
throw err();
}
But it is not matched with Stripe secret key which is received in header.
Here is event data which I am also trying to use in Stripe API (it also fails):

it's not event.body you should hash According to the documentation (https://stripe.com/docs/webhooks/signatures#verify-manually
) its a concatenation of :
The timestamp (as a string)
The character .
The actual JSON payload (i.e., the request body) => JSON.stringify(req.body)
you will need to parse this to get the timestamp (the xxxxx in the "t=xxxxx" part)
const sig = request.headers['stripe-signature'];
if you give me a sample stripe signature header i can try a code sample.

event.body might not be enough — it's very common in Node server environments for that to be a "parsed" version of the incoming request body, and instead you need to make sure to access the actual raw JSON string in the body — that's what Stripe's signature is computed against. It can be a little tricky!
https://github.com/stripe/stripe-node/tree/master/examples/webhook-signing
(and many examples contributed for various frameworks at https://github.com/stripe/stripe-node/issues/341 )
Also, is there a specific reason to do this manually and not just use Stripe's Node library? :)

Related

Xero webhooks with Node Red; OK, than not ok

I have been dealing with cryptography craziness since yesterday, I 've literally lost my sleep over this.
I am implementing a node red solution to get webhooks from Xero to be written in a custom app. I have experienced a lot of issues with the payload and how it needs to be stringified and how needs to be hashed, but eventually figured it out thanks to a Github fellow that posted this code to get the body to its 'raw' state
let msgPayloadRaw = JSON.stringify(msg.payload).split(':').join(': ').split(': [').join(':[').split(',"entropy"').join(', "entropy"');
I then create a sha256 base64 hash to check against the header value using the following js code
var cryptojs = context.global.cryptojs;
const webhookKey = 'MyWebhookKeyHere';
let msgPayloadRaw = JSON.stringify(msg.payload).split(':').join(': ').split(': [').join(':[').split(',"entropy"').join(', "entropy"');
let bdata = new Buffer(msgPayloadRaw).toString();
let ciphertext = cryptojs.HmacSHA256(bdata, webhookKey );
let base64encoded = cryptojs.enc.Base64.stringify(ciphertext);
msg.payload = base64encoded;
return msg;
Now everything should work great, but I get a crazy result showcased in this recording, where the web hooks intent status turns to 'OK', and some seconds later returns to this error:
Retry
We haven’t received a successful response on the most recent delivery attempt and will retry sending with decreasing frequency for an overall period of 24 hours.
Response not 200. Learn more
Last sent at 2022-06-22 11:48:28 UTC
What's the problem ?
The problem relies in the http input, where node red parsed the body.
Body needs to be hashed. The body in the http request is like that
{"events":[],"firstEventSequence": 0,"lastEventSequence": 0, "entropy": "IVMMHNWPBAZYRZJRCUAQ"}
Notice the spaces after each :
Node Red converts that body to JSON object. When I do JSON.stringify(msg.payload); I will get the following
{"events":[],"firstEventSequence":0,"lastEventSequence":0, "entropy":"IVMMHNWPBAZYRZJRCUAQ"}
which is obviously the same, but technically it is not (due to spaces) and when hashed it generates a different hash value.
The GitHub fellow did that that walkround
JSON.stringify(msg.payload).split(':').join(': ').split(': [').join(':[').split(',"entropy"').join(', "entropy"');
So in order to solve this, I need to find a way to get the raw http input, instead of the parsed one that node red is providing.
Any ideas how to get the raw input ?
Sort answer: you don't.
The raw body is not available if the Content-Type header is set to application/json the bodyParser will kick in and generate the matching JSON object that is passed as the msg.payload.
the httpNodeMiddleware is attached after the bodyParser so the body has already been changed.

Feathersjs find request "invalid query parameter" error with reactjs

When I add $show:true to the data (shown below), the find request shows an error.
When I remove $show:true, there is no error and working fine.
Error is
Invalid query parameter $show.
I tried using different $values but the server always shows 'invalid parameter $values'
data = {$noshow:true,$show:true} ;
let res = await client.service('servv').find({query:data});
In Feathers, query parameters starting with a $ are treated as special parameters. Often they can be used to make database specific queries (instead of just querying properties) which is why they have to be explicitly allowed on the server in the service configuration (see https://docs.feathersjs.com/api/databases/common.html#options).

I Call SmartContract Token ERC20, Why Show Hashing Output?

I do not know why with this, even though in the previous version (web3 + Metamask) can issue real data. But now used as hashing (output). I took the example in the code and output below (to get the TotalSupply on the ERC20 Token):
Output : 0x18160ddd
const contractInstance = web3.eth.contract(contractAbi).at(contractAddress);
const total_supply = contractInstance.totalSupply.getData();
console.log(total_supply);
How to showing real data? In a sense it doesn't come out hashing. Thanks
.getData() returns the ABI-encoded input you would have to send to the smart contract to invoke that method.
If you want to actually call the smart contract, use .call() instead.

Reply to an email in Gmail with AppScript with changed recipients ends up in a new thread

I have an email in my mailbox and I want the AppScript program to reply to it with just me and a special google group as the recipients. The purpose of this is communication of the program with me as the program replies to the message once it has processed it with necessary details about the processing in the reply body. There might also be other recipients apart from me in the original message and I don't want the program to send the reply to them.
So I need to reply with a changed set of recipients. When I do it in the Gmail GUI it works just fine, I hit reply, change the recipients, send the message and the reply ends up in the original thread. However when I do it in the script the reply always ends up in a new thread. Originally I thought Gmail decides based on the subject of the email but it seems there's more to it (perhaps it has recently changed as I think it used to work that way).
I tried multitude of slightly different approached, one of them being:
var messageBody = "foo";
var newRecipients = "me#gmail.com, my-group#gmail.com";
var messageToReplyTo = ...;
var advancedParams = {from : "my-alias#gmail.com"};
var replyDraft = messageToReplyTo.createDraftReply(messageBody);
var replySubject = replyDraft.getMessage().getSubject();
var replyBody = replyDraft.getMessage().getBody();
replyDraft.update(newRecipients, replySubject, replyBody, advancedParams);
replyDraft.send();
There are a couple fun things you need to do in order to achieve this, but you can do it without too much trouble. You should definitely review the guide to Drafts.
Per the API spec:
In order to be part of a thread, a message or draft must meet the following criteria:
The requested threadId must be specified on the Message or Draft.Message you supply with your request.
The References and In-Reply-To headers must be set in compliance with the RFC 2822 standard.
The Subject headers must match.
To start, you need to get a reference to the draft you want to update. This is probably simplest by using GmailApp:
const thread = /** get the thread somehow */;
const newBody = /** your plaintext here */;
const reply = thread.createDraftReply(newBody);
The primary issue with Gmail & Drafts is that a Draft is an immutable message to server resources. If you change any of it, you change all of it. Thus, to change a header value such as the recipient address, you need to completely rebuild the message. This is why using the GmailApp methods to update a draft fail to maintain the existing thread information - you can't specify it as one of the advanced options for building the new message. Thus, you must use the Gmail REST API for this task:
const rawMsg = Gmail.Users.Drafts.get("me", reply.getId(), {format: "raw"}).message;
To update a draft, you need to supply an RFC 2822 formatted message encoded in base64. If you are comfortable converting the rich format message parts into such a valid string, by all means work with the non-raw format, as you have direct access to the headers in the message.payload.
To work with the raw message, know that Apps Script casts the described base64 encoded string to a byte array in the above call. The leap is then to treat that byte array as string bytes, specifically, charCodes:
const msg_string = rawMsg.raw.reduce(function (acc, b) { return acc + String.fromCharCode(b); }, "");
console.log({message: "Converted byte[] to str", bytes: rawMsg.raw, str: msg_string});
Once you have the message as a string, you can use regular expressions to update your desired headers:
const pattern = /^To: .+$/m;
var new_msg_string = msg_string.replace(pattern, "To: <....>");
// new_msg_string += ....
Since the Gmail API endpoint to update a Draft expects a base64 web-safe encoded string, you can compute that:
const encoded_msg = Utilities.base64EncodeWebSafe(new_msg_string);
And the only remaining bit is to perform the call (and/or send the updated draft).
const resource = {
id: <draft id>, // e.g. reply.getId()
message: {
threadId: <thread id>, // e.g. thread.getId()
raw: encoded_msg
}
}
const resp = Gmail.Users.Drafts.update(resource, "me", reply.getId());
const sent_msg = Gmail.Users.Drafts.send({id: resp.id}, "me");
console.log({message: "Sent the draft", msg: sent_msg});
I don't claim that the handling of the Byte array returned from the Message.raw property is 100% correct, only that it seems correct and didn't result in any errors in the test message I sent. There may also be an easier approach, as the Apps Script service has a Drafts.update endpoint which accepts a Blob input and I have not investigated how one would use that.

Twitter, JavaScript & OAuth: failed to validate oauth signature and token

Im trying to implement OAuth using JavaScript, but when I make my request to http://api.twitter.com/oauth/request_token I am getting the above message in the response ("failed to validate oauth signature and token").
As far as I can tell I'm including all the correct parameters, both in the encoding of the signature base:
basestring: (consumer key removed for security)
POST&http%3A%2F%2Ftwitter.com%2Foauth%2Frequest_token%26oauth_callback
%3Doob%26oauth_consumer_key
%3D11111111111111111111112222222222222%26oauth_nonce
%3DO3cHsSXrfnzT%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp
%3D1275928008%26oauth_version%3D1.0
consumer secret: (removed for security)
11111111111111111111112222222222222&
Signature:
R3eHMuQ04F37+xPJSIsoo0aMzc8
Post Data: (consumer key removed for security)
oauth_callback=oob&oauth_consumer_key=11111111111111111111112222222222222&oauth_signature_method=HMAC-SHA1&oauth_signature=pjDh8jkp89ThBtzzB9dQmxQfcg&oauth_timestamp=1275928413&oauth_nonce=qyq3Jhn8rtTZ&oauth_version=1.0
And I've checked that the clock is correct on my device as that's the only real result I can find for this problem :( The nonce is unique and generated every time it runs...
Unfortunately I don't know where to look now. I can't spot anything obvious. I've re-written the entire request twice - once using the oauth.js library and once completely manually, but in both cases it fails with the same error!
Any suggestions?
Cheers
Perhaps the same as this question - which links to a discussin on twitter: apparently client side javascript with oob is not allowed!?!?
Your signature looks wrong, it should always end with a =. Here is an example of a valid one: "YEBbMFDYmp6DvZ3qW1aCx8q7kTc=". Your base string looks right, so I think you've made a mistake with your signature key.
In C#, here is how I built my signature key,
string signatureKey = Uri.EscapeDataString( consumer_secret ) + "&";
var hmacsha1 = new HMACSHA1( new ASCIIEncoding().GetBytes(signatureKey));
string signatureString = Convert.ToBase64String(
hmacsha1.ComputeHash(
new ASCIIEncoding().GetBytes( signatureBaseString ) ) );
string oauth_signature = signatureString;
More info on this process: https://www.dinochiesa.net/?p=17

Categories