I’m trying to validate the requests to my API from the custom cards in hubspot. I have 2 things that I’m supporting: GET data for the cards, and POST to “mark as primary”. Both call the same base API with different endpoints, and as I said, one is GET and one is POST
The Guard i made for this security works perfectly for GET. The signature is validated and lets the request go through fine.
The POST doesn’t work. I can never get the hashes to match.
Here’s my code within my custom Guard:
canActivate(context: ExecutionContext,): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
const signature = request.headers['x-hubspot-signature'];
if (!signature) {
// throw new UnauthorizedException('Hubspot Signature is missing');
this.logger.error('Hubspot Signature is missing');
return false;
}
// this secret looks like: e8bd048a-3ca1...
const clientSecret = process.env.HUBSPOT_CLIENT_SECRET;
const method = request.method;
// TODO: Hard coded protocol. Should use request.protocol, but in local
// testing, the request goes to https, but ngrok
// translates it to http for local test, so it doesn't
// match for the signature check.
const fullUrl = 'https://' + request.get('host') + request.originalUrl;
let builtString = clientSecret + method + fullUrl;
if (request.body && Object.keys(request.body).length > 0) {
builtString += JSON.stringify(request.body);
}
console.log('builtString', builtString);
const sourceString = Buffer.from(builtString.trim(), 'utf-8').toString();
const hash = crypto.createHash('sha256').update(sourceString).digest('hex');
if (hash === signature) {
return true;
}
this.logger.error({
message: 'Invalid Hubspot Signature',
hash,
signature,
});
return false;
}
As the comments say, I am using ngrok for local testing, so i’m hard-coding the https which the original request is sent from. If I hardcode the example source string from the v2 documentation here, it generates a matching hash and validates. And like I said, GET works fine.
My thought is that the order the body is in when hubspot generates the hash is different than the order I’m getting them in, or NestJS is parsing the body into an object and it’s changing the order. I’ve tried disabling the default bodyparser and getting the raw body (like in this comment), but I get the same body/same order that I get without doing that.
So has anybody successfully authenticated a custom card POST request to a NestJS api?