Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Next.js Authentication with JWT

I am moving a project from React to Next.js and was wondering if the same authentication process is okay. Basically, the user enters their username and password and this is checked against database credentials via an API (Node.js/Express). So, I am not using Next.js internal api functionality, but a totally decoupled API from my Next.js project.

If the login credentials are correct, a JWT token is sent back to the client. I wanted to store that in local storage and then redirect the user. Any future HTTP requests will send the token in the header and check it is valid via the API. Is this okay to do? I ask because I see a lot of Next.js auth using cookies or sessions and don't know if that is the 'standard' approach which I should rather adopt.

like image 842
user8463989 Avatar asked Feb 04 '23 14:02

user8463989


1 Answers

My answer is purely based on my experiences and things I read. Feel free to correct it if I happened to be wrong.

So, my way is to store your token in HttpOnly cookie, and always use that cookie to authorize your requests to the Node API via Authorization header. I happen to also use Node.js API in my own project, so I know what's going on.

Following is an example of how I usually handle authentication with Next.js and Node.js API.

In order to ease up authentication problems, I'm using Next.js's built in getServerSideProps function in a page to build a new reusable higher order component that will take care of authentication. In this case, I will name it isLoggedIn.

// isLoggedIn.jsx

export default (GetServerSidePropsFunction) => async (ctx) => {
  // 1. Check if there is a token in cookies. Let's assume that your JWT is stored in 'jwt'.
  const token = ctx.req.cookies?.jwt || null;

  // 2. Perform an authorized HTTP GET request to the private API to check if the user is genuine.
  const { data } = await authenticate(...); // your code here...

  // 3. If there is no user, or the user is not authenticated, then redirect to homepage.
  if (!data) {
    return {
      redirect: {
        destination: '/',
        permanent: false,
      },
    };
  }

  // 4. Return your usual 'GetServerSideProps' function.
  return await GetServerSidePropsFunction(ctx);
};

getServerSideProps will block rendering until the function has been resolved, so make sure your authentication is fast and does not waste much time.

You can use the higher order component like this. Let's call this one profile.jsx, for one's profile page.

// profile.jsx

export default isLoggedIn(async (ctx) => {
  // In this component, do anything with the authorized user. Maybe getting his data?
  const token = ctx.req.cookies.jwt;
  const { data } = await getUserData(...); // don't forget to pass his token in 'Authorization' header.

  return {
    props: {
      data,
    },
  },
});

This should be secure, as it is almost impossible to manipulate anything that's on server-side, unless one manages to find a way to breach into your back-end.

If you want to make a POST request, then I usually do it like this.

// profile.jsx

const handleEditProfile = async (e) => {
  const apiResponse = await axios.post(API_URL, data, { withCredentials: true });
  
  // do anything...
};

In a POST request, the HttpOnly cookie will also be sent to the server, because of the withCredentials parameter being set to true.

There is also an alternative way of using Next.js's serverless API to send the data to the server. Instead of making a POST request to the API, you'll make a POST request to the 'proxy' Next.js's serverless API, where it will perform another POST request to your API.

like image 131
Nicholas Avatar answered Feb 06 '23 08:02

Nicholas