go back

Placeholder for post cover image
Cover image for post

Using Secrets in Cloudflare Workers with GitHub Actions

May 10, 2022

In my last post I wrote about building a web worker to hit the dev.to API and render the list of posts that I've written:

After publishing the article though, I noticed something: the /articles API response didn't update & show my new post in the list! The data when directly hitting the endpoint in my browser was correct, but via fetch it was outdated. This meant the response for the latter was being cached somewhere or was hitting a slower follower database. 🙁

The main difference between fetch and my browser were request headers, specifically that my browser had a cookie header with authentication information. I needed a way for the getPosts function to send this cookie header so when I make a new post it shows up in my list!

Get cookie header value 🍪

This step was the easiest: just hit any API endpoint and open developer tools. In Chrome you can do this by hitting ⌘+⌥+i (command+option+i). Then, open the Network tab and in the list of requests on the left select the articles request (or whatever API endpoint you chose). If you don't see any requests just reload the page and it should appear.

It should display the Headers tab by default, in which you can scroll down and see the list of Request Headers tied to the request. We want the cookie value, so copy its full value and keep it somewhere for later!

Note: looking at the set-cookie in login callback, the cookie expires after 2 weeks so this may only work until then (and the value will need to be refreshed).

Add a global value with esbuild 🧩

Thankfully esbuild supports a define feature that can replace a global identifier with a constant value. This means setting a value (cookie) on process.env and then use it in another function (fetch)! process.env is Node's global environment object, so setting the value here will allow other files to natively reference it. This is what my build script ended up looking like:

const { build } = require('esbuild')

build({
  ...
  define: {
    'process.env.COOKIE': "my-cookie-value",
  },
  ...
})
Enter fullscreen mode Exit fullscreen mode

Then using the value in getPosts:

const headers = {
  cookie: process.env.COOKIE
}

const response = await fetch(url, { headers })
Enter fullscreen mode Exit fullscreen mode

Ta-da! 🙌 If you replace "my-cookie-value" with the value from the first step you should see up-to-date API data.

Make things secret 🤫

This cookie value shouldn't be seen by others! It's tied to your account and should be handled carefully. Values like this leaking to the wild are the cause of countless security breaches that still occur regularly, and there are entire projects dedicated to finding them.

So instead of storing the value directly in your build script, locally you can make an .env file and put it there:

COOKIE="my-cookie-value"
Enter fullscreen mode Exit fullscreen mode

Then, update the build script to import from process.env:

define: {
  'process.env.COOKIE': JSON.stringify(process.env.COOKIE),
},
Enter fullscreen mode Exit fullscreen mode

Et voila, it's now hidden from prying eyes! Just don't commit .env to git 😉 it can be stored secretly elsewhere for CI.

Integrate with GitHub Actions ⚙️

Only two steps left! This assumes you already have a deploy script that uses wrangler-action.

Secrets are how GitHub Actions handle environment variables; in workflow logs the value is masked. First, we need to add this secret to the action. To do this, navigate to your repository Settings:

Settings

Then under Security click Secrets > Actions:

Secrets Actions

And finally New repository secret:

New repository secret

For name type COOKIE and for value use your header value from earlier. Then click "Add secret":

Image description

Then, it's just a matter of referencing this secret in the deploy action and adding it to the env:

- name: Publish
  uses: cloudflare/wrangler-action@1.3.0
  with:
    apiToken: ${{ secrets.CF_API_TOKEN }}
    secrets: COOKIE
  env:
    COOKIE: ${{ secrets.COOKIE }}
Enter fullscreen mode Exit fullscreen mode

This will add it to the env, which will be picked up by esbuild, added to proces.env then used in the fetch!


And to dev.to admins: as far as I can tell this doesn't go against any terms, but let me know otherwise. The response is also edge cached so it shouldn't be repeating requests.

Hopefully this helps! It's super satisfying to be able to handle build/deploys completely via CI.

go back