Keep API secrets secret with Gitlab CI and Webpack

Tim Barclay
3 min readApr 24, 2018

--

If your app communicates with any 3rd party APIs, it’s likely to need to use some pieces of information to identify itself, such as IDs, API keys or client secrets. Your app needs them to run properly, but you definitely don’t want to check them into your repo — particularly if your repo is public.

So what can you do if you have a serverless, client-only app and your public git repo, your CI server and your host are all the same thing, as they are if you use Gitlab Pages? To get around the issue, here’s a way to set up Gitlab and webpack so it uses variables from a local file during development but Gitlab Secret Variables during a CI build.

A local config file

Add a file called config.js and add the secrets to that:

module.exports = {
CLIENT_ID: "<YOUR_CLIENT_ID>",
CLIENT_SECRET: "<YOUR_CLIENT_SECRET"
}

Add that to your .gitignore file so you don’t check it in. Note, you’ll need to let any collaborators know that they’ll need to recreate the file locally too.

Gitlab secrets

Now you need to add the same values to your project’s Secret Variables on Gitlab.

Click Settings in the left menu and choose CI / CD.

From CI / CD settings, expand Secret Variables and add the values from your config.js file. This will make those variables available during builds in Gitlab CI. Read more about Secret Variables here.

Configure Gitlab CI

If you haven’t already set this up, you’ll need to add a .gitlab-ci.yml file to your repo so that Gitlab CI builds and deploys your app. Read more about this here if you haven’t done it before, but it’ll probably look a bit like:

image: node:9.10cache:
paths:
- node_modules/
pages:
script:
- npm install
- npm run prod
- mv dist public
artifacts:
paths:
- public

only:
- master

Setup webpack

Now we’re ready to configure webpack to get its secrets from the correct place depending on where it’s running. To do that, it’s useful to know that Gitlab sets a number of environment variables (full list here) on its build environment so we can use the presence of those to work out what’s going on.

In webpack.config.js:

// Indicates we're building in the CI environment
const isCiBuild = !!process.env.CI;
let clientId;
let clientSecret;
if(isCiBuild) {
clientId = process.env.CLIENT_ID;
clientSecret = process.env.CLIENT_SECRET;
} else {
const config = require("./config");
clientId = config.clientId;
clientSecret = config.clientSecret;
}
// webpack config stuff...
plugins: [
// other plugins...
new webpack.DefinePlugin({
CLIENT_ID: JSON.stringify(clientId),
CLIENT_SECRET: JSON.stringify(clientSecret)
})
]

Now everywhere in your app, you will have access to the variables CLIENT_ID and CLIENT_SECRET and you’ll be able to pass them into whatever API setup calls they’re needed for.

--

--