loading...
Cover image for Keeping your keys safe in JAMStack

Keeping your keys safe in JAMStack

mattnield profile image Matt Nield Updated on ・5 min read

Working with Kentico Kontent and Gatsby has been a nice learning curve for me, as I pick up and build upon my front-end developer skillset. You can end up taking quite a lot for granted when you're working with .NET. Securing your calls to an API can be one of these things, as you add things like API keys to your .config files and make sure that you don't push those files into your git repo.

When I started on my journey with Gatsbyjs and Kentico Kontent, I wasn't clear on a method that I could use to hide my API keys. 😕 As with most things though, a little digging on Google goes a long way and I managed to find two solutions:

  1. Using environment variables
  2. Create a settings object

Let's look at these in a little more detail.

Environment Variables

Creating and using environment variables

Environment variables are settings that are usually stored as key-value pairs that you can use in your application. To store your application settings in environment variables, you can create a .env file in your project folder.
The .env file format is just a simple flat file, no hierarchy. As an example, my .env file looks as follows (obviously the values in brackets are replaced):

KONTENT_PROJECT_ID=<Kontent Project ID>
KONTENT_PREVIEW_KEY=<Kontent API Key>
KONTENT_PREVIEW_ENABLED=<true of false>

Reading that file requires you to have the dotenv module in your project. You can install this using the following:

npm install dotenv

The using it is as simple as setting it up (in my case at the top of my gatsby.config file):

require('dotenv').config();

As it happens, in my Gatsby project, I have two .env files, one for running gatsby develop and one for gatsby build (one uses the preview mode of Kentico Kontent while the other does not). In order to do this, I pass a little more info to dotnet to tell the configuration which file to look for, bypassing in the node environment:

require("dotenv").config({
  path: `.env.${process.env.NODE_ENV}`,
})

This means that when I look at my gatsby.config file, I have a much cleaner file that I can commit to my repo that does not contain my various keys as follows:

{
  resolve: `@kentico/gatsby-source-kontent`,
  options: {
    deliveryClientConfig: {
      projectId: process.env.KONTENT_PROJECT_ID,
      previewApiKey: process.env.KONTENT_PREVIEW_KEY,
      globalQueryConfig: {
        usePreviewMode: (process.env.KONTENT_PREVIEW_ENABLED == 'true'),
      },
    },
    languageCodenames: [
      `default`
    ]
  }
}

What you may notice is that I'm not simply using the value from the .env file for the value of usePreviewMode. There is a good reason for this and very simple reason for this and that is that dotenv does not support boolean values. If you want to debug out your environment variables you can use the following:

console.log(process.env);

which means you'll see something like this:

{
  KONTENT_PREVIEW_ENABLED: 'true',
  KONTENT_PREVIEW_KEY: 'Swiper, no swiping!',
  KONTENT_PROJECT_ID: 'Swiper, no swiping!',
  NODE_ENV: 'development',
}

(I actually have a load more in there from my Windows environment variables like PATH, but we don't need to worry about those here)
That's it. When you run npm run build or npm run develop everything should now be picking up your new settings!
It's working!

Ignore the .env file!

A key point here is, add your .env files to the .gitignore file. The whole point here for me is to not commit your keys and other sensitive data to the git repository.

To achieve this, just add the following to your .gitignore file:

# dotenv environment variable files
.env*

Using environment variables with Netlify

I'm my scenario, I'm using Netlify to build and host my solution. If you are too, then you may have already come across the environment variables in your projects build & deploy settings:
Netlify Environment Variables Screenshot

Netlify has no concept of build or develop environment variables in my setup (hot I think it can support them), so when we run npm run build, it simply picks up the available variables and gets on with business.

Using environment variables with Azure DevOps

At Ridgeway, we use Azure DevOps for our build pipelines. Typically, we set up the pipelines using yaml files, but the screenshot here uses the classic designer (it's quite an old one):DevOps  Environment Variables Screenshot

If you're editing a yaml pipeline, then the option is still there if you click Variables in the top right while editing the pipeline.
How to edit variables in Azure DevOps
Then you can just set the values you want. The options here to make things secret are quite a nice touch, as are the tips on how to use them.Setting up variables in DevOps with yaml

Settings object

Creating and using a settings object

Another option I've seen in use is the creation of a settings object in a separate file. So for example, on one project we have a file called gatsby.keys as follows:

module.exports = {
  enablePreviewMode:  false,
  enableSecuredMode:  true,
  securedApiKey:  'Swiper, no swiping!',
  previewApiKey:  'Swiper, no swiping!'
};

This is then used in the gatsby.config file as follows:

const  keys = require('./gatsby-keys');

The variables are then used to set up the plugins as before.

This method supports boolean values, so we don't need to do any additional work with those. Again, this file needs to be excluded from the repository using the .gitignore file to make sure that we don't push the keys into the wrong place.

Settings objects in build pipelines

I've only tried this with Azure DevOps, and it required me to add a custom pipeline component to create the key's file. So I have a step in my yaml that looks like this:

- task: eliostruyf.build-task.custom-build-task.file-creator@5
  displayName: 'Create settings keys'
  inputs:
    fileoverwrite: true
    filepath: 'gatsby-keys.js'
    filecontent: |
      module.exports = {
        enablePreviewMode: true,
        enableSecuredMode: false,
        securedApiKey: 'Swiper, no swiping!',
        previewApiKey: 'Swiper, no swiping!'
      };

You can probably spot the flaw in this implementation, right? I'm not using variables so actually, this is a massive fail as those keys are directly in my yaml file and so also in source control.

Fail!
(on the upside, it's a private repo)

Summary

These are to the two methods that I came across while working on both work and personal projects. The first exposure I had was the settings object. While it solves the issue of booleans, it's note really my favourite. The environment variables appears to be a much more robust approach to things and it's the one I'm going to be using (and asking my team to use) going forward.

If you can find the time, I recommend testing both out and seeing which works best in your case.

Cover photo by Chunlea Ju on Unsplash

Discussion

pic
Editor guide