How to add Auth0 to Nextjs- the Ultimate Guide
Tags: javascript, tutorial, beginners, webdev
Published: 2020-04-01 14:10:37

In this video, we will show how you can add Auth0 to your Next.js application. This is something that used to be quite tricky but after the release of the api feature in Next.js which makes it super easy to add your own api-only endpoints to a Next.js application, the task of adding Auth0 became a lot more manageable. This became dead simple after Auth0 created a [`auth-nextjs` package](https://github.com/auth0/nextjs-auth0) which we will use in this video to set up all of our api endpoints.

**Github Starter Project**

First, go ahead and clone our [Next.js Auth0 Starter project on github](https://github.com/auth0/nextjs-auth0). Make sure to checkout the `start-here` tag or you will end up with the finished project. I'm adding a `-b development` flag so that you will check out the tag and immediately create a development branch so that you won't be in a dreaded detached HEAD state. Feel free to name the branch whatever you'd like:

```bash
git checkout start-here -b development
```

**Video of what we are doing:**

{% youtube vrj9gCSjzw0 %}

Let’s first start by installing the @auth0/nextjs-auth0 package.

```bash
npm install @auth0/nextjs-auth0 --save
```

Next, we want to initialize auth0 on the server so that we can use it in all of our api methods. Make sure to define all of the variables in a .env file in the root of your project. You should be able to find the clientId, clientSecret, and domain from auth0 once you create a new single-page application project. Make sure to add the redirect Uri and the postLogoutRedirectUri urls into the respective fields within your auth0 app so that auth0 knows which urls that it should trust during the entire redirect process.

_utils/auth.ts_

```ts
import { initAuth0 } from '@auth0/nextjs-auth0'

export default initAuth0({
  domain: process.env.domain,
  clientId: process.env.clientId,
  clientSecret: process.env.clientSecret,
  scope: 'openid profile',
  redirectUri: process.env.redirectUri,
  postLogoutRedirectUri: process.env.postLogoutRedirectUri,
  session: {
    cookieSecret: process.env.cookieSecret,
    cookieLifetime: 60 * 60 * 8,
    storeIdToken: false,
    storeAccessToken: false,
    storeRefreshToken: false,
  },
  oidcClient: {
    httpTimeout: 2500,
    clockTolerance: 10000,
  },
})
```

```.env
domain=your-auth0-domain.auth0.com
clientId=your-client-id
clientSecret=your-client-secret
redirectUri=http://localhost:3000/api/callback
postLogoutRedirectUri=http://localhost:3000/
cookieSecret=here-is-a-really-long-string-please-use-a-unique-one
```

In order for the .env variables to be recognized by Next.js we have one more step. Create a next.config.js file and add the following to it:

_next.config.js_

```js
require('dotenv').config()

module.exports = {}
```

Make sure to restart your next.js development server after you do this or these settings won't get loaded in. Then, add `dotenv` to the project:

```bash
npm install --save dotenv
```

This will load the environmental variables from the .env into our app but they will only be accessible from the server, which is exactly what we want. Now let's go ahead and make our api routes. They are all very similar except we will be calling different methods from the auth0 instance that we are defining above.

_pages/api/login.ts_

```ts
import auth0 from '../../utils/auth0'

export default async function login(req, res) {
  try {
    await auth0.handleLogin(req, res, {})
  } catch (error) {
    console.error(error)
    res.status(error.status || 500).end(error.message)
  }
}
```

_pages/api/callback.ts_

```ts
import auth0 from '../../utils/auth0'

export default async function callback(req, res) {
  try {
    await auth0.handleCallback(req, res, {})
  } catch (error) {
    console.error(error)
    res.status(error.status || 500).end(error.message)
  }
}
```

_pages/api/me.ts_

```ts
import auth0 from '../../utils/auth0'

export default async function me(req, res) {
  try {
    await auth0.handleProfile(req, res, {})
  } catch (error) {
    console.error(error)
    res.status(error.status || 500).end(error.message)
  }
}
```

_pages/api/logout.ts_

```ts
import auth0 from '../../utils/auth0'

export default async function logout(req, res) {
  try {
    await auth0.handleLogout(req, res)
  } catch (error) {
    console.error(error)
    res.status(error.status || 500).end(error.message)
  }
}
```

We start by defining an async function that has a `req` and a `res`. This is similar to express middlewares where we get access to the request through the `req` and modify the response by writing over pieces of the `res` object. In our case, we have a try/catch block where we try to call the auth0 method and pass the `req` and `res` into it. In the event that an error occurs, we log the error and return the error status and the error message.

Next, we need to create a way to store the user state on the client. The following code is pulled directly from the [`auth-nextjs`](https://github.com/auth0/nextjs-auth0) example folder. The general idea is that we create a react context that will store the user profile information. We will create a provider component called `UserProvider` which we will have in the root of our project and we will call the `useFetchUser` react hook in any of the nested react components that need access to the user profile information.

_utils/user.tsx_

```tsx
import React from 'react'
import fetch from 'isomorphic-unfetch'

// Use a global to save the user, so we don't have to fetch it again after page navigations
let userState

const User = React.createContext({ user: null, loading: false })

export const fetchUser = async () => {
  if (userState !== undefined) {
    return userState
  }

  const res = await fetch('/api/me')
  userState = res.ok ? await res.json() : null
  return userState
}

export const UserProvider = ({ value, children }) => {
  const { user } = value

  // If the user was fetched in SSR add it to userState so we don't fetch it again
  React.useEffect(() => {
    if (!userState && user) {
      userState = user
    }
  }, [])

  return <User.Provider value={value}>{children}</User.Provider>
}

export const useUser = () => React.useContext(User)

export const useFetchUser = () => {
  const [data, setUser] = React.useState({
    user: userState || null,
    loading: userState === undefined,
  })

  React.useEffect(() => {
    if (userState !== undefined) {
      return
    }

    let isMounted = true

    fetchUser().then(user => {
      // Only set the user if the component is still mounted
      if (isMounted) {
        setUser({ user, loading: false })
      }
    })

    return () => {
      isMounted = false
    }
  }, [userState])

  return data
}
```

Let's now update the `MainLayout` component to add the `UserProvider`. Since the `MainLayout` was a class-based component we need to also convert it to a functional component because hooks require functional components to function. We will also call the `useFetchUser` hook here so that we can feed the user and loading boolean into the provider itself.

_components/layout/MainLayout.tsx_

```tsx
import { Layout } from 'antd'
import { ReactNode, Component } from 'react'
import Navbar from './Navbar'
import styled from 'styled-components'
import { UserProvider, useFetchUser } from '../../utils/user'

const { Content } = Layout

const StyledContent = styled(Content)`
  min-height: 100vh;
`

export const MainLayout = ({ children }: { children: ReactNode }) => {
  const { user, loading } = useFetchUser()
  return (
    <UserProvider value={{ user, loading }}>
      <Layout>
        <Navbar />
        <StyledContent>{children}</StyledContent>
      </Layout>
    </UserProvider>
  )
}
```

We are now ready to update the `Navbar` component to add the `useFetchUser` hook and use the user object that we get back as a way to tell if the user is logged in or not. If it is undefined we can assume the user is not logged in an display the login button. Otherwise, if there is a user object then we know they are logged in and we can display the logout and profile buttons:

_components/layout/Navbar.tsx_

```tsx
import { Layout, Menu } from 'antd'
import Link from 'next/link'
import styled from 'styled-components'
import { useFetchUser } from '../../utils/user'

const { Header } = Layout

const StyledHeader = styled(Header)`
  background-color: #dddbe8;
  .ant-menu {
    width: 100%;
    background-color: #dddbe8;
    a {
      height: 64px;
    }
  }
`

const Navbar = () => {
  const { user, loading } = useFetchUser()

  return (
    <StyledHeader>
      <Menu mode="horizontal">
        <Menu.Item key="/">
          <Link href="/">
            <a>Home</a>
          </Link>
        </Menu.Item>
        {user && !loading
          ? [
              <Menu.Item key="/api/logout">
                <Link href="/api/logout">
                  <a>Logout</a>
                </Link>
              </Menu.Item>,
              <Menu.Item key="/profile">
                <Link href="/profile">
                  <a>Profile</a>
                </Link>
              </Menu.Item>,
            ]
          : null}
        {!user && !loading ? (
          <Menu.Item key="/api/login">
            <Link href="/api/login">
              <a>Login</a>
            </Link>
          </Menu.Item>
        ) : null}
      </Menu>
    </StyledHeader>
  )
}

export default Navbar
```

Finally, let's update the profile page so that we can display the user's profile information if they are logged in. Otherwise, we will redirect them to the home page.

_pages/profile.tsx_

```tsx
import { MainLayout } from '../components/layout/MainLayout'
import styled from 'styled-components'
import { useFetchUser } from '../utils/user'
import Router from 'next/router'

const StyledProfile = styled.div`
  padding: 50px 10px;
  text-align: center;
  h1 {
    font-size: 60px;
  }
`

export default function Profile() {
  const { user, loading } = useFetchUser()

  if (loading) {
    return (
      <MainLayout>
        <p>Loading...</p>
      </MainLayout>
    )
  }
  if (!user && !loading) {
    Router.replace('/')
  }

  return (
    <MainLayout>
      <StyledProfile>
        <h1>🤸</h1>
        <p>Welcome to the Profile Page! Here is your profile information:</p>
        <p>{JSON.stringify(user)}</p>
      </StyledProfile>
    </MainLayout>
  )
}
```

That's it! You should now have a working Next.js application with Auth0 and are now ready to build the next great web application with this as a starter. Cheers!

If you liked this tutorial, I created an entire course that teaches you how to build a recipe sharing application from the ground up using Next.js, Auth0 and a graphQL CMS called GraphCMS. We will go through how to take the foundation that you learned here and build a fully-featured application that has file uploads, user permissions, responsive design with Ant Design, and the entire app is hosted serverlessly using Zeit's Now service so that you can deploy it with one command and be confident that it will be reliable and scalable no matter how many people visit the page.

![Frontend Serverless with React and GraphQL](https://www.codemochi.com/static/7fcb9baf6ae2814c06c77c89c83d8c59/9b4a8/frontend-serverless.png)

[Frontend Serverless with React and GraphQL](https://courses.codemochi.com/frontend-serverless-with-react-and-graphql/)