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.
useEffect
hook so it only renders on the client and not the server. – WendiwendieAuthorization
header, the problem is that you have no way to set theAuthorization
header when the user access a page (+ it is a bit less secure because malicious JS code may access it in some scenarios). I see this localStorage patterns very often (and used it a lot in the past) but it's not the best approach. This pattern is meant for REST API calls from the browser but not to secure web app pages, cookies are better for that. – Klecka