Setting the cookies using the JS, axios and expressJS
Tags: javascript, webdev, tutorial, authjs
Published: 2024-03-02 13:34:46

Hi all
recently I am learning about the auth.
I tried setting the accessToken and refreshToken for the auth.

Initially decided to use fetch for it. It did not work in my case. Exhausted all the combination of credentials.

```JSON
credentials:true,
credentials:include,
etc.
```

I was really pissed and sad for like 3-4 days.

Finally decided to switch to axios also made some adjustment to my backend and it started working.

Let me show you my code.

My axios request

```JS
 const loginCall = () => {
// You may get these data from the relevant selector
let data= {
        email: "dummy@mail.com", 
        password: "123",
      }
    const options = {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      withCredentials: true, // Ensure Axios includes cookies in the request
    };

    axios("http://localhost:8002/auth/login", data,options)
      .then((response) => {
        // Check if response status is in the 200 range
        if (response.status >= 200 && response.status < 300) {
          console.log("response is: ", response);

          // Print all headers from response
          console.log("headers are: ");
          console.log(response.headers);

          let responseData = response.data;
          console.log(responseData);

          let accessToken = responseData.accessToken;
          console.log("access token is: ", accessToken);

          // Set the accessToken cookie correctly
          document.cookie = `accessToken=${accessToken}; path=/; SameSite=Strict; expires=${new Date(
            Date.now() + 1000 * 60 * 5
          ).toUTCString()}`;
          console.log("cookie is: ", document.cookie);

          // The refreshToken HttpOnly cookie will be automatically stored by the browser
        } else {
          throw new Error("Network response was not ok");
        }
      })
      .catch((err) => console.error(err));
  };
```

Put this in your index.js/server.js

```JS
const express = require("express");
const app = express();
let cors = require("cors");
app.use(
  cors({
    origin: "localhost",
    credentials: true,
  })
);
```

To add multiple domains do this.
 `origin: ["http://localhost:3000", "http://example.com", "https://subdomain.example.com"]`


now the below code in your 

```JS
let express = require("express");
let bcrypt = require("bcrypt");
let jwt = require("jsonwebtoken");
let User = require("../models/user-model");

// Login Route with JWT
router.post("/login", async (req, res) => {
  try {
    const { email, password } = req.body;

    // Check if user exists
    const user = await User.findOne({ email });

    if (!user) {
      return res
        .status(401)
        .json({ message: "Authentication failed, email not found" });
    }

    // Check password
    // @ts-ignore
    const isMatch = await bcrypt.compare(password, user.password);

    if (!isMatch) {
      return res
        .status(401)
        .json({ message: "Authentication failed, wrong password" });
    }

    // Generate JWT token with expiration time of 1 month (in seconds)
    // jwt.sign({ id user._id, exp Math.floor(Date.now() / 1000) + (60 * 60 * 24 * 30) }, SECRET);
    //@ts-ignore
    const { accessToken, refreshToken } = signToken(user);

    // httpOnly token way for the deployment
    // res.cookie("accessToken", accessToken, {
    //   httpOnly: false,
    //   sameSite: "none",
    // });
    const expires = new Date(Date.now() + 60 * 60 * 1000);
    res.cookie("refreshToken", refreshToken, {
      httpOnly: true,
      expires: expires,
      sameSite: "none",
    });

    res
      .status(201)
      .json({ message: "User created successfully", accessToken: accessToken });
  } catch (error) {
    console.log(error);
    res.status(500).json({ message: "Server Error" });
  }
});
```

Pardon me for a lot of commented code. 

I have removed some yet some remains.

So for me this code worked in the firefox browser

```JS
    const expires = new Date(Date.now() + 60 * 60 * 1000);
    res.cookie("refreshToken", refreshToken, {
      httpOnly: true,
      expires: expires,
      sameSite: "none",
    });
   res.send("refresh Token sent")
```

For my friend using Brave browser the below code worked.

```JS
const expires = new Date(Date.now() + 60 * 60 * 1000);
    return res
      .status(200)
      .cookie("refreshToken", refreshToken, {
        path: '/',
        httpOnly: true,
        secure: true,
        expires: expires,
        sameSite: "none",
        domain: "localhost",
      })
      .json({
        accessToken: accessToken,
        id: user._id,
        role: user.role,
        firstname: user.firstname,
        lastname: user.lastname,
        email: user.email,
        message: "User logged In Successfully",
    });
```

Now let me explain you these settings
First of all you must add the origin and credentials. Its a must. Else your tokens won't be sent or set.

```JS
const expires = new Date(Date.now() + 60 * 60 * 1000);
    res.cookie("refreshToken", refreshToken, {
        path: '/',
        httpOnly: true,
        secure: true,
        expires: expires,
        sameSite: "none",
        domain: "localhost",
      }
```

Now these cookie settings.

`path: /` This means the cookie is valid across all the path.
If you want this to be valid to certain parts only say  
`/protected`


`httpOnly:true` This option is very crucial here. It tells the browser don't allow any JS touch it. Which means its totally secure. This is protected from the **Cross Origin Attack**

`secure:true` This option means that browser will only send this cookie over https connection not http. 
My reccomendation is to make a variable named cookieOptions or refreshTokenOptions. 

Try setting the `secure:true` in the production environment.
As you will go nuts when your backend on local system does not 
get the cookies. As almost all of us have our local working on http.


```JS
let cookieOptions = {
    httpOnly: true,
    sameSite: "none",
  }

if(process.env.NODE_ENVIRONMENT=="production")
  cookieOptions.secure= true;
```

```JS
const expires = new Date(Date.now() + 60 * 60 * 1000);
expires: expires,
```
This sets the expiry time for the cookie to 1 hour.  
You can set the time as per your choice.  
My personal reccomendations are 3hour to a day.  
Not more than that.

Also the time has to be given in miliseconds.
So to set 1 hour = 60mins X 60sec X 1000 milisec

`sameSite: "none"` is used to send the cookies cross-site requests,  
which basically means your backend and client can be on different ports and even different addresses.

`domain: "localhost"` means its valid for only for localhost domain.
You may want to have multiple domains do it like this.

```JS
    domain: ["localhost", "example.com", ".subdomain.example.com"]```