6

Front-End: [Axios]

  const formSubmit = async (e) => {
    e.preventDefault()
    const formData = new FormData(e.target)
    const email = formData.get('email')
    const password = formData.get('password')
    try {
      const res = await axios.post('http://172.16.2.19:3001/api/v1/auth/login', {
        email,
        password,
      })
      console.log(res.data) // its okay, I can login if email & password are correct.
    } catch (error) {
      console.log(error)
    }
  }

Back-End [Nodejs ExpressJs]:

Inside App.js:

const cors = require('cors')
app.use(cors({ credentials: true }))

Inside Login.js (/auth/login endpoint):

// ... code, then... if email & password are correct:
// 3600000ms = 1hour
res.cookie('jwt', token, { httpOnly: true, expires: new Date(Date.now() + 3600000 })
res.status(200).json({
    status: 'success'
    token,
    data: userDoc,
})

Then, when I login in my browser:

enter image description here

I can login successfully, but no cookies will be created, see:

enter image description here

  • The front-end http service (react app) is running on http://172.16.2.19:3000
  • The back-end http service (expressjs) is running on http://172.16.2.19:3001
  • The axios requests I'm sending from the front-end are requesting: http://172.16.2.19:3001

So what's the problem?

The problem that no cookies are getting created in the browser is preventing me from continuing to design the front-end application, because if I wanted to request anything from my API, I have to be authenticated, all the routes on the API I made are protected, so if I wanted to request anything from the API, I will have to send my jwt token along with the request.

edit **:

here's the response from the /auth/login endpoint after successfully logging in:

enter image description here

  • I am using brave browser, the latest version.
  • I tried this on firefox, it has the same behavior.

enter image description here

4
  • In your frontend axios call you need to set withCredentials: true or the Set-Cookie header is ignored. further reading
    – user5734311
    Commented May 3, 2022 at 21:54
  • @ChrisG, Setting this on the post results in the following error: ibb.co/RgS6x5Q --> ibb.co/9q7kfCS ___ (Access to XMLHttpRequest at '172.16.2.19:3001/api/v1/auth/login' from origin '172.16.2.19:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.)
    – Normal
    Commented May 3, 2022 at 21:59
  • @ChrisG, Ok, I tried to set this and tried to test it with firefox, firefox gave me a cleaner error: (screenshot: ibb.co/ynZpcQY ) ________ Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at ‘172.16.2.19:3001/api/v1/auth/login’. (Reason: Credential is not supported if the CORS header ‘Access-Control-Allow-Origin’ is ‘*’).
    – Normal
    Commented May 3, 2022 at 22:18
  • You found it out yourself but the message is pretty clear: you need to set the specific origin server-side, i.e. the hostname at which your client is served. It should also be noted that a deployed react app is usually served by the node backend, which renders the CORS setup moot, and until then you can use the proxy setting of your React's package json to redirect API calls, which also means you no longer require cors during development
    – user5734311
    Commented May 3, 2022 at 23:00

4 Answers 4

5

let me save you time:

For anyone having the same problem, all you have to do is

change your backend code from:

const cors = require('cors')
app.use(cors({ credentials: true }))

to

app.use(cors({ credentials: true, origin: true }))

and make sure you're using withCredentials: true on the front-end with every request (the login POST method and all the other requests that requires authentication)

why?

setting origin property to true is going to reflect the request origin, the origin property can be a string if you wanted to specify a particular domain, ex: http://localhost:3000. But if you have more than one client, setting this to true is a wise choise.

and for those of you wondering about mobile devices in case of specifying a string for the origin field with one particular domain. This problem of cors only happens in browsers, any other client doesn't use that CORS policy.

0
1

Just in case someone else runs into this issue, if your client and server are on different domains, you should also set sameSite: 'none' and secure: true

https://web.dev/samesite-cookies-explained/

0

I would check by passing {withCredentials: true} as the third argument to the axios method to allow the browser to set the cookie via the request.

4
  • 1
    Setting this on the post results in the following error: ibb.co/RgS6x5Q --> ibb.co/9q7kfCS ___ (Access to XMLHttpRequest at '172.16.2.19:3001/api/v1/auth/login' from origin '172.16.2.19:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.)
    – Normal
    Commented May 3, 2022 at 21:57
  • Are you sure that your browser is not blocking the cookies due to their setting?
    – Sathya
    Commented May 3, 2022 at 22:01
  • I'm not sure, but I tried it on firefox, it behaved the same way
    – Normal
    Commented May 3, 2022 at 22:03
  • Ok, I tried to set this and tried to test it with firefox, firefox gave me a cleaner error: (screenshot: ibb.co/ynZpcQY ) ________ Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at ‘172.16.2.19:3001/api/v1/auth/login’. (Reason: Credential is not supported if the CORS header ‘Access-Control-Allow-Origin’ is ‘*’).
    – Normal
    Commented May 3, 2022 at 22:15
0

I don't think it is correct to use the backend to save cookies, as cookies is a browser feature separate from the database. I might be wrong though. When the post is successful, res will return a token. You save this token in the browser's local storage.

const formSubmit = async (e) => {
e.preventDefault()
const formData = new FormData(e.target)
const email = formData.get('email')
const password = formData.get('password')
try {
  const res = await axios.post('http://172.16.2.19:3001/api/v1/auth/login', {
    email,
    password,
  })
  //browsers local storage
  
  localStorage.setItem('access_token',res.data.access);
  localStorage.setItem('refresh_token',res.data.refresh);
  console.log(res.data) // its okay, I can login if email & password are correct.
}

You will then have to create an authorization header as such

headers:{
        Authorization: localStorage.getItem('access_token') 
            ? 'JWT '+localStorage.getItem('access_token')
            : null

    }
2
  • 2
    I thought about that as a way to get rid of this disaster completely, but the reason we set the httpOnly flag on cookies is because we don't want the front-end of the browser to be able to change the cookie information (this is how it should be). Also I have seen hundreds of billions of people doing it this way, so it's not fair they can do it, but not me. Also I'll have many clients who are going to use this API, so it will be a headache to do this on every client
    – Normal
    Commented May 3, 2022 at 22:11
  • Cookies are created by the browser and the server sends instructions via Set-cookie header on how the cookie will be created including restrictions. Commented Nov 17, 2022 at 11:25

Not the answer you're looking for? Browse other questions tagged or ask your own question.