Passwordless Login with Email and JSON Web Token (JWT) Authentication using Next.js
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. 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.
Step 1: Show the Register/Login form
Install the dependencies:
Add a div to contain the form below our title in pages/index.js
Then import and initialize Cotter to embed the email form.
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
.
Now let’s define setIsLoggedIn()
, this will help us show whether the user is logged in or not.
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()
.
Now let’s show if the user is logged in below our form:
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
Home
before return
And show the logout button:
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.
Let’s define our /api/private
endpoint in pages/api/private.js
. First we'll check if the authorization header exists.
Now let’s validate the access token.
First, import Cotter’s jwt validator function at the top of pages/api/private.js
Then call CotterValidateJWT(token)
under step (2) inside checkJWT
.
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
.
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:
grid
classWe 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.