Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HttpOnly cookies are not sent with request to server

I've created API in node express and I'm running it on port :8000, I am consuming APIs through simple CRA on port :3000. I've created registration and login with setting httpOnly cookie. Furthermore, I've put middleware to check each endpoint in order to verify if it has that token. When I test through Thunder/Postman everything works, after logging in I get the cookie in response, I set that cookie as auth token and make request to get data and I get the data.

When I log in through the React Frontend it succeeds and I can see in network tab that I have received the cookie in response. But when I make a request to protected endpoint, the request does not have a cookie in it (I log incoming requests on server and compare ones made with Thunder/Postman client and via app in Browser).

I use axios, and I've put {withCredentials: true} it doesn't work. I've used withAxios hook and it doesn't work either.

SERVER

index.js

...
const app = express()
app.use(cors({
    credentials: true,
    origin: 'http://localhost:3000',
}));
...

controllers/User.js

...
const loginUser = async(req, res) => {
    const body = req.body
    const user = await User.findOne({ email: body.email })
        if(user) {
        const token = generateToken(user)
        const userObject = {
            userId: user._id,
            userEmail: user.email,
            userRole: user.role
        }
        const validPassword = await bcrypt.compare(body.password, user.password)
        if(validPassword) {
            res.set('Access-Control-Allow-Origin', req.headers.origin);
            res.set('Access-Control-Allow-Credentials', 'true');
            res.set(
                'Access-Control-Expose-Headers',
                'date, etag, access-control-allow-origin, access-control-allow-credentials'
            )
            res.cookie('auth-token', token, {
            httpOnly: true,
            sameSite: 'strict'
            })
            res.status(200).json(userObject)
        } else {
            res.status(400).json({ error: "Invalid password" })
        }
    } else {
        res.status(401).json({ error: "User doesn't exist" })
    }
}
...

middleware.js

...
exports.verify = (req, res, next) => {
    const token = req.headers.authorization
    if(!token) res.status(403).json({ error: "please provide a token" })
    else {
        jwt.verify(token.split(" ")[1], tokenSecret, (err, value) => {
            if(err) res.status(500).json({error: "failed to authenticate token"})
            req.user = value.data
            next()
         })
    }
}
...

router.js

...
router.get('/bills', middleware.verify, getBills)

router.post('/login', loginUser)
...

CLIENT

src/components/LoginComponent.js

...
const loginUser = (e) => {
        setLoading(true)
        e.preventDefault()
        let payload = {email: email, password: password}
        axios.post('http://localhost:8000/login', payload).then(res => res.status === 200 
        ? (setLoading(false), navigate('/listbills')) : navigate('/register'))
    }
...

src/components/ListBills.js

...
useEffect(() => {
        fetch('http://localhost:8000/bills', {
            method: 'get',
            headers: {'Content-Type': 'application/json'}, 
            credentials: 'include',
        })
            .then(response => {console.log(response)}).catch(err => console.log(err));
    }, [])
...

I've also tried:

axios.get('http://localhost:8000/bills',{withCredentials: true})
  .then((data) => console.log(data))
  .then((result) => console.log(result))
  .catch((err) => console.log('[Control Error ] ', err))
    }

and

const [{ data, loading, error }, refetch] = useAxios(
  'http://localhost:8000/bills',{
  withCredentials: true,
  headers: {'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json'
}})

Console.log error:

enter image description here

After I login I get this in Network tab:

enter image description here

enter image description here

However when I want to access the list:

enter image description here

enter image description here

=== UPDATE ===

So the cause of the issue is not having the httpOnly cookie passed in the request header. This is the log of the middleware I am using:

token undefined
req headers auth undefined
req headers {
  host: 'localhost:8000',
  connection: 'keep-alive',
  'sec-ch-ua': '" Not;A Brand";v="99", "Google Chrome";v="97", "Chromium";v="97"',
  'sec-ch-ua-mobile': '?0',
  'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36',
  'sec-ch-ua-platform': '"macOS"',
  'content-type': 'application/json',
  accept: '*/*',
  origin: 'http://localhost:3000',
  'sec-fetch-site': 'same-site',
  'sec-fetch-mode': 'cors',
  'sec-fetch-dest': 'empty',
  referer: 'http://localhost:3000/',
  'accept-encoding': 'gzip, deflate, br',
  'accept-language': 'en-US,en;q=0.9,hr;q=0.8,sr;q=0.7,bs;q=0.6,de;q=0.5,fr;q=0.4,it;q=0.3'
}

token is read from headers.authorization but from the log of the headers it doesn't exist so my requests fail to be authorized.

Still not working.

like image 873
fedjedmedjed Avatar asked Nov 14 '25 21:11

fedjedmedjed


1 Answers

After reading everything on CORS and httpOnly cookies I've managed to get it working. First I removed sameSite and added domain prop according to documentation in controllers/User.js on SERVER

res.cookie('auth-token', token, {
    httpOnly: true,
    domain: 'http://localhost:3000'
})

Then I got a little yellow triangle in the console request view, it said that domain was invalid. Then I just changed domain to origin and the cookie appeared in the request log of the headers 🎉

res.cookie('auth-token', token, {
    httpOnly: true,
    origin: 'http://localhost:3000',
})

The cookie was not in the Authorization property of the headers but in the cookie so I had to change the code in the middleware.js since it expected format bearer xxyyzz but receiving auth-token=xxyyzz, it looks like this now:

exports.verify = (req, res, next) => {
    const token = req.headers.cookie
    if(!token) res.status(403).json({ error: "please provide a token" })
    else {
        jwt.verify(token.split("=")[1], tokenSecret, (err, value) => {
            if(err) res.status(500).json({error: "failed to authenticate token"})
            req.user = value.data
            next()
         })
    }
}
like image 51
fedjedmedjed Avatar answered Nov 17 '25 11:11

fedjedmedjed



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!