NetlifyCMS with GitHub OAuth authentication hosted on Vercel

Published
2021-05-23
Tags
Next.jsNetlifyCMSVercelGuideOAuthGitHub

Recently, I was moving www.danielamulle.at to Vercel while transitioning to Next.js from Gatsby. The transition went quite smooth in general, but one thing I didn't expect troubles with was making NetlifyCMS work on Vercel.

NetlifyCMS is a great headless content management system that has good defaults, is flexible enough to fulfill most needs, has a pluggable API, and works well if you host your content on GitHub. I have also contributed to the project when it was at the very early stage. For obvious reasons, Netlify CMS works really well when you host on Netlify but unfortunately things become a bit more complicated if you decide to move to another hosting provider.

This is due to a requirement of GitHub’s authentication flow. To use a GitHub backend with NetlifyCMS, you have to run your own server to handle OAuth or use serverless provider like Vercel.

Netlify substitutes the need for the server and makes it work out-of-the-box but it also might feel like a verndor lock-in. Fortunately, there are open source solutions that implement everything needed but setting everything up can take some time. This is a step-by-step guide of things that worked for me.

Step 1. Configure Netlify CMS

Put you config.yml file to /public so it's accessible by NetlifyCMS when deployed to production. Open the file and additionally to your existing config you need to:

  1. Set repo to your GitHub repo that contains the source code of your site.
  1. Set base_url to the production domain with HTTPS protocol, for example, https://www.danielamulle.at in my case.
  1. Add auth_endpoint: api/auth. This is the URL that NetlifyCMS will open when you click "Authenticate" button. We'll implement this endpoint in our next.js app later.

I've also enabled local_backend: true in the config to make CMS work with the local git without going over the server when in development mode. You can read about making NetlifyCMS to work with a local git repository.

Step 2. Create GitHub App

Go to GitHub and navigate to Settings → Developer settings → GitHub Apps → New GitHub App or use this direct link.

It's not important how you'll name the app since it will not be published. Fill User authorization callback URL with the URL https://<domain>/api/callback. Use exactly the URL that you entered as base_url at the previous step.

Disable Webhook checkbox and set permissions to Read & Write for "Contents":

Hit "Create app" and on the next page you'll see the app details with Client ID and Client secret. We will need those on the next step.

Step 3. Set environment secrets

Add those values to Vercel environment variables:

vercel env add secret OAUTH_CLIENT_ID <client-id-of-GitHub-App>
vercel env add secret OAUTH_CLIENT_SECRET <client-secret-of-GitHub-App>

Step 4. Setup OAuth client in Next.js app

Install https://github.com/bericp1/netlify-cms-oauth-provider-node as a dependency to your project:

npm install netlify-cms-oauth-provider-node

and create a .env file to add following configuration options:

OAUTH_CLIENT_ID=
OAUTH_CLIENT_SECRET=
COMPLETE_URL=https://<domain>/api/callback
ORIGIN=<domain>

where <domain> is your domain without https. It's important to match COMPLETE_URL here and in the GitHub App so make sure they are the same.

Then create 2 files: /src/pages/api/auth.js and /src/pages/api/callback.js

import { createVercelBeginHandler } from "netlify-cms-oauth-provider-node"

module.exports = createVercelBeginHandler({}, { useEnv: true })
import { createVercelCompleteHandler } from "netlify-cms-oauth-provider-node"

module.exports = createVercelCompleteHandler({}, { useEnv: true })

After that, push the code to production branch on Vercel and wait till it's deployed. Navigate to your site and go to the CMS URL. You should be able to authenticate with GitHub now.

Credits

Huge thanks to @bericp1 for https://github.com/bericp1/netlify-cms-oauth-provider-node and @robinpokorny for https://github.com/robinpokorny/netlify-cms-now. Robin's solution didn't work for me but it led me to the right direction.