TL;DR: In this article, you'll learn how to secure a basic Gatsby static site with Auth0. The finished code for this tutorial is at the gatsby-auth0-sample-app
repository.
Gatsby beautifully merges technologies like GraphQL and MDX with concepts like progressive web apps and server-side rendering. There's even an easy-to-use command line interface (the Gatsby CLI) to make developing, building, and deploying your static site virtually painless. To me, it is the most intuitive JAM stack (JavaScript + APIs + Markup) solution out there.
"@GatsbyJS is a powerful static site generator combining React, GraphQL, and many other modern web technologies."
Tweet This
As wonderful as this is, many static sites still need authentication. Stores, member areas, or admin dashboards can all be built as static sites, and all require either a protected route or a persisted user profile. Luckily, Auth0 is here to help.
This article will take a slightly different path than our typical authentication tutorial. Ordinarily, I would have you build a sample application that includes styling and a source of data. Because Gatsby orchestrates things like GraphQL, CSS-in-JS, API data, and much more, it's incredibly easy to get lost in the weeds in a tutorial and lose track of what is specific to Gatsby.
For that reason, I'm going to have you build the absolute simplest (and, well, ugliest) sample application possible so you can focus solely on learning how to set up authentication in that app. I won't be covering any data sources, GraphQL, or styling strategies. Keeping this tutorial super-simple means that this strategy will work for you whether you're building a blog, a store, or anything else your heart desires.
Prerequisites
To go through this tutorial, you will need Node.js 16 and NPM installed. You should download and install them before continuing.
You'll also need some basic knowledge of React. You only need to know the basic scaffolding of components and JSX to follow along, but it will be difficult if you're a total beginner. If that's you, you can read our article on building and securing your first React app before continuing.
Gatsby Basics
To get started with Gatsby, you'll first need to install the Gatsby CLI globally on your machine. You can do that by running the command:
npm install -g gatsby-cli
The Gatsby CLI has some built-in commands like develop
to run a development server, build
to generate a production build, and serve
to serve the production build. There are many other options and commands that you can check out in the Gatsby CLI docs.
One cool feature of the CLI is the ability to use a starter as the template for a new project. You can use either an official Gatsby starter or any other Gatsby repository. For this tutorial, you'll use gatsby-starter-hello-world
by running the following command:
gatsby new gatsby-auth0 gatsbyjs/gatsby-starter-hello-world
Note that the first argument (gatsby-auth0
) is just the name of the new project. You can call it whatever you'd like.
Gatsby will automatically run npm install
for you, so once that's done, open the project in your preferred editor. You'll see the simplest possible Gatsby project, which includes one file inside of the src/pages/
folder called index.js
. Open that file and replace it with the following:
// ./src/pages/index.js
import React from "react";
import { Link } from "gatsby";
export default () => (
<div>
<p>Hello Gatsby!</p>
<Link to="/account">Go to your account</Link>
</div>
);
You can see that this is a super simple React component written in JSX. It imports Gatsby's Link
function to navigate to an account
route. You'll create that route in the next section. For now, run the command gatsby develop
in your terminal and navigate to localhost:8000
. You should see "Hello Gatsby!" with a link to go to the account page.
Create an Account Route
You've now created your first static site with Gatsby — congratulations! You're ready to create an account
route that you'll protect with Auth0 in just a bit.
To start, create a new file called account.js
in the src/pages/
folder and paste in the following code:
// src/pages/account.js
import React from "react";
const Account = () => (
<div>
<p>This is going to be a protected route.</p>
</div>
);
export default Account;
The above code is another super simple React component. Gatsby will automatically turn this file (and any file in this folder that exports a React component) into a route. Start the development server again with gatsby develop
if it's not already running. You should see the following if you navigate to localhost:8000/account
:
Add Auth0 to Your Gatsby Site
You're now ready to protect the entire account
route with Auth0. Auth0 handles identity features like user registration, email confirmation, and password reset, so you don't have to build them yourself.
Sign up for Auth0
If you don't have one yet, you will have to create a free Auth0 account now. After creating your account, go to the Applications section of your Auth0 dashboard and click on the Create Application button. Then, fill the form as follows:
- Application Name: "Gatsby App"
- Application Type: "Single Page Web Applications"
When you click on the Create button, Auth0 will redirect you to your new application's Quick Start tab. From there, head to the Settings tab and make two changes:
- Add
http://localhost:8000
to the Allowed Callback URLs field. - Add
http://localhost:8000
to Allowed Logout URLs and Allowed Web Origins.
For security reasons, after the login and logout processes, Auth0 will only redirect users to the URLs you register in these fields.
After updating the configuration, scroll to the bottom of the page, and click Save Changes. For now, leave this page open.
Set up Gatsby with Auth0
To get started with adding Auth0 to your Gatsby app, you'll need to install Auth0's SDK for React Single Page Applications in your app. Back in the terminal, stop the development server (Ctrl
+ C
), and issue the following command:
npm install @auth0/auth0-react
Note: This tutorial follows the Authorization Code Flow with Proof Key for Code Exchange. The Auth0 React SDK uses this under the hood. You can read more about these changes in this article by Auth0 Principal Architect Vittorio Bertocci.
Add Gatsby Auth env config
While you could manually update the variables AUTH0_DOMAIN
, AUTH0_CLIENTID
, and AUTH0_CALLBACK
, it's a much better practice to create a separate environment file that's not checked in to source control.
Luckily, Gatsby ships with the library dotenv
, so you can provide the values to the authentication service by creating a file at the root of the project called .env.development
. Paste in the following:
# ./.env.development
# Get these values at https://manage.auth0.com
AUTH0_DOMAIN=<value>
AUTH0_CLIENTID=<value>
AUTH0_CALLBACK=http://localhost:8000/callback
Replace the placeholders with your Auth0 domain (e.g. yourtenant.auth0.com
) and client ID. You can find both in your application's settings page.
You can do the same thing with production values by creating .env.production
. Don't forget to change AUTH0_CALLBACK
to whatever your production server is and add it to your application settings in Auth0.
Add Login to a Gatsby App with Auth0
Auth0 is now ready to use. The first step to using Auth0 is to wrap your root element in the Auth0Provider
component. Create a gatsby-browser.js
file at the root of your project. This file is the behind-the-scenes root component for Gatsby.
Paste in the following code:
//gatsby-browser.js
import React from 'react';
import { Auth0Provider } from '@auth0/auth0-react';
import { navigate } from 'gatsby';
const onRedirectCallback = (appState) => {
// Use Gatsby's navigate method to replace the url
navigate(appState?.returnTo || '/', { replace: true });
};
export const wrapRootElement = ({ element }) => {
return (
<Auth0Provider
domain={process.env.AUTH0_DOMAIN}
clientId={process.env.AUTH0_CLIENTID}
redirectUri={window.location.origin}
onRedirectCallback={onRedirectCallback}
>
{element}
</Auth0Provider>
);
};
Here's what's happening in this code:
The
wrapRootElement
hook from the Gatsby Browser API sets up the use of Provider components.Wrap the root element in the
<Auth0Provider>
Pass in the
AUTH0_DOMAIN
andAUTH0_CLIENTID
into thedomain
andclientId
propsPass in
window.location.origin
to theredirectUri
prop, which is the URL Auth0 will redirect your browser to with the authentication result.onRedirectCallback
removes the code and state parameters from the URL when you are redirected from the authorize page.
In this sample application, you won't be using the access token to access an API. Suppose your Gatsby application gets data using a secure API. In that case, you will use the access token stored here to access those protected resources (for example, by adding it as an
Authorization
header). Check out the Gatsby documentation on Sourcing from Private APIs to learn more about how to use private API data in your Gatsby site.
Protect a Gatsby Route with Auth0
Now that Auth0Provider
is set up and ready to be used in Gatsby. You're going to protect the /account
route. When logged-out users try to visit, they're redirected to the Auth0 login.
Add the new code indicated by the 👇 emojis to account.js
// src/pages/account.js
import React from "react";
/* 👇 New code 👇 */
import { Link } from "gatsby";
import { useAuth0 } from "@auth0/auth0-react";
/* 👇 Import the withAuthenticationRequired HOC 👇 */
import { withAuthenticationRequired } from '@auth0/auth0-react';
const Account = () => {
/* 👇 Access user from the useAuth0 hook 👇 */
const { user } = useAuth0();
return (
<>
<nav>
{/* 👇 Link to homepage */} 👇
<Link to="/">Home</Link>
{/* 👇 Display users email */} 👇
<p>Email: {user.email}</p>
</nav>
</>
);
};
/* 👇 Wrap the component in the withAuthenticationRequired handler 👇 */
export default withAuthenticationRequired(Account);
What's happening in this code?
You accessed the
user
object from theuseAuth0
hookAdded a link to the home page with the Gatsby
Link
componentDisplayed the users' email address stored in the
user
objectWrapped the exported
account
component inwithAuthenticationRequired()
Wrapping account
in withAuthenticationRequired()
makes it so that when anonymous users visit the /account
route, they will be redirected to the login page. The user will also return to the page they were visiting after successful login.
With the server running, visit http://localhost:8000/account, and you'll be redirected to the Auth0 log in.
Instead of making the user visit the /account
path to log in, which is inconvenient. In the next section, you'll use Auth0 to allow users to choose when they want to log in with a login button.
Create a login button component
The next thing to do is to create a login button. When the user clicks this button, they will be redirected to Auth0's Universal Login page and authenticated after successful login. Inside the src
directory, create a components
directory, and then create a file called LoginButton.js
in src/components/
and paste in the code below:
// src/components/LoginButton.js
import React from "react";
import { useAuth0 } from "@auth0/auth0-react";
function LoginButton() {
const {
isAuthenticated,
loginWithRedirect,
} = useAuth0();
return !isAuthenticated && (
<button onClick={loginWithRedirect}>Log in</button>
);
};
export default LoginButton;
Here's a breakdown of this code:
The
useAuth0
hook gives you access auth state and methods. In this example, you get theisAuthenticated
auth state and theloginWithRedirect
method from theuseAuth0
hook.The auth state
isAuthenticated
checks if Auth0 has authenticated the user before React renders any component.Pass
loginWithRedirect
to theonClick
handler to redirect your users to the Auth0 Universal Login Page, where they can get authenticated.
With this component created, the next step is to add it to the index page.
Add the LoginButton component to index.js
So far, there isn't a way for the user to actually log in and get redirected to Auth0 for login and authentication. Let's import and add the LoginButton
component to index.js
.
Update index.js
with the new code shown below:
// src/pages/index.js
import React from "react";
import { Link } from "gatsby";
/* 👇 New Code */
import LoginButton from "../components/LoginButton";
export default function Home() {
return (
<div>
{/* 👇 New Code */}
<LoginButton />
<p><Link to="/account">Visit Your Account</Link></p>
</div>
);
};
Restart the Gatsby development server and navigate to localhost:8000/account
. You should now see a button labeled login, click it, and you're directed to Auth0 to log in to your application (you will need to authorize the application the first time you log in). You'll also see your email address on the account page.
Add Logout to a Gatsby App with Auth0
Finally, you'll need a way for users to log out of your application. Inside src/components
create a new file called LogoutButton.js
.
Then add the following code:
// src/components/LogoutButton.js
import React from "react";
import { useAuth0 } from "@auth0/auth0-react";
function LogoutButton() {
const {
isAuthenticated,
logout,
} = useAuth0();
return isAuthenticated && (
<button onClick={() => {
logout({ returnTo: window.location.origin });
}}>Log out</button>
);
};
export default LogoutButton;
Here is a quick recap of this code:
- Access the
isAuthticated
auth state and thelogout
method from theuseAuth0
hook. - The
logout()
method redirects your users to your Auth0 logout endpoint (https://YOUR_DOMAIN/v2/logout) and then immediately redirects them to your application.
Update the account component
Now you can add a LogoutButton
to the navigation section of the Account
component. The finished Account
component code will look like this:
// src/pages/account.js
import React from "react";
import { Link } from "gatsby";
import { useAuth0 } from "@auth0/auth0-react";
import { withAuthenticationRequired } from "@auth0/auth0-react";
/* 👇 New Code */
import LogoutButton from "../components/LogoutButton";
const Account = () => {
const { user } = useAuth0();
return (
<>
<nav>
<Link to="/">Home</Link>
<p>Email: {user.email}</p>
{/* 👇 New Code */}
<LogoutButton />
</nav>
</>
);
};
export default withAuthenticationRequired(Account);
Running gatsby develop
again, you should now be able to log in, refresh your application, and log out successfully. Congratulations!
Remember, the finished code for this tutorial is at the gatsby-auth0
repository.
Conclusion
You now know how to implement authentication using Auth0 in virtually any Gatsby application. Feel free to explore the excellent Gatsby docs and learn more about sourcing and querying data with GraphQL, styling your application using CSS modules or CSS-in-JS, and much, much more. Most importantly, I hope you have as much fun as I do developing with Gatsby!