node.jscookiesnext.jsserver-side-renderinghigher-order-components

Create a HOC (higher order component) for authentication in Next.js


So I'm creating authentication logic in my Next.js app. I created /api/auth/login page where I handle request and if user's data is good, I'm creating a httpOnly cookie with JWT token and returning some data to frontend. That part works fine but I need some way to protect some pages so only the logged users can access them and I have problem with creating a HOC for that.

The best way I saw is to use getInitialProps but on Next.js site it says that I shouldn't use it anymore, so I thought about using getServerSideProps but that doesn't work either or I'm probably doing something wrong.

This is my HOC code: (cookie are stored under userToken name)

import React from 'react';
const jwt = require('jsonwebtoken');

const RequireAuthentication = (WrappedComponent) => {

  return WrappedComponent;
};


export async function getServerSideProps({req,res}) {
  const token = req.cookies.userToken || null;

// no token so i take user  to login page
  if (!token) {
      res.statusCode = 302;
      res.setHeader('Location', '/admin/login')
      return {props: {}}
  } else {
    // we have token so i return nothing without changing location
       return;
     }
}
export default RequireAuthentication;

If you have any other ideas how to handle auth in Next.js with cookies I would be grateful for help because I'm new to the server side rendering react/auth.


Solution

  • You should separate and extract your authentication logic from getServerSideProps into a re-usable higher-order function.

    For instance, you could have the following function that would accept another function (your getServerSideProps), and would redirect to your login page if the userToken isn't set.

    export function requireAuthentication(gssp) {
        return async (context) => {
            const { req, res } = context;
            const token = req.cookies.userToken;
    
            if (!token) {
                // Redirect to login page
                return {
                    redirect: {
                        destination: '/admin/login',
                        statusCode: 302
                    }
                };
            }
    
            return await gssp(context); // Continue on to call `getServerSideProps` logic
        }
    }
    

    You would then use it in your page by wrapping the getServerSideProps function.

    // pages/index.js (or some other page)
    
    export const getServerSideProps = requireAuthentication(context => {
        // Your normal `getServerSideProps` code here
    })