Passwordless Login with Email and JSON Web Token (JWT) Authentication using Next.js
Tags: webdev, javascript, tutorial
Published: 2020-07-23 17:49:33

---
title: Passwordless Login with Email and JSON Web Token (JWT) Authentication using Next.js
published: true
date: 2020-07-22 19:00:00 PST
tags: webdev, js, javascript, tutorial
canonical_url: https://blog.cotter.app/passwordless-login-with-email-and-json-web-token-jwt-authentication-with-nextjs/
---

 ![Passwordless Login with Email and JSON Web Token (JWT) Authentication using Next.js](https://blog.cotter.app/content/images/size/w2000/2020/05/EmailAuthenticationWithJWTTokenUsingNextJS.png)

<a href="http://www.youtube.com/watch?feature=player_embedded&v=D6432VV4TdQ
" target="_blank"><img src="http://img.youtube.com/vi/D6432VV4TdQ/0.jpg" 
</a>

#### _How do you log your users in and how do you give them access? We'll go over how to authenticate and authorize users without passwords in Next.js._

____

When you start adding users to your website, the main question that you need to answer is: **how do you log your users in and how do you give them access to the appropriate resources?**

In this tutorial we'll go over how to address both questions and build a Next.js app that only allows logged-in users to access private resources within the app.

## So you want to have users.

Let's go over some concepts: **authentication vs authorization**.

### Authentication: How do I log my users in?

Authentication is a way for your server to verify the user's identity. The most common way to authenticate users is by using the email/password combo. Unfortunately, [passwords have serious disadvantages on both security and user interface](https://blog.cotter.app/medium-and-slack-moved-away-from-passwords-and-heres-a-reason-why-you-should-too/). **In this tutorial, we'll use a verification code sent to the user's email to authenticate the user**.

### Authorization: How do I keep my users logged in?

Authorization is a way for your server to authorize a request. In simpler terms, this is where you pass in a token or session to your backend server when calling an API to view or update some data. The 2 common strategies are **cookie-based sessions** and **JWT tokens**.

The main advantage for JWT tokens is that it's not stored in your database so you don't need to do a DB check to validate every request. That's why **we're going to use JWT Tokens in this tutorial.**

[Learn more about how OAuth 2.0 and Access Token works.](https://blog.cotter.app/what-on-earth-is-oauth-super-simple-intro-to-oauth-20-access-tokens-and-how-to-implement-it-in-your-site/)

### How would the overall registration/login look like?

**Authentication:** We'll ask for the user's email and send them an email containing a code. If the user enters the code correctly, we'll get a JWT Token in the frontend and store it in `localStorage`.

**Authorization:** Every time we want to access a private API endpoint we need to include a header `Authorization: Bearer ${token}`.

> Storing the token in the browser local storage is susceptible to cross-site scripting (XSS) attack. If an attacker can successfully run JavaScript code in your site, they can retrieve the tokens stored in local storage. XSS vulnerability arises when your website takes data from users without proper validation or from a third-party JavaScript code (like Google Analytics, jQuery, etc) included in the website.

## Let's Start Building

Create your Next.js app. We'll call the app `next-passwordless-login` and use the default starter app.

```
yarn create next-app
cd next-passwordless-login && yarn dev
```

### Update our website

Update your `pages/index.js` . Delete everything except the styling and the container div, then add this inside the container div.

```react
<main>
    <h1 className="title">Passwordless App.</h1>

    {/* 1️⃣ TODO: Setup a div to contain the form */}
    
    <div className="grid">
        <div className="card">
            <h3>Public Endpoint</h3>
            <p>You should be able to access this when not logged in</p>
        </div>

        <div className="card">
            <h3>Private Endpoint</h3>
            <p>You need to log in to access this endpoint</p>
        </div>
    </div>
</main>
```
<figcaption>pages/index.js</figcaption>
### Step 1: Show the Register/Login form

Install the dependencies:

```
yarn add cotter cotter-node
```

Add a div to contain the form below our title in `pages/index.js`

```react
<h1 className="title">Passwordless App.</h1>

{/* 1️⃣ TODO: Setup a div to contain the form */}
<div id="cotter-form-container" style={{ width: 300, height: 300 }} />
```
<figcaption>pages/index.js</figcaption>

Then import and initialize Cotter to embed the email form.

```react
// 1️⃣ import Cotter verification form and useEffect from react
import Cotter from "cotter";
import { useEffect } from "react";
```
<figcaption>top of pages/index.js</figcaption>

```react
export default function Home() {
  // 1️⃣ Initialize and show the form
  // Add the lines here
  useEffect(() => {
    var cotter = new Cotter(API_KEY_ID); // 👈 Specify your API KEY ID here
    cotter
      .signInWithOTP()
      .showEmailForm()
      .then(payload => {
        console.log(payload);
        alert("Success");
      })
      .catch(err => console.log(err));
      
  }, []);
  // until here

  return (...);
}
```
<figcaption>pages/index.js</figcaption>

You need to add your `API_KEY_ID` here. [Create a free account](https://dev.cotter.app/) at Cotter, then create a Project and take notes of the API Keys.

Now you should be able to see the login form like below.

![Passwordless Login with Email and JSON Web Token (JWT) Authentication using Next.js](https://blog.cotter.app/content/images/2020/05/image.png)

The form will automatically send an email as necessary and show an input to enter the code. It won't send another email if you've already verified your email in this browser.

### Step 2: Keep users logged in with `access_token`

#### Read the `console.log` 

Try entering your email and logging-in. You should see that the `payload` we receive in the `OnSuccess` function contains the following object:

```react
{
  "token": {...},
  "email": "team@cotter.app",
  "oauth_token": {
    "access_token": "eyJhbGciOiJFUzI1NiIsIn...",
    "id_token": "eyJhbGciOiJFUzI1NiIsInR5cC...",
    "refresh_token": "199:doZor3GtgsrYo4R7L...",
    "expires_in": 3600,
    "token_type": "Bearer",
    "auth_method": "OTP"
  },
  "user": {
    "ID": "ecadbd2c-56f8-4078-b45d-f17786ed499e", // Cotter User ID
    ...
  }
}
```

We want to use the `access_token` in this tutorial, so let's grab that and store it in `localStorage`.

```react
    useEffect(() => {
    var cotter = new Cotter(API_KEY_ID); // 👈 Specify your API KEY ID here
    cotter
      .signInWithOTP()
      .showEmailForm()
      .then(payload => {
        console.log(payload);
-        alert("Success");
        
+        // 2️⃣(a) Store the access token and set logged in
+        localStorage.setItem("ACCESS_TOKEN", payload.oauth_token.access_token);
+        setIsLoggedIn(true);
      })
      .catch(err => console.log(err));
  }, []);
```
<figcaption>pages/index.js</figcaption>

Now let's define `setIsLoggedIn()`, this will help us show whether the user is logged in or not.

```react
     import Cotter from "cotter";
     import { useEffect } from "react";
+    import { useState } from "react";

    export default function Home() {
+      // 2️⃣(a) Show if the user is logged in.
+      var [isLoggedIn, setIsLoggedIn] = useState(false);
```
<figcaption>pages/index.js</figcaption>

We also want to check if the `localStorage` contains `ACCESS_TOKEN` every time the page loads and update our `isLoggedIn` variable. Add this below the first `useEffect()` .

```react
// 1️⃣ Initialize and show the form
useEffect(() => {...}, []);

// Add the lines below here
// 2️⃣(b) Check if the ACCESS_TOKEN exists every time the page loads
useEffect(() => {
    if (localStorage.getItem("ACCESS_TOKEN") != null) {
        setIsLoggedIn(true);
    }
}, []);
```
<figcaption>pages/index.js – below the first useEffect</figcaption>

Now let's show if the user is logged in below our form:

```react
{/* 2️⃣(c) Show if the user is logged in. */}
<p>
    {isLoggedIn ? "✅ You are logged in" : "❌ You are not logged in"}
</p>
```
<figcaption>pages/index.js</figcaption>
### Step 3: Logging-out

Logging-out is achieved by removing the `access_token` from our `localStorage`. Let's add the logout function inside `Home` before `return()` in `pages/index.js`

```react
// 3️⃣ Log out users
const logOut = () => {
    localStorage.removeItem("ACCESS_TOKEN");
    setIsLoggedIn(false);
};
```
<figcaption>pages/index.js inside <code>Home</code> before <code>return</code></figcaption>

And show the logout button:

```react
{/* 3️⃣ Show the logout button */}
{isLoggedIn ? (
    <div
        className="card"
        style={{ padding: 10, margin: 5 }}
        onClick={logOut}
    >
    	Log Out
    </div>
) : null}
```
<figcaption>pages/index.js</figcaption>

You can now see the if you're logged in and the logout button:

![Passwordless Login with Email and JSON Web Token (JWT) Authentication using Next.js](https://blog.cotter.app/content/images/2020/05/NextEmailLogin-LoginLogout.gif)

### Step 4: Allowing the user from accessing public/private endpoints.

Let's add 2 routes in our `pages/api`

```react
touch pages/api/public.js pages/api/private.js
```

#### Defining the routes

Let's define our `/api/public` endpoint in `pages/api/public.js`. We're just going to return that the request is successful.

```react
export default (req, res) => {
  res.statusCode = 200;
  res.end(
    "Success! This is a public resource, you can see it without logging in."
  );
};

```
<figcaption>pages/api/public.js</figcaption>

Let's define our `/api/private` endpoint in `pages/api/private.js`. First we'll check if the authorization header exists.

```react
// 2) TODO: Import Cotter

const checkJWT = (handler) => async (req, res) => {
  // 1) Check that the access_token exists
  if (!("authorization" in req.headers)) {
    res.statusCode = 401;
    res.end("Authorization header missing");
  }
  const auth = await req.headers.authorization;
  const bearer = auth.split(" ");
  const token = bearer[1];
  console.log(token);
    
  // 2) TODO: Validate the access_token
    
  handler(req, res);
}

const handler = (req, res) => {
  res.statusCode = 200;
  res.end(
    `Success! This is a private resource and you have the access_token to view it.`
  );
};

export default checkJWT(handler);
```
<figcaption>pages/api/private.js</figcaption>

**Now let's validate the access token.**

First, import Cotter's jwt validator function at the top of `pages/api/private.js`

```react
import { CotterValidateJWT } from "cotter-node";
```
<figcaption>pages/api/private.js</figcaption>

Then call `CotterValidateJWT(token)` under step (2) inside `checkJWT`.

```react
  // 2) TODO: Validate the access_token
  var valid = false;
  try {
    valid = await CotterValidateJWT(token);
  } catch (e) {
    console.log(e);
    valid = false;
  }
  if (!valid) {
    res.statusCode = 403;
    res.end("Authorization header is invalid");
  }
```
<figcaption>pages/api/private.js inside <code>checkJWT</code></figcaption>
#### Calling the `/public` and `/private` API endpoints

Let's go back to `pages/index.js` and add 2 functions: `getPublicResource` and `getPrivateResource` that will call the endpoint `/api/public` and `/api/private`.

```react
export default function Home() {
  ...
  
  // 4️⃣ Get Public and Private Resources
  // Add the lines here
  var [publicResource, setPublicResource] = useState(null);
  var [privateResource, setPrivateResource] = useState(null);
  
  // Get Public Resource
  const getPublicResource = async () => {
    var resp = await fetch("/api/public");
    setPublicResource(await resp.text());
  };
    
  // Get Private Resource
  const getPrivateResource = async () => {
    var token = localStorage.getItem("ACCESS_TOKEN");
    if (token == null) {
      setPrivateResource("Token doesn't exist, you're logged-out");
      return;
    }
    var resp = await fetch("/api/private", {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    });
    setPrivateResource(await resp.text());
  };
  // Until here
  
  return(...);
}
```
<figcaption>pages/index.js</figcaption>

Now let's call the 2 functions from our buttons and show the response from the endpoints. Update the `div` with `className="grid"` to the following code:

```react
{/* 4️⃣ Call Get Public and Private Resources */}
<div className="grid">
    <div className="card" onClick={getPublicResource}>
        <h3>Public Endpoint</h3>
        <p>{publicResource}</p>
    </div>

    <div className="card" onClick={getPrivateResource}>
        <h3>Private Endpoint</h3>
        <p>{privateResource}</p>
    </div>
</div>
```
<figcaption>pages/index.js update the <code>grid</code> class</figcaption>

We display the response from the endpoints in the `publicResource` and `privateResource` variables.

![Passwordless Login with Email and JSON Web Token (JWT) Authentication using Next.js](https://dev-to-uploads.s3.amazonaws.com/i/kr2t1h18l0gce8c9nthm.gif)

## That's it

Now you can authenticate users by sending a code to their emails and allow them to access private endpoints that require an `access_token` to access.

If you're curious, print out the `access_token` and copy it to [https://jwt.io/](https://jwt.io/) to see what information is decoded. The `id_token` contains more information about the user and the `refresh_token` is used to [get a new `access_token` if it's expired](https://docs.cotter.app/v/0.1.0/getting-access-token/renewing-expired-tokens).

## What's Next?

Learn more about [the OAuth tokens returned from Cotter](https://docs.cotter.app/v/0.1.0/getting-access-token/handling-authentication-with-cotter) and use them in your API endpoints.

If you want to authenticate users using their phone number, follow this guide on [Verifying User's Phone Number via SMS and WhatsApp](https://blog.cotter.app/angular-send-email-sms-and-whatsapp-verification-code-for-new-users-worldwide/#verify-the-phone-number-via-sms-or-whatsapp).

* * *

### Questions & Feedback

If you have any questions or feedback, feel free to join [Cotter's Slack Channel](https://join.slack.com/t/askcotter/shared_invite/zt-dxzf311g-5Mp3~odZNB2DwYaxIJ1dJA) and chat us there.

### Ready to use Cotter?

If you enjoyed this tutorial and want to integrate Cotter into your website or app, you can [create a free account](https://dev.cotter.app/) and [check out our documentation](https://docs.cotter.app/).