GCP Credentials & Next.js
Tags: tutorial, gcp, nextjs, javascript
Published: 2020-04-14 18:58:47

Let's begin with the problem. You're running a Next.js app on ZEIT Now, you've created some API functions and now you want to make an authenticated call to some GCP service using their client libraries (Firebase, GCS, Big Query, etc). For apps not running on GCP, you must provide your own set of credentials to make authenticated requests to your GCP services, however, we don't want to just go and store those credentials in our repo as plaintext (🚨 danger️️ous 🚨)! We *should* use some secure data-store for this. Luckily, if you're deploying on Now, it has support for [secrets](https://zeit.co/docs/v2/serverless-functions/env-and-secrets#adding-secrets), but they only allow the secret value to be a string.

    $ now secrets add <secret-name> <secret-value>

Wouldn't it be nice if we could store our service account JSON  in that secret? Turns out we can with the power of `base64` encoding. With a few commands we can turn our JSON key into a secret that can be consumed in our API functions. 

### Here are the steps we'll need to do

1. Create a GCP service account with the appropriate permissions.
2. Download a JSON credential for that service account.
3. Convert that service account into a `base64` encoded string and save it as a Now Secret.
4. Configure the build processes (remote and local) of Now to access this secret and store it as an environment variable.
5. Configure Next.js to expose this environment variable to the application.
6. Read the environment variable in our API function, `base64` decode it and create a Google Credential from that key.
7. Make authenticated requests to your GCP services like Firebase, GCS, BigQuery, etc. 

### Creating and managing the Service account

First you'll need to create a service account in GCP with the appropriate permissions granted to it, Google has a [simple guide](https://cloud.google.com/iam/docs/creating-managing-service-accounts) explaining how to do this. Next, [download a JSON key](https://cloud.google.com/iam/docs/creating-managing-service-account-keys) of this account. Once you have your JSON key, you can rename it if you'd like to match the following shell command. Now that we have our JSON key, we can create a Now secret using the data in it.

    $ now secret add <secret-name> $(cat service-account.json | base64)

### ZEIT Now configuration

Now that your service account is encoded via `base64` and stored in Now. We need to set up a few more things in the build process for that secret to be accessible by your API function. We have two cases we need to cover, one, your local development build will need to read that secret and two, the remote deployment of Next.js on Now. Using [Now build configuration](https://zeit.co/docs/configuration#project/build-env) we will tell the Now deployment to mount that secret into our Next app configuration so we can access the secret as an environment variable. 

For the local case, we will create a new file called `.env.build` at your project root. You will need to copy the base64 encoded secret into this file. Be sure to add this file to your `.gitignore` or else your secret may become public!

    $ echo GOOGLE_APPLICATION_CREDENTIALS=$(cat service-account.json | base64) >> .env.build
    $ echo .env.build >> .gitignore

Now instead of starting up your service with `npm run dev` or `yarn dev` you will need to start using `now dev` check this [blog post](https://zeit.co/blog/now-dev) for more info.

For the remote case, you will need to create a file in the root called `now.json` and populate it as follows.

    {
      "build": {
        "env": {
          "GOOGLE_APPLICATION_CREDENTIALS": "@secret-name"
        }
      }
    }
    

Be sure to note the "@" symbol, this tells Now to use a secret of this name instead of the raw string.
### Next.js Configuration

Next up we want to configure Next to expose this environment variable to the application. To do so, modify your `next.config.js`. If you don't already have one, create an empty file at the root again and name it `next.config.js`. Add the following to that file. Check out the [Next docs](https://nextjs.org/docs/api-reference/next.config.js/introduction) for more info on using a custom `next.config.js`.

    module.exports = {
      env: {
        GOOGLE_APPLICATION_CREDENTIALS: process.env.GOOGLE_APPLICATION_CREDENTIALS,
      },
    };
    

### Accessing the Service Account in the API Function

We have one final step before we can make authenticated calls to GCP. That is reading the environment variable where our secret is stored, and turning it back (`base64` decode) into a credential that can be consumed by the various GCP SDKs. Once we do this, we can make all the authenticated requests we like!

    const credential = JSON.parse(
        Buffer.from(process.env.GOOGLE_APPLICATION_CREDENTIALS, 'base64').toString()
    );
    
    // Authenticate with the GCS SDK
    import { Storage } from '@google-cloud/storage';
    
    const storage = new Storage({
        projectId: '<gcp-project-id>',
        credentials: credential,
    });
    
    // Authenticate with the Firebase Admin SDK
    import * as admin from 'firebase-admin';
    
    admin.initializeApp({
        ...appOptions,
        credential: admin.credential.cert(credential),
    });
    

That just about sums it up. Hope this helps you on your Next project!

If you have any questions, hit me up on [Twitter](https://twitter.com/parondeau_).

---

This article was originally published on [parondeau.com](https://parondeau.com/blog/gcp-credentials-nextjs) on 2020-04-03.