Passwordless Login with Email and JSON Web Token (JWT) Authentication using Next.js

Putri Karunia
Cotter
Published in
5 min readMay 26, 2020

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.

NextJS Video Tutorial

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. 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.

How does the overall registration/login would 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.

Update our website

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

pages/index.js

Step 1: Show the Register/Login form

Install the dependencies:

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

pages/index.js

Then import and initialize Cotter to embed the email form.

top of pages/index.js
pages/index.js

You need to add your API_KEY_ID here. Create a free account 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.

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:

{
"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 .

pages/index.js

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

pages/index.js

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() .

pages/index.js — below the first useEffect

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

pages/index.js

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

pages/index.js inside Home before return

And show the logout button:

pages/index.js

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

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

Let’s add 2 routes in our pages/api

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.

pages/api/public.js

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

pages/api/private.js

Now let’s validate the access token.

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

pages/api/private.js

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

pages/api/private.js inside checkJWT

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.

pages/index.js

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:

pages/index.js update the grid class

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

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/ 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.

What’s Next?

Learn more about the OAuth tokens returned from 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.

Questions & Feedback

If you have any questions or feedback, feel free to join Cotter’s Slack Channel 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 and check out our documentation.

Originally published at https://blog.cotter.app on May 26, 2020.

--

--

Putri Karunia
Cotter
Editor for

Co-Founder at Typedream.com | Sharing my journey as a Founder, 0-1 Product Designer & Software Developer