Cannot send httponly cookie to express server from react js - node.js

I am working on a react app which receives httponly cookie from third party website.
I am able to see the cookie in Chrome developer console.
I am trying to send this cookie to backend of my app, built in expressjs, so that I can read the cookie. I am using fetch to send a GET request to the app while including the prop below:
Credentials: 'include'
In the express server, am allowing my front-end inside CORS and also
set credentials equal to true.
Issue:
In request header of my express server, I can't see the httponly cookie.
Can anyone guide me how can I send httponly and get it inside express server?

On client you must enable credentials as well. There is axios module to make requests with credentials. Example of usage:
import axios from 'axios'
const instance = axios.create({
withCredentials: true,
baseURL: API_SERVER
})
instance.get('todos')
In other way, you could provide cookie with XMLHttpRequest:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/', true);
xhr.withCredentials = true;
xhr.send(null);
XMLHttpRequest

The first thing is allow our Express application to be able to receive requests from the host on which the Fetch API makes the calls in our situation it makes them from https://localhost:8080
const express = require('express');
const cors = require('cors');
app.use(cors({
origin: 'http://localhost:8080',
credentials: true
}));
The last thing is to create a fetch request via the Fetch API from [https://localhost:8080] to [http://localhost:9090/example]:
fetch('http://localhost:9090/example',{
method: ‘GET’,
credentials: 'include'
});
And now no matter that we made the request from another host ,we receive the cookies

Related

Browser receives httponly cookie but does not store it on React frontend

I have a React-App with a Node and Express backend (both are running locally and on different ports) and I'm trying to implement JWT authentication/authorization in my application. I send the accessToken and refreshToken, at the time of login, and store the refreshToken as an httponly cookie. Here is the code used to achieve that at the backend:
const {accessToken, refreshToken} = await user.generateAuthToken()
res.cookie('jwt', refreshToken, {httpOnly: true,maxAge: 24*60*60*1000})
res.json({accessToken,user})
This is pretty straightforward and this flow works on postman by the way. Postman receives the refreshToken and stores it as an httpCookie thus allowing me to refresh a Token whenever my access Token expires.
However, I've been trying to achieve the same thing on React frontend for days but am unable to do so.
Now I understand that Cross Origin requests are not allowed to set cookies but I'm actually doing the following using Cors:
app.use(cors({
credentials: true,
allowedHeaders: ['Content-Type', 'Authorization'],
origin: ['http://localhost:3000', 'http://localhost:5000']
}));
On the frontend, this is how I make the login request:
const loginUser = await axios.post('/users/login',body,{withCredentials: true})
As far as I understand, this should work and I know that refresh token is received as a cookie when I login (by observing the network tab in the developer tools) so the problem is that it is not being stored on the browser. I've tried almost all the solutions given previously here on stackoverflow but none of them worked for me.
For anyone still suffering this issue, turns out the problem was with axios.post. I removed {withCredintials: true} config option from the request and just added
axios.defaults.withCredentials = true;

HTTP request working from Postman and Node but not React

There are a few questions similar to this on Stack Overflow, and none of the proposed solutions worked, so I'll walk through the case and what I've tried.
I have a server application hosted on Cloud Run, which can only be accessed with the appropriate Bearer token in the request Authorization header. I've tried accessing it via Postman and an Axios request from a local Nodejs server, with the Authorization header, and it worked fine. With React (create-react-app specifically), I get the following error: Access to XMLHttpRequest at 'https://myserver-lhp5a9xp5a-ue.a.run.app/api/rules' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
On the server side, I get the 403 error that Cloud Run gives when the incorrect Authorization token is passed. Also, when I allow unauthenticated access from the Cloud Run side (so remove the need for an Authorization header), the request works fine, so it looks like this is indeed an issue with the Authorization header and not CORS.
In addition, I'm handling CORS on the server side. Here's my server-side code:
var express = require('express');
var router = express.Router();
const cors = require('cors');
router.options('/api/rules', cors());
router.get('/api/rules', cors(), (req, res, next) => {
res.status(200).send()
});
Here's my React code:
const axiosInstance = axios.create({
baseURL: process.env.REACT_APP_API_BASE_URL
});
const buttonClickHandler = async (event) => {
const resp = await axiosInstance.get('/api/rules'
, {
headers: {
'Authorization': 'Bearer eyJhbGciOiJSUzI1NiIsImtpZ...' // I used this token within the same minute when trying the request via Postman or from my Nodejs app, so a token expiry isn't the issue.
}
}
)
console.log(resp.data)
}
Here's what I tried so far:
Using fetch instead of axios - same error
Using the same token, within the same 5 seconds, to send the request from Postman or a Nodejs server - it worked fine.
Using an axios interceptor to set the Authorization - same error
Removing the single quotes around Authorization - same error
Sending the request to my Nodejs server instead and doing a console.log of the header to make sure the Authorization token is being passed correctly (it is)
Not using an an axios instance but spelling out the full URL in the request - same error
Trying a different endpoint on my Cloud Run server - same error
Deploying my React app to be served from a https endpoint and sending the request from there - same error
Adding Accept: '*/*' to the headers
Adding 'Accept': '*/*' to the headers
Adding 'Content-Type': 'application/json' to the headers
All combinations of the three above points
I found the answer after some digging, thanks #aniket-kolekar for pointing me in the right direction.
When Postman or a Nodejs server query an endpoint like GET, POST, PUT, DELETE, they send the call without checking the OPTIONS first. Create-React-App does.
The service I was querying is hosted on Cloud Run and doesn't allow unauthenticated invocations. So while I was including the authorization header to make my GET call, it wasn't being included in the pre-flight OPTIONS call. In fact, CORS prevents auth headers from being included in an OPTIONS call.
A Cloud Run PM replied in this post that this is a known issue with Cloud Run. The way I'll get around it for now is to host two services on Cloud Run - one that doesn't require authentication, and effectively acts as a proxy server to route calls from the client service to the shielded server service.
TLDR;
CORS is a mechanism built into the web browser. It’s not a UI code issue.
To fix CORS problems, you need to make changes on the API (server) side.
Here is the behind the scenes working:
Browser: Sends OPTIONS call to check the server type and getting the headers before sending any new request to the API endpoint. Where it checks for Access-Control-Allow-Origin. Taking this into account Access-Control-Allow-Origin header just specifies which all CROSS ORIGINS are allowed, although by default browser will only allow the same origin.
Postman: Sends direct GET, POST, PUT, DELETE etc. request without checking what type of server is and getting the header Access-Control-Allow-Origin by using OPTIONS call to the server.
You will have to configure Access-Control-Allow-Origin header in your server to resolve the CORS issue.

How to solve node.JS passport oauth2 cors error

I am trying to configure node passport oauth2 login.
If call the get url directly from the browswer, everyhting works perfectly.
I am trying to set up the login using react client as frontend.
react axios api call
const res = await Axios.get(http://locahost:5000/login`)
node.js express
app.use(cors({
'allowedHeaders': ['sessionId', 'Content-Type', 'authorization'],
'exposedHeaders': ['sessionId','authorization'],
// 'origin': true,
// 'Access-Control-Allow-Origin': 'http://localhost:8080',
'methods': 'GET,HEAD,PUT,PATCH,POST,DELETE, OPTIONS',
'preflightContinue': false
}));
app.use(bodyParser.json({ limit: "50mb", extended: true }));
app.get('/npt/login', passport.authenticate('oauth2'));
This is the error I get
Access to XMLHttpRequest at 'https://...' (redirected from 'http://localhost:5000/npt/login') from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Do I need to set the headers in the client app?
UPDATE:
I am trying to use this configuration using Azure App Registration.
I have updated the express CORS settings.
UPDATE2:
I have managed to solve the issue running the frontend on the same origin as the nodeJS express server.
This is the example I have followed: https://github.com/cicorias/react-azuread-passport
if you run react side via CRA
you can add proxy in package.json file
like this :
"proxy": "http://yourTarget.com",
and call http requests from
http://localhost:3000
notice : when you change package.json file you must reset npm start command to see the changes

Axios not receiving cookies in Heroku deployed MERN application

I have a Node/Express server deployed on Heroku that I am using to send httpOnly cookies to store JWTs. I have been using Axios to set the cookies while hosting my React app on port 3000 and the Express server on port 5000.
Axios Config in React App
const myAxios = axios.create({
withCredentials: true,
baseURL: "http://localhost:5000/",
});
index.js of Express App
app.use(cors({ origin: true, credentials: true }));
This has been working fine until I deployed my app to Heroku and switched my Axios config to use baseURL:http://app-name.herokuapp.com. I am now no longer receiving cookies (for both GET and POST requests). I still get the cookies if I visit http://app-name.herokuapp.com/get-cookie-endpoint directly in Chrome or in Postman. However, when I try calling myAxios.get('/get-cookie-endpoint) from my React app on localhost:3000, I no longer receive the cookie. I inspected the XHR request and I am still getting a Set-Cookie response header.
Any ideas? I have experimented with many different Axios and CORS settings but nothing seems to work.
Try adding sameSite = 'none'; in cookie object. I was having this issue and it worked for me.

Vue-cookies with Firebase and Heroku, not being sent

I have a Vue.js project deployed on firebase and a node-express app deployed on Heroku. Now I want to send cookies along with each request to the server using Axios. I am using Axios and cookies are being set using vue-cookies (which are of sameSite: none and secure: true attributes).
In localhost, I can see the cookies in each request in my backend and can access them using req.cookies.session. (The session is my cookie name that is saved on the client-side.)
But in production, I can't see the cookies in the request. What am I doing wrong?
node-express cors
app.use(cors({
credentials: true,
origin: 'https://paid-kickstartu-webapp.web.app',
'Access-Control-Allow-Origin': '*',
}));
Also attaching my screenshots of both Axios configuration and node-express backend for more understanding. Everything is working but cookies are not being sent in the backend from the frontend. In localhost both work as required.
Try this
If you are using Firebase Hosting + Cloud Functions, __session is the only cookie you can store, by design. This is necessary for us to be able to efficiently cache content on the CDN -- we strip all cookies from the request other than __session. This should be documented but doesn't appear to be (oops!). We'll update documentation to reflect this limitation.
Also, you need to set Cache-Control Header as private
res.setHeader('Cache-Control', 'private');
Thank you all for helping. I have solved this problem, what I was doing before was getting the cookie in res body and saving the cookie on the client-side using vue-cookie, So any call to the backend was showing me empty cookies. But now I am setting the cookie header from my backend (node-express) during login and now when I send any further request's I can see the previous cookies that were set in my headers during login.

Resources