# Using Next.js and Auth0

Source: https://tpiros.dev/blog/using-next-js-and-auth0

# Before you start

This guide walks through setting up authentication and authorisation in an example application built with Next.js and Auth0.

> If you're new to Auth0, have a look at this [overview](https://auth0.com/docs/overview).

## Get the code

Grab the code from this GitHub repository: [https://github.com/tpiros/nextjs-auth0](https://github.com/tpiros/nextjs-auth0).

## What is Next.js?

Next.js is a JavaScript framework for building server-rendered or statically exported React.js apps. A standard React application gets generated in the browser, which then serves the final HTML to the user. With Next.js, the HTML gets built on the server. The browser receives pure HTML and just needs to render it. (The browser still handles content updates after the initial load.)

Next.js is lean to set up. Three dependencies, no webpack configuration. It's all handled for you.

> Server-side rendering (SSR) exists in other frameworks too. Angular2+ applications can use SSR via Angular Universal, for instance.

# What is Auth0?

Auth0 provides Authentication as a Service. Their SDK and APIs slot into applications without much friction. You can wire up multiple identity providers, and there's a generous free tier (up to 7,000 users).

In this post, we'll build an application using Next.js and Auth0. Let's get into it.

## Get your application keys

Sign up for Auth0 and create a new client. Grab the `Client ID` and `Client Secret` from the dashboard. You'll need both.

# Configure callback URLs

Before writing any code, configure the callback URLs. You'll find these settings in the same place as the `Client ID` and `Client Secret`.

Set the `Allowed Callback URL` to `http://localhost:3000/redirect` and the `Allowed Logout URL` to `http://localhost:3000`.

> If you're running on a different port, update the port numbers. These URLs are required. Without them, the application will throw a mismatch error on login.

# Install libraries

Install the project dependencies:

```
npm i auth0-js next react react-dom isomorphic-unfetch js-cookie jsonwebtoken
```

Then open `package.json` and update the `scripts` section:

```json
"scripts": {
  "dev": "next",
  "build": "next build",
  "start": "next start"
}
```

# The application

The app has a welcome page and a public page, both accessible to everyone. There's also a secret page that only appears after login.

## Creating the application

Next.js requires pages to live in a `pages` folder. Static resources go in `static`. We'll also create a reusable component.

```
components
  - Header.js
pages
  - index.js
  - login.js
  - logout.js
  - public.js
  - redirect.js
  - secret.js
  -
static
  - auth.js
  - auth0.js
  - secure-template.js
  - template.js
- settings.js
```

### Testing the setup

Create an `index.js` file under `pages` with this content:

```javascript

```

Fire up the dev server with `npm run dev`. Open `http://localhost:3000` in a browser and you should see "Hello world!".

> Hot reloading comes built in with `npm run dev`. No need to restart anything when you change the code.

### The application flow

Users log in to see the secret page. On successful login, we store the JWT token in a cookie. As long as that cookie holds valid data, we can show the secret page.

Here's the tricky bit. We need to check whether the cookie exists and contains our data, then let Next.js server-render the page accordingly. We also need to verify the token hasn't been tampered with.

## settings.js

This file holds global settings. Sign up for Auth0 and copy your credentials from the dashboard:

```javascript
const clientID = process.env.CLIENTID || ''; // your clientID
const domain = process.env.DOMAIN || ''; // your domain

```

## components/Header.js

A navigation bar component used across the application. Here's the gist:

```javascript

const Header = ({ isLoggedIn }) => (
  <div>
    // ... some inline styles
    <nav>
      <ul>
        <li>
          
        </li>
        <li>
          
        </li>
        {isLoggedIn ? (
          <li>
            
          </li>
        ) : (
          <li>
            
          </li>
        )}
        {isLoggedIn ? (
          <li>
            
          </li>
        ) : (
          ''
        )}
      </ul>
    </nav>
    <h1>Auth0 & Next.js</h1>
  </div>
);

Header.propTypes = {
  isLoggedIn: PropTypes.bool,
};

```

The Header takes an `isLoggedIn` prop to toggle the right navigation items.

## static/auth0.js

This file handles the Auth0-specific functions: `login()`, `logout()` and `parseHash()`. Here's the `login()` function:

```javascript

const clientID = settings.clientID;
const domain = settings.domain;

function webAuth(clientID, domain) {
  return new auth0.WebAuth({
    clientID: clientID,
    domain: domain,
  });
}

function login() {
  const options = {
    responseType: 'id_token',
    redirectUri: 'http://localhost:3000/redirect',
    scope: 'openid profile email',
  };

  return webAuth(clientID, domain).authorize(options);
}
// continued implementation of the other functions
```

## static/auth.js

This file handles token verification and cookie operations. First, pull in the dependencies:

```javascript

```

We need functions to save and delete cookies, verify the token, and retrieve the JWK.

### Token verification and JWK

Auth0 supports two algorithms for signing JWTs: `RS256` and `HS256`. Our example uses `RS256`, which means we need to fetch a JSON Web Key (JWK) to verify the token.

> A JWK lives inside a JWKS (JSON Web Key Set). Auth0 currently supports a single JWK for signing. You can access the signing JWK via the `keys` property in the JWKS.

Auth0 exposes the JWKS file through an endpoint, so we can grab the signing JWK easily.

At a high level, token verification goes like this:

- Retrieve the JWKS and extract the `key` property for the JWK
- Decode the JWT and grab the `kid` from the header
- Compare that `kid` with the `kid` from the JWK
- Build a certificate from the `x5c` property of the JWK
- Run the verification

Two functions handle this:

```javascript
async function getJWK() {
  const res = await fetch(`https://${settings.domain}/.well-known/jwks.json`);
  const jwk = await res.json();
  return jwk;
}

async function verifyToken(token) {
  if (token) {
    const decodedToken = jwt.decode(token, { complete: true });
    const jwk = await getJWK();
    let cert = jwk.keys[0].x5c[0];
    cert = cert.match(/.{1,64}/g).join('\n');
    cert = `-----BEGIN CERTIFICATE-----\n${cert}\n-----END CERTIFICATE-----\n`;
    if (jwk.keys[0].kid === decodedToken.header.kid) {
      try {
        jwt.verify(token, cert);
        return true;
      } catch (error) {
        console.error(error);
        return false;
      }
    }
  }
}
```

We also need cookie save/delete functions. We'll use `js-cookie` for this. ([This GitHub issue](https://github.com/zeit/next.js/issues/2252#issuecomment-308223970) explains why.)

```javascript
function saveToken(jwtToken, accessToken) {
  Cookie.set('user', jwtDecode(jwtToken));
  Cookie.set('jwtToken', jwtToken);
}

function deleteToken() {
  Cookie.remove('user');
  Cookie.remove('jwtToken');
}
```

Both are dead simple.

Finally, we need functions to retrieve the token. Two of them: one reads the cookie from the browser, the other extracts it from request headers on the server:

```javascript
async function getTokenForBrowser() {
  const token = Cookie.getJSON('jwtToken');
  const validToken = await verifyToken(token);
  if (validToken) {
    return Cookie.getJSON('user');
  }
}

async function getTokenForServer(req) {
  if (req.headers.cookie) {
    const jwtFromCookie = req.headers.cookie
      .split(';')
      .find((c) => c.trim().startsWith('jwtToken='));
    if (!jwtFromCookie) {
      return undefined;
    }
    const token = jwtFromCookie.split('=')[1];
    const validToken = await verifyToken(token);
    if (validToken) {
      return jwt.decode(token);
    } else {
      return undefined;
    }
  }
}
```

Notice the `verifyToken()` call. Nobody gets to tamper with the token unchecked.

Export everything so other files can use these functions:

```javascript

  saveToken,
  deleteToken,
  getTokenForBrowser,
  getTokenForServer,
  verifyToken,
};
```

The hardest bit is behind us. Let's look at the templates.

## static/template.js

This template checks for a token and builds up the properties that pages will consume:

```javascript

  class Template extends React.Component {
    static async getInitialProps({ req }) {
      const loggedInUser = process.browser
        ? await getTokenForBrowser()
        : await getTokenForServer(req);
      const pageProperties =
        (await Page.getInitialProps) && (await Page.getInitialProps(req));
      return {
        ...pageProperties,
        loggedInUser,
        isLoggedIn: !!loggedInUser,
      };
    }

    render() {
      return (
        <div>
          
          
        </div>
      );
    }
  };
```

The template renders both the header and the page content based on the properties it sets up.

## pages/index.js

The entry file. This loads first when someone hits `http://localhost:3000`. It uses our template, and if `isLoggedIn` is false, it shows a "You're not logged in yet" message:

```javascript

const Index = ({ isLoggedIn }) => (
  <div>
    Hello, this is the main application.
    {!isLoggedIn && <p>You're not logged in yet</p>}
  </div>
);

Index.propTypes = {
  isLoggedIn: PropTypes.bool,
};

```

## pages/login.js

This file calls the login method from `auth0.js`:

```javascript

class Login extends React.Component {
  componentDidMount() {
    login();
  }
  render() {
    return null;
  }
}

```

That's the whole thing.

## pages/redirect.js

Auth0 login works via a popup that asks for credentials. After a successful login, the user gets redirected to a callback URL (the one we set up in the Auth0 console). On that redirect, we save the JWT token to a cookie:

```javascript

  componentDidMount() {
    parseHash((err, result) => {
      if (err) {
        console.error('Error signing in', err);
        return;
      }
      verifyToken(result.idToken).then((valid) => {
        if (valid) {
          saveToken(result.idToken, result.accessToken);
          Router.push('/');
        } else {
          Router.push('/');
        }
      });
    });
  }
  render() {
    return null;
  }
}
```

We're also using `Router.push('/')` to send the user back to the front page.

## pages/logout.js

Logging out is simple. Call the right functions from `auth.js`:

```javascript

  componentDidMount() {
    deleteToken();
    logout();
    Router.push('/');
  }
  render() {
    return null;
  }
}
```

> Don't forget to delete the cookies during logout. Skip that step and you'll get odd behaviour.

## static/secure-template.js

For pages that should only appear after login, we create a separate template. It's identical to `template.js` except the `render()` method swaps content based on login state:

```javascript
render() {
  if (!this.props.isLoggedIn) {
    return (
      <div>
        
        <p>You're not authorised. Try to <a href="/login">Login</a></p>
      </div>
    )
  }
  return (
    <div>
      
      
    </div>
  )
}
```

## pages/secret.js

The page we only show to logged-in users, built on the secure template:

```javascript

const Secret = ({ loggedInUser }) => (
  <div>
    Hi {loggedInUser.nickname}! <img src={loggedInUser.picture} width="100px" />
    <div>
      <style jsx>
        {`
          pre {
            width: 500px;
            background: #ddd;
            white-space: pre-wrap;
          }
        `}
      </style>
      <pre>{JSON.stringify(loggedInUser, null, 2)}</pre>
    </div>
  </div>
);

Secret.propTypes = {
  loggedInUser: PropTypes.object,
};

```

## pages/public.js

The simplest page. A `<div>` that's visible to everyone:

```javascript

const Public = () => (
  <div>
    <p>This page is visible to everyone!</p>
  </div>
);

```

# Conclusion

Server-side rendering with Next.js gets interesting when you layer in authentication. The tricky part is making sure the server can access the JWT token so it can build the right pages. Cookies bridge that gap, and token verification keeps things secure.
