Techtales.

Handling Authentication with JWT in NextJs
TD
The Don✨Author
Jul 28, 2024
6 min read

Handling Authentication with JWT in NextJs

43

Authentication is crucial in every app, and ensuring sufficient security is vital, whether you are creating a small app or a production-ready deployment. Understanding authentication concepts such as hashing and cryptography can be challenging, but fortunately, there are a few libraries that simplify the process.

For this blog, I will explain how I have implemented authentication in my app using JSON Web Token and server actions in NextJS. Since NextJS is a full-stack app, the concept of CORS will not be covered here. 

To begin, we need to install the relevant libraries. This includes jose, and bcrypt.js. There are vital to encode and decode the user passwords stored in the database. I am using a PostgreSQL database with PRISMA ORM.

To get started, assuming you have all the necessary setup (NextJS app and database), run the following command:

Jose is similar to jsonwebtoken but also works in a browser environment, enabling you to decode the token if you need the data in the frontend.

Next, we are going to create a JWT_SECRET that will be used to hash the passwords. You can set this to any random number or use OpenSSL to generate the token by running the command:

Save the token in the .env file, without the .local prefix, to ensure it is not exposed in the browser environment. 

NOTE: It’s crucial that you keep this key private and only accessible to the server. You can use an env provider or keep it in a secure location. Be sure you set it in Next.js without the NEXT_PUBLIC prefix.

If you accidentally leak the key to the client, then an attacker could use your key to sign bogus credentials with whatever role they like and gain full access to your platform!

Next, we are going to tap into the power of middleware in NextJS. Middleware is the cornerstone of our authentication system, designed to protect routes that require authentication.

By implementing this, you ensure that only authenticated users can access certain parts of your application, safeguarding sensitive information and user privacy.

NextJS already includes a middleware.js file, but if it does not exist, create one in the root directory and name it middleware.js. Add the following code to the middleware.

Note, we are using jose instead of JWT since we need to read user data rather than just check if the token exists. If we just check if the token exists, a user can create a token with nothing and be authenticated.

The matcher object configures the path that you want the API to match. You can modify the protected and public routes, and make sure to include them in the matcher function.

Next, we are going to create a login functionality that utilizes PRISMA to find and authenticate the user and return an HTTP-only cookie in the response body. It is better to create this in an api route.

Here is the login function in a file called /api/auth/login:

You can add some more complicated functionalities, such as checking the request ip address to rate-limit the user. If you wish to add a rate limiter in the login function, you can get the user's IP address by doing the following:

I know this is a lot of if requests; I am guilty, but you can also streamline this code to make it more efficient. In the register route, you can use the same functionality in the login route to return the HTTP code. 

Now we need to create a helper function that you can call to get the user. Remember, this function cannot return the user to the frontend; it will only work on the server.

This function enables you to decode the token, but does not account for scenarios where the token exists but has expired, and you might need to further modify it to handle such scenarios. 

To get user data, create a route (/api/me) where you can fetch the user data each time there is a logged-in user. This route will receive the request cookies and use the decodeToken function to return user data:

With the current setting, you can fetch user data and save it in context each time the user logs in. This ensures that components that need user data have access to the data.

Creating Logout Route

Creating a logout route is as easy as just deleting the token. Create a new route in /api/auth/logout and add this code:

Conclusion

We’ve covered the use of JWTs for efficient user authentication, storing user data in cookies for quick access, and the importance of custom middleware for route protection. 

Remember, the journey to secure and efficient authentication is ongoing and ever-evolving. The strategies and tools we’ve discussed are at the forefront of current best practices, providing a strong foundation for your Next.js applications.

However, the world of web security is dynamic, so staying updated with the latest trends and updates is crucial. Adding the rate limiter is also necessary to protect the login route from brute attacks.

4
3