Protecting Routes from unauthenticated users is a crucial part of any app.
In this blog, I'll show you exactly how to do that with your NextJS pages using Higher-Order Components. [1]
There can be several ways of authenticating a user like using cookies or JWT tokens.[2]
I'll be using JWT token as an example, where the accessToken
is stored in the localStorage
Let's consider a page "/dashboard". This page should be only accessed by authenticated users
In our Dashboard.jsx
// pages/dashboard.jsx
import withAuth from "HOC/withAuth.js";
const Dashboard = ({ user }) => {
return (
<div>
<h1>Dashboard</h1>
<h2>{user.name}</h2>
</div>
);
};
export default withAuth(Dashboard);
Notice that we are importing withAuth.jsx
and exporting the page by passing it as an argument. That is all we need to do for our pages.
In our withAuth.jsx
I'll show you two methods of implementations:
- Method 1: We don't verify the token
- Method 2: We verify the token
Method 1: (We don't verify the token)
// HOC/withAuth.jsx
import { useRouter } from "next/router";
const withAuth = (WrappedComponent) => {
return (props) => {
// checks whether we are on client / browser or server.
if (typeof window !== "undefined") {
const Router = useRouter();
const accessToken = localStorage.getItem("accessToken");
// If there is no access token we redirect to "/" page.
if (!accessToken) {
Router.replace("/");
return null;
}
// If this is an accessToken we just render the component that was passed with all its props
return <WrappedComponent {...props} />;
}
// If we are on server, return null
return null;
};
};
export default withAuth;
Method 2: We need to verify the token.
// HOC/withAuth.jsx
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
// the below function could be any of your custom implementation for verifying the token. I've added it as means of explanantion
import verifyToken from "services/verifyToken";
const withAuth = (WrappedComponent) => {
return (props) => {
const Router = useRouter();
const [verified, setVerified] = useState(false);
useEffect(async () => {
const accessToken = localStorage.getItem("accessToken");
// if no accessToken was found,then we redirect to "/" page.
if (!accessToken) {
Router.replace("/");
} else {
// we call the api that verifies the token.
const data = await verifyToken(accessToken);
// if token was verified we set the state.
if (data.verified) {
setVerified(data.verified);
} else {
// If the token was fraud we first remove it from localStorage and then redirect to "/"
localStorage.removeItem("accessToken");
Router.replace("/");
}
}
}, []);
if (verified) {
return <WrappedComponent {...props} />;
} else {
return null;
}
};
};
export default withAuth;
Footer
Wasn't that easy!
I hope this blog helped you. If you got any queries or feedback then let me know 😀
Top comments (35)
Thank you for the post. however you might need to check this
if (typeof window === "undefined") {....}
I think it should be if (typeof window !== "undefined") {...}
because you are checking to know if you are on the browser.
Yes, I think that was a mistake by him but overall this post is good and it helped me a lot. I'm sure this post will keep helping a lot of people later on.
Thank you Kevin! Glad it helped you
yes, that was a mistake. Thanks for notifying it, I'll fix it
Thanks a lot Shubham!
return (props) => { ... }
What is purpose of "props"? Thanks for your article 😘
props is a object that would contain a react component in the form of an object.
So those props are again passed in the
<WrappedComp />
You can try
console.log(MyReactComponent())
to see the props!Thanks a lot for this post its very helpful.
i am facing an issue when wrapping my page withAuth, it loads the page then redirects to "/" or whatever my fallback is, is there a way to prevent that from happening.
You could try removing the Router.replace("/"), by returning some JSX like "Verifyiing" or something
Thank you so much! I was looking for a way to do protected routes for so long!
Glad it helped!
Edit: This won't works
Thanks for the article. I'm using
" next ":" ^ 12.0.4 ",
and I don't have access to localStorage or cookies, do you have any idea what that might be?localStorage is in the browser. Every modern browser has local storage
Will not work with a page which has getInitialProps or getServerSideProps()
I havent tried but thanks for notifying
Hey, Can you explain why and any solution for this issue?
Very helpful 👏
Thanks, this was a major save. I have one issue though, when I navigate through the protected routes, there seems to be a short flash before content apears using the verify token.
Glad it saved you!
yes those flashed are actually the useEffect running on mount and until then there nothing to render.
You can maybe add Loading components or something to indicate that the token is getting verified
Oh. Guessed as much. Thanks for your prompt response. You're awesome!