Implementing Authentication in a Next.js App using Router, Sessions, Cookies, and JWTs

Posted by


In this tutorial, we will be exploring how to implement router authentication in a Next.js app using sessions, cookies, and JSON Web Tokens (JWTs). Router authentication is essential for protecting sensitive pages or routes in your application and ensuring that only authenticated users can access them.

Here’s a step-by-step guide on how to set up router authentication in a Next.js app:

Step 1: Set up your Next.js app

Before we begin implementing router authentication, make sure you have a Next.js app set up and running. If you haven’t already created a Next.js app, you can do so by running the following command:

npx create-next-app your-app-name

Next, navigate to your app directory and start the development server by running:

npm run dev

Step 2: Install necessary packages

To implement router authentication, we’ll need to install a few packages. Run the following command to install dependencies:

npm install next-cookies cookie jwt-decode

Step 3: Create a custom hook for authentication

Next, we’ll create a custom hook that will handle authentication logic in our app. Create a new file called useAuth.js in your hooks directory and add the following code:

import { useState, useEffect } from 'react';
import cookies from 'next-cookies';
import jwtDecode from 'jwt-decode';

const useAuth = () => {
  const [user, setUser] = useState(null);

  useEffect(() => {
    const token = cookies.get(null).token;
    if (token) {
      const decodedToken = jwtDecode(token);
      setUser(decodedToken);
    } else {
      setUser(null);
    }
  }, []);

  const login = (token) => {
    cookies.set(null, 'token', token, { path: '/' });
    const decodedToken = jwtDecode(token);
    setUser(decodedToken);
  };

  const logout = () => {
    cookies.remove(null, 'token');
    setUser(null);
  };

  return { user, login, logout };
};

export default useAuth;

This custom hook will handle setting and removing the JWT token in cookies, as well as decoding the token to get user information. It will also provide login and logout functions.

Step 4: Protect routes with authentication

Now let’s protect the routes in our app with authentication. To do this, we’ll create a higher-order component (HOC) called withAuth that will check if the user is authenticated before rendering the page. Create a new file called withAuth.js in your hoc directory and add the following code:

import { useRouter } from 'next/router';
import useAuth from '../hooks/useAuth';

const withAuth = (Component) => {
  const Auth = (props) => {
    const { user } = useAuth();
    const router = useRouter();

    if (!user) {
      router.push('/login');
      return null;
    }

    return <Component {...props} />;
  };

  return Auth;
};

export default withAuth;

Step 5: Implement login and logout functionality

To enable users to log in and out of your app, create a new login page and implement login and logout functionality. Create a new page called login.js in your pages directory with the following code:

import { useState } from 'react';
import { useRouter } from 'next/router';
import useAuth from '../hooks/useAuth';

const Login = () => {
  const { login } = useAuth();
  const router = useRouter();
  const [credentials, setCredentials] = useState({ username: '', password: '' });

  const handleSubmit = (e) => {
    e.preventDefault();
    // Perform authentication logic here
    login('your_jwt_token_here');
    router.push('/');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={credentials.username}
        onChange={(e) => setCredentials({ ...credentials, username: e.target.value })}
      />
      <input
        type="password"
        value={credentials.password}
        onChange={(e) => setCredentials({ ...credentials, password: e.target.value })}
      />
      <button type="submit">Log In</button>
    </form>
  );
};

export default Login;

Step 6: Protect routes using the withAuth HOC

Finally, to protect a route in your app, use the withAuth HOC that we created earlier. For example, let’s protect a dashboard page. Create a new page called dashboard.js in your pages directory with the following code:

import withAuth from '../hoc/withAuth';

const Dashboard = () => {
  return (
    <div>
      <h1>Dashboard</h1>
    </div>
  );
};

export default withAuth(Dashboard);

Now, the Dashboard page will only be accessible to authenticated users. If a user is not authenticated, they will be redirected to the login page to log in.

Congratulations! You have successfully implemented router authentication in your Next.js app using sessions, cookies, and JWTs. This will help protect sensitive routes and ensure that only authenticated users can access them. Feel free to customize the authentication logic and user experience to fit your app’s requirements.

0 0 votes
Article Rating
34 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
@0GRANATE0
2 months ago

which nextjs version is this? which version should I take for a new project?

@longhuynh8277
2 months ago

would be great if there is an example with service worker.

@rtoronto7637
2 months ago

Why is email 'null'? Shouldn't the email that was typed in be displayed under the form?

@nardove
2 months ago

Thanks! for the tutorial, just a small note for those that wonder why the email value is always null, simply add name='email' to the input field

@salmenbejaoui1696
2 months ago

The website I'm working on uses Akamai as the first cache layer to cache the HTML response. In this case, the login mechanism must be handled client-side to avoid security issues and data leaks right? Does this prevent me from managing the session server-side?

@SumonaYesnin-y3y
2 months ago

Garcia Frank Davis Jose Rodriguez Anthony

@financialwonton
2 months ago

please do one on google recaptcha

@myzone2867
2 months ago

This approach is more usefull than using Auth.js. I don't know why it's so popular but it's coding approach is more confusing and may be need more logic and code than this nextjs official approach.

@nandomax3
2 months ago

Do you really trust jose to encrypt your jwt?

@davidllanes1019
2 months ago

Hello, I have a genuine doubt: how can we use clean architecture on Next?

@notbarbara2647
2 months ago

I know this is a very big long shot, but I would absolutely love to see a video about a multi-tenant nextjs app with authentication (with custom domains and such). Ideally without using the Vercel starter kit, cuz that's no way to learn how it actually works… Also not ideal for people not hosting on Vercel. As far as I can tell there is not a single good resource for this on either YouTube or Google.

@caseyspaulding
2 months ago

This was great. Thank you

@saadmalik899
2 months ago

Hi, I'm having a problem using authjs, when the admin deletes the user from the admin dashboard or database, then the user who is deleted should be logged out and their session cookie should be deleted but nextauth is not following that behaviour or maybe I'm not setting things up correctly, so If you have any idea about this then please let me know, and I'm using the "JWT Strategy". Thanks

@PalakBhatt
2 months ago

awesome

@sebastiannjose
2 months ago

I want to thank you for this excellent introduction to authentication in Next.js

@LightSpeedDevil
2 months ago

Authjs is still in beta 😢
When will it be ready for production?

@phamtienthinh1795
2 months ago

Thank you for a basic tutorial

@AbuBakr1
2 months ago

No database session in nextAuth credentials strategy, hence why i prefer Lucia Auth

@benizukebeni
2 months ago

i got this error below when using getSession() on the middleware and other page. also the cookies is suddenly got deleted in random occassion.
although i did the exact same method with your example.

node_modulesjosedistbrowserlibjwt_claims_set.js (89:18) @ default
"exp" claim timestamp check failed

@alirabiee-q1b
2 months ago

I wanted to use Zustand to have my user data and token in a client-side store, the I made my <<root template.tsx>> as server component and did the "get token and user" logic there and wrapped the zustand Provider around the children of the component, since I couldn't use layout.tsx because it didn't update I used template.tsx instead
is it a bad practice? what should I do?
I am using Laravel as my backend service

please answer I need your help