'Making A Cloudflare Worker That Tweets Though My Twitter Developer APIs. Can't Get CryptoJS.HmacSHA1 to Create Signature for Fetch Request

I am making a Cloudflare Worker that Tweets from my Twitter Developer APIs whenever I make a blank GET request to the worker. At first you might think "that's easy just use npm's twitter-api-v2," but that won't work because Cloudflare workers need to be in pure javascript/typescript and can't rely on any external modules. So I attempted to do this with the following code.

index.ts

import { handleRequest } from './handler'

addEventListener('fetch', (event) => {
  event.respondWith(handleRequest(event.request))
})

handler.ts

import * as CryptoJS from 'crypto-js'

function getRandomString(length) {
    const randomChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    let result = '';
    for ( let i = 0; i < length; i++ ) {
        result += randomChars.charAt(Math.floor(Math.random() * randomChars.length));
    }
    return result;
}
function hexToBase64(str) {
  const stringChange = str.toString()
  return btoa(String.fromCharCode.apply(null, stringChange.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" ")));
}
async function postTweet() {

  const oauth_consumer_key = '<OAUTH CONSUMER KEY HERE>'
  const oauth_consumer_secret = '<OAUTH CONSUMER SECRET HERE>'
  const oauth_token = '<OAUTH TOKEN HERE>'
  const oauth_token_secret = '<OAUTH TOKEN SECRET HERE>'
  const oauth_signature_method = 'HMAC-SHA1'
  const oauth_version = '1.0'
  const oauth_nonce = getRandomString(42)
  const oauth_timestamp = Math.round(Date.now() / 1000)

  const endpointURL = 'https://api.twitter.com/1.1/statuses/update.json?status';
  
  const tweetData = {
    status: 'I am Tweeting this now'
  }
  
  const encodedTweet = encodeURIComponent(tweetData.status).replace(/!/g, '%21')

  const params = 'POST&' + encodeURIComponent('https://api.twitter.com/1.1/statuses/update.json') + '&include_entities' + encodeURIComponent('=true&') + 'oauth_consumer_key' + encodeURIComponent('='+oauth_consumer_key+'&') + 'oauth_nonce' + encodeURIComponent('='+oauth_nonce+'&') + 'oauth_signature_method' + encodeURIComponent('='+oauth_signature_method+'&') + 'oauth_timestamp' + encodeURIComponent('='+oauth_timestamp+'&') + 'oauth_token' + encodeURIComponent('='+oauth_token+'&') + 'oauth_version' + encodeURIComponent('='+oauth_version+'&') + 'status' + encodeURIComponent('='+encodedTweet)
  const signingKey = encodeURIComponent(oauth_consumer_secret) + '&' + encodeURIComponent(oauth_token_secret)
  //////HMAC-SHA1 Functionality//////
  const hexStr = CryptoJS.HmacSHA1(params, signingKey)
  console.log(hexStr)
  const signature = hexToBase64(hexStr)
  const oauth_signature = encodeURIComponent(signature)
  fetch('https://api.twitter.com/1.1/statuses/update.json', {
    method: 'post',
    headers: {
      'Authorization': 'OAuth oauth_consumer_key="'+oauth_consumer_key+'",oauth_token="'+oauth_token+'",oauth_signature_method="HMAC-SHA1",oauth_timestamp="'+oauth_timestamp+'",oauth_nonce="'+oauth_nonce+'",oauth_version="1.0",oauth_signature="'+oauth_signature+'"',
      'Content-Type': 'application/x-www-form-urlencoded' // 'application/json'
    },
    body: JSON.stringify(tweetData)
  }).then(function(response) {
    return response.json();
  }).then(function(data) {
    console.log('result:', data);
  });
  console.log('postTweet ran')
}

export async function handleRequest(request: Request): Promise<Response> {
  await postTweet()
  return new Response(`Hello worker! this is a ${request.method} request`, {
    headers: { 'content-type': 'text/plain' },
  });
}

But when I run the code with wrangler dev and then do a blank GET request to http://127.0.0.1:8787 with Postman, I get this in my terminal:

<myusername>@<mycomputername> <myappname> % wrangler dev
👂  Listening on http://127.0.0.1:8787
🌀  Detected changes...
💁  Ignoring stale first change
[2022-04-24 15:42:37] GET <myappname>.<myworkername>.workers.dev/ HTTP/1.1 200 OK
{unknown object}
postTweet ran
^C
<myusername>@<mycomputername> <myappname> %

I noticed that the problem probably starts with the fact that const hexStr = CryptoJS.HmacSHA1(params, signingKey) is failing. You can see in the output that console.log(hexStr) is printing {unknown object}

What am I doing wrong? And how can I get my Cloudflare worker to Tweet upon request?



Solution 1:[1]

that won't work because Cloudflare workers need to be in pure javascript/typescript and can't rely on any external modules

That's not the case, Node.js support was announced recently: https://blog.cloudflare.com/node-js-support-cloudflare-workers/

There's also a list of libraries (from npm) that work with Workers: https://workers.cloudflare.com/works

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 RozenMD