Search code examples
javascriptnode.jsmongoosenext.jsjwt

Why is token is undefined on client side?


I make an ecommerce site with nextjs and mongoose using a jwt token in a cookie, going client side in the console, the "application" parameter we can see the cookie which is stored with the name OursiteJWT and it has the value the token :

enter image description here

But after several tests we notice that when I retrieve the token it is undefined pages/orders.js :

import { useEffect, useState } from "react";
import axios from "axios";
import jwt from "jsonwebtoken";
import cookie from "cookie";

function Orders() {
  const [orders, setOrders] = useState([]);

  useEffect(() => {
    async function fetchOrders() {
      try {
        // Récupère le secret depuis l'API
        const secretRes = await axios.get("/api/secret");
        const secret = secretRes.data.secret;
        console.log(secret);
        // Récupère le token JWT depuis les cookies
        const cookies = cookie.parse(document.cookie);
        const token = cookies.OursiteJWT;
        console.log(token);
        // Si le token n'est pas présent, redirige vers la page de login
        if (!token) {
          console.log("pas de token")
          return;
        }

        // Vérifie le token JWT
        const decoded = jwt.verify(token, secret);
        const userId = decoded._id;

        // Récupère les commandes de l'utilisateur depuis l'API
        const ordersRes = await axios.get(`/api/orders?user_id=${userId}`);
        const orders = ordersRes.data;

        setOrders(orders);
      } catch (error) {
        console.error(error);
      }
    }

    fetchOrders();
  }, []);

  return (
    <div>
      {orders.map((order) => (
        <div key={order._id}>
          <h2>Commande {order._id}</h2>
          <p>Utilisateur: {order.name}</p>
          <p>Adresse: {order.address}</p>
          <p>Ville: {order.city}</p>
          <p>Produits:</p>
          <ul>
            {order.products.map((product) => (
              <li key={product._id}>{product.name}</li>
            ))}
          </ul>
        </div>
      ))}
    </div>
  );
}

export default Orders;

In the console I have "token not found" and the token value is undefined, the secret is correct.

This is how I stored the token :

api/login.js :

import {initMongoose} from "../../lib/mongoose";
import User from '../../models/User';
import { sign } from "jsonwebtoken";
import { serialize } from "cookie";



initMongoose()
const secret = process.env.SECRET;

export default async function handler(req,res){
  
  const {email,password}=req.body;
  const user = await User.findOne({email,password});
  
  if(!user){
    res.status(401).json({message: "Impossible de trouver un utilisateur"});
    return;
  }
  else{
    
    const token = sign(
      {
        exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 30, // 30 days
        email: email,
        _id: user._id,
      },
      secret
    );
    const serialised = serialize("OursiteJWT", token, {
      httpOnly: true,
      secure: process.env.NODE_ENV !== "development",
      sameSite: "strict",
      maxAge: 60 * 60 * 24 * 30,
      path: "/",
    });
    
    res.setHeader("Set-Cookie", serialised);
    res.status(200).json({message :"Success!"});
    
    
  }

}

Solution

  • The reason you are unable to retrieve it is because you are setting httpOnly: true on the cookie.

    Using the HttpOnly flag when generating a cookie helps mitigate the risk of client side script accessing the protected cookie (if the browser supports it).

    A cookie with the HttpOnly attribute is inaccessible to the JavaScript Document.cookie API; it's only sent to the server. For example, cookies that persist in server-side sessions don't need to be available to JavaScript and should have the HttpOnly attribute. This precaution helps mitigate cross-site scripting (XSS) attacks.

    see https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies