Skip to content
Swizec Teller - a geek with a

How to set up Gatsby and Auth0 for easy authentication on your next project

In my quest never to set up a server ever again, I discovered Auth0 – a serverless authentication service. You give them keys to the kingdom, and they keep you and your users secure.

You don't even have to store anything. πŸ‘Œ

Since Gatsby is my favorite way to build new React apps with server-side rendering and super-fast performance, I decided to combine the two. Easy in theory, but turns out tricky in practice.

Took me 3 livecoding sessions to figure out. Luke Oliff's article on Gatsby v1 + Auth0 article helped a lot, as did Auth0's React SPA setup guide.

You can think of this article as a distillation of those guides and these 3 hours of coding πŸ‘‡

Step 1: Get an Auth0 account

This one's easy. Go to Auth0 sign up for a free account. They even have a "Just playing around" option, and you're set.

Right now, a free account will hold you over up to 7,000 active users. I don't know about you, but none of my side projects ever hit 7000 DAU (daily active users).

See, it's just me on this one

Auth0 dashboard screenshot

Step 2: Create an app, get domain and client id

Click the big fat New Application button, fill out the form, and get your application's domain and client id. You'll need these to configure your Auth0 client.

The domain is where your login form will live. Your app is going to redirect people there. They'll see a login screen, do the things, and come back to your site with a redirect and a bunch of params.

Client ID tells the Auth0 machinery which site it's working with.

Strangely, you never have to set up a client secret. They generate one for you, but I haven't needed to use it yet. Maybe when you're doing server-to-server authentication?

Step 3: Whitelist your callback URL

Like I said, Auth0 redirects users back to your site after login. You give it a URL, and that's where they end up.

For your safety, callback URLs must be whitelisted. There's an Allowed Callback URLs field in your app settings.

Add http://localhost:8000/auth0_callback. I like to prefix callbacks with a service name because you never know how many you'll need.

Callback config

Step 4: Start a new Gatsby project

This part is easy.

$ gatsby new gatsby-auth0-playground

Creates a new Gatsby site with the basic setup. You're welcome to use a starter. This guide should work with all of them.

Step 5: The auth service

All your Auth0 stuff lives in /utils/auth.js. It's like a library on top of Auth0's library that makes your life a little easier.

You're best off copy-pasting this whole file verbatim into your project. Make sure to configure AUTH0_DOMAIN and AUTH0_CLIENT_ID. If I was a particularly good human, I'd put this on NPM and make it configurable through package.json, but we're not there yet :P

// src/utils/auth.js
import auth0 from 'auth0-js'
import { navigate } from 'gatsby'
const AUTH0_DOMAIN = '<your domain="">'
const AUTH0_CLIENT_ID = '<your client="" id="">'
export default class Auth {
auth0 = new auth0.WebAuth({
domain: AUTH0_DOMAIN,
redirectUri: 'http://localhost:8000/auth0_callback',
audience: `https://${AUTH0_DOMAIN}/api/v2/`,
responseType: 'token id_token',
scope: 'openid profile email',
login = () => {
logout = () => {
handleAuthentication = () => {
if (typeof window !== 'undefined') {
// this must've been the trick
this.auth0.parseHash((err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
} else if (err) {
// Return to the homepage after authentication.
isAuthenticated = () => {
const expiresAt = JSON.parse(localStorage.getItem('expires_at'))
return new Date().getTime() < expiresAt
setSession = authResult => {
const expiresAt = JSON.stringify(
authResult.expiresIn * 1000 + new Date().getTime()
localStorage.setItem('access_token', authResult.accessToken)
localStorage.setItem('id_token', authResult.idToken)
localStorage.setItem('expires_at', expiresAt)
this.auth0.client.userInfo(authResult.accessToken, (err, user) => {
localStorage.setItem('user', JSON.stringify(user))
getUser = () => {
if (localStorage.getItem('user')) {
return JSON.parse(localStorage.getItem('user'))
getUserName = () => {
if (this.getUser()) {
return this.getUser().name

You don't have to understand how all this works, which is the beauty of using a service like Auth0, so here's a nutshell explanation:

  1. You set up an auth0 client with config, put it in this.auth0
  2. When you call auth.login(), the client takes over
  3. Redirects to AUTH0_DOMAIN with some params
  4. Users login through Auth0's beautiful UI
  5. Auth0 redirects back to redirectUri
  6. Your callback page calls auth.handleAuthentication
  7. Your auth0 client parses URL params and decides all's good
  8. setSession takes auth result and updates info in LocalStorage
  9. Gatsby navigates back to your home page with navigate('/')

We also have a couple convenience methods like isAuthenticated, getUser, and getUserName. You might want to make these into proper ES6 getters. I haven't decided yet if that's better or not but it sure looks neater.

Step 6: Gatsby callback page

I mentioned a "callback page". You might think that's complicated, but with Gatsby, it's not. You just need a React component that loads on /auth0_callback and calls handleAuthentication on mount.

Like this πŸ‘‡

// src/pages/auth0_callback.js
import React from "react";
import { ClipLoader } from "react-spinners";
import Auth from "../utils/auth";
import Layout from "../components/layout";
import useComponentDidMount from "../useComponentDidMount";
const Auth0CallbackPage = () => {
useComponentDidMount(() => {
const auth = new Auth();
return (
This is the auth callback page, you should be redirected immediately.
<cliploader sizeunit="px" size={150}></cliploader>
export default Auth0CallbackPage;

I'm using React hooks, but don't let that distract you.

Gatsby turns every file in the /src/pages directory into a static page. So you now have a page that renders some text and a loading spinner from react-spinners.

When the component mounts, that's only in browsers, we create a new Auth() object and call auth.handleAuthentication(). Our auth service takes care of the rest.

PS: the useComponentDidMount hook looks like this:

export default function useComponentDidMount(onMounted) {
const [mounted, setMounted] = useState(false);
useEffect(() => {
}, [mounted]);

It's a combination of useState and useEffect that ensures a function runs once and only once – on component mount. My understanding is that useEffect runs any time React touches our component and I wanted to make certain the auth code runs once.

If you run handleAuthentication multiple times, it leads to strange errors. State gets corrupted, tokens are out of date, everything breaks.

Step 8: A login button

Now all you need is a login button.

// src/components/Login.js
import React from "react";
import { Button } from "reakit";
import Auth from "../utils/Auth";
const auth = new Auth();
const Login = () => {
const { isAuthenticated } = auth;
if (isAuthenticated()) {
return <button onclick={auth.logout}>Logout {auth.getUserName()}</button>;
} else {
return <button onclick={auth.login}>Login</button>;
export default Login;

Since Auth0 handles all our user state, we don't need any sort of props or state management. Just a component that instantiates an Auth0 object and renders a button.

Use the isAuthenticated helper to decide whether to say Login or Logout and call the appropriate methods on click.

That's it.

Did you enjoy this article?

Published on January 23rd, 2019 in Front End, Technical

Learned something new?
Want to become a high value JavaScript expert?

Here's how it works πŸ‘‡

Leave your email and I'll send you an Interactive Modern JavaScript Cheatsheet πŸ“–right away. After that you'll get thoughtfully written emails every week about React, JavaScript, and your career. Lessons learned over my 20 years in the industry working with companies ranging from tiny startups to Fortune5 behemoths.

Start with an interactive cheatsheet πŸ“–

Then get thoughtful letters πŸ’Œ on mindsets, tactics, and technical skills for your career.

"Man, love your simple writing! Yours is the only email I open from marketers and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. πŸ‘Œ"

~ Ashish Kumar

Join over 10,000 engineers just like you already improving their JS careers with my letters, workshops, courses, and talks. ✌️

Have a burning question that you think I can answer?Β I don't have all of the answers, but I have some! Hit me up on twitter or book a 30min ama for in-depth help.

Ready to Stop copy pasting D3 examples and create data visualizations of your own? Β Learn how to build scalable dataviz components your whole team can understand with React for Data Visualization

Curious about Serverless and the modern backend? Check out Serverless Handbook, modern backend for the frontend engineer.

Ready to learn how it all fits together and build a modern webapp from scratch? Learn how to launch a webapp and make your first πŸ’° on the side with ServerlessReact.Dev

Want to brush up on your modern JavaScript syntax?Β Check out my interactive cheatsheet:

By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❀️