Using Secrets in Cloudflare Workers with GitHub Actions
Tags: webdev, tutorial, beginners, devops
Published: 2022-05-10 15:32:28

In my last post I wrote about building a web worker to hit the [dev.to API](https://developers.forem.com/api) and render the list of posts that I've written:

{% embed https://dev.to/bryce/web-workers-are-the-future-3nkg %}

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](https://developer.mozilla.org/en-US/docs/Web/HTTP/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](https://dev.to/api/articles) 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`](https://esbuild.github.io/api/#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`](https://nodejs.org/dist/latest-v8.x/docs/api/process.html#process_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:

```js
const { build } = require('esbuild')

build({
  ...
  define: {
    'process.env.COOKIE': "my-cookie-value",
  },
  ...
})
```

Then using the value in `getPosts`:

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

const response = await fetch(url, { headers })
```

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](https://github.com/zricethezav/gitleaks).

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"
```

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

```js
define: {
  'process.env.COOKIE': JSON.stringify(process.env.COOKIE),
},
```

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](https://github.com/cloudflare/wrangler-action). 

Secrets are how GitHub Actions handle environment variables; in workflow logs the value is [masked](https://docs.github.com/en/actions/security-guides/encrypted-secrets). First, we need to add this secret to the action. To do this, navigate to your repository Settings:

![Settings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i5w3483znw6bjhgv3zem.png)

Then under Security click Secrets > Actions:

![Secrets Actions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ilfou1aoz9q9itewzhmz.png)

And finally New repository secret:

![New repository secret](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/swttak00ijuovx3jnt45.png)

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

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/08e0y2ko48ost617nrjk.png) 

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

```yml
- name: Publish
  uses: cloudflare/wrangler-action@1.3.0
  with:
    apiToken: ${{ secrets.CF_API_TOKEN }}
    secrets: COOKIE
  env:
    COOKIE: ${{ secrets.COOKIE }}
```

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](https://dev.to/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.