Sry delete me plz

Hey guys!

I’m trying to write a facebook data deletion callback and I have this problem of generating hmacs using the crypto module… I know already about the limitations but facebook is using base64 and a weird pattern I just can’t figure out, could use some help…

Trying to convert this:
const expectedSig = crypto.createHmac('sha256', config.facebookAppSecret).update(payload).digest('base64').replace(/\+/g, '-').replace(/\//g, '_').replace('=', '');

To this:
const expectedSig = await crypto.subtle.importKey('raw', config.facebookAppSecret, { name: 'HMAC', hash: 'SHA-256' }, false, ['verify']).digest('base64').replace(/\+/g, '-').replace(/\//g, '_').replace('=', '');
But I’m not even close to getting it to work :confused:

I also tried this:

const verified = await crypto.subtle.verify(

and I also have this:
const encoder = new TextEncoder();

Facebook gave it for me in PHP so I’ve been converting it to js for quite a while:

header('Content-Type: application/json');

$signed_request = $_POST['signed_request'];
$data = parse_signed_request($signed_request);
$user_id = $data['user_id'];

// Start data deletion

$status_url = 'https://www.<your_website>.com/deletion?id=abc123'; // URL to track the deletion
$confirmation_code = 'abc123'; // unique code for the deletion request

$data = array(
  'url' => $status_url,
  'confirmation_code' => $confirmation_code
echo json_encode($data);

function parse_signed_request($signed_request) {
  list($encoded_sig, $payload) = explode('.', $signed_request, 2);

  $secret = "appsecret"; // Use your app secret here

  // decode the data
  $sig = base64_url_decode($encoded_sig);
  $data = json_decode(base64_url_decode($payload), true);

  // confirm the signature
  $expected_sig = hash_hmac('sha256', $payload, $secret, $raw = true);
  if ($sig !== $expected_sig) {
    error_log('Bad Signed JSON signature!');
    return null;

  return $data;

function base64_url_decode($input) {
  return base64_decode(strtr($input, '-_', '+/'));

I ended up with this:

// NodeJS:
function parse_signed_request(signed_request, secret) {
    encoded_data = signed_request.split('.',2);
    // decode the data
    sig = encoded_data[0];
    json = base64url.decode(encoded_data[1]);
    data = JSON.parse(json); // ERROR Occurs Here!

    // check algorithm - not relevant to error
    if (!data.algorithm || data.algorithm.toUpperCase() != 'HMAC-SHA256') {
        console.error('Unknown algorithm. Expected HMAC-SHA256');
        return null;

    // check sig - not relevant to error
    expected_sig = crypto.createHmac('sha256',secret).update(encoded_data[1]).digest('base64').replace(/\+/g,'-').replace(/\//g,'_').replace('=','');
    if (sig !== expected_sig) {
        console.error('Bad signed JSON Signature!');
        return null;

    return data;

but that didn’t work in Cloudflare so I also tried this:

// No base64
// Fixed an error with character replacement, removed a dependency, included imports, throw errors, and fixed global variables
var crypto = require('crypto');

//remove a dependency on b64url
function atob(str) {
	return new Buffer(str, 'base64').toString('binary');

//this is not used here, but to leave it out would be like passing the salt without the pepper.
function btoa(str) {
	return new Buffer(str, 'utf8').toString('base64');

function parseSignedRequest(signedRequest, secret) {
	var encodedData = signedRequest.split('.');
	// decode the data
	var sig = encodedData[0];
	var json = atob(encodedData[1]);
	var data = JSON.parse(json); // ERROR Occurs Here!

	// check algorithm - not relevant to error
	if (!data.algorithm || data.algorithm.toUpperCase() != 'HMAC-SHA256') {
		throw Error('Unknown algorithm: ' + data.algorithm + '. Expected HMAC-SHA256');

	// check sig - not relevant to error
	var expectedSig = crypto.createHmac('sha256', secret).update(encodedData[1]).digest('base64').replace(/\+/g,'-').replace(/\//g,'_').replace('=','');
	if (sig !== expectedSig) {
		throw Error('Invalid signature: ' + sig + '. Expected ' + expectedSig);

	return data;

but yeah this didn’t work either…

So I’m currently with this:

import config from './config';

const encoder = new TextEncoder();

// SHA-512 Hashing Algorithm
async function hash(inputText, usePrivateSalt = true, algorithm = 'SHA-512') {
	const myText = encoder.encode(usePrivateSalt ? config.privateSalt + inputText + config.privateSalt : inputText);
	const myDigest = await crypto.subtle.digest({ name: algorithm }, myText);
	const myUint8Array = new Uint8Array(myDigest);
	return myUint8Array.toString();

// Decodes a String
function atob(str) {
	return new Buffer.from(str, 'base64').toString('binary');

// Encodes a String
function btoa(str) {
	return new Buffer.from(str, 'utf8').toString('base64');

// Parse Facebook Request
async function parseSignedRequest(signedRequest) {
	const encodedData = signedRequest.split('.');
	const signature = encodedData[0];
	const payload = encodedData[1];
	const data = JSON.parse(atob(payload));
	if (!data.algorithm || data.algorithm.toLowerCase() != 'hmac-sha256') return false; // Unknown algorithm. Expected HMAC-SHA256
	//const expectedSig = crypto.createHmac('sha256', config.facebookAppSecret).update(payload).digest('base64').replace(/\+/g, '-').replace(/\//g, '_').replace('=', '');
	const expectedSig = await crypto.subtle.importKey('raw', config.facebookAppSecret, { name: 'HMAC', hash: 'SHA-256' }, false, ['verify']).digest('base64').replace(/\+/g, '-').replace(/\//g, '_').replace('=', '');
	/*const verified = await crypto.subtle.verify(
	if (signature !== expectedSig) return false; // Bad signed JSON Signature!
	return data;

export default {

console.log('Initialized tools.js');

if anybody knows anything I will be super thankful. thank you for readin.

What actually is the error that you’re receiving?

Have you looked at this?

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.