Cookies not set when nodejs server is not on localhost - node.js

I have a ReactJS frontend and a NodeJS backend. The frontend calls the login API from NodeJS it returns a cookie with the JWT token which will be used for other calls. I've tested it and it works when both the frontend and backend are running on localhost.
I've used postman and called the same API. One running on localhost and one running on AWS EC2. Both returns the same response, however, the one running on EC2 doesn't set the cookie. set-cookie header is present on both.
I've included the cors configuration.
var corsOptions = {
origin: ['http://localhost:3001'],
credentials: true
}
app.use(cors(corsOptions));
FOUND THE PROBLEM:
Turns out safari was blocking the cookies. Chrome on iOS is affected by it too. Turning off the option "Prevent cross-site tracking" on safari preferences solved the issue

try to use the full link to send a request to API
axios.post('https://mysite/api/v1/login', {email,password,...})

Related

Response header Set-Cookie doesn't store cookies in browser

I'm setting 2 cookies in response from backend to the browser.
One that is secure HTTPOnly (it's refreshToken) and the other one without those parameters so it's accessible to JavaScript (carrying information about refreshToken is set and when it expires).
Cookies in response:
Neither of those two is set in browser. I studied all about cookies and I'll be honest, I'm lost here and need your help.
At first it was working very well on my localhost environment (backend on localhost:8080, frontend on localhost:3000).
Then I made deploy and there it was NOT working.
I tested again on localhost and I checked "Disable cache" to prevent unwanted behaviour and it is not working even there.
I'll mention that I'm using CORS, not sure if that can may interfere:
I tested this both in Chrome and Firefox.
I FINALY figured it out. Cookies are valid. The answer is to set withCredentials = true both in frontend and backend.
First I thought the parameter withCredentials on frontend is used only when we want to send our credentials hidden in secure HttpOnly cookies to which we don't have access to but browser does.
Apparently it is also used when we want to set cookies from response. Link to docs.
responses from a different domain cannot set cookie values for their own domain unless withCredentials is set to true before making the request
Secondly, we also have to add withCredentials on backend to CorsFilter (in Spring boot) otherwise we get CORS error.
CorsConfiguration()
.apply {
allowedOrigins = listOf(uiUrl)
allowedHeaders = listOf("*")
allowedMethods = listOf("*")
allowCredentials = 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.

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.

Specific Endpoint: Access Denied (no Access-Control-Allow-Origin header) on API Request in React/Redux App with Node

I cannot access a custom heroku API endpoint in a react/redux app. The heroku endpoint loads up fine in a browser, and I can see the network request coming back on my console, so it seems something in my app itself is preventing it from returning.
It's the classic 'No 'Access-Control-Allow-Origin' header is present on the requested resource' error, but what's perplexing is that I can use another endpoint (such as openweather's API) and don't get this error.
Do I need to use Express or other middleware to deal with Heroku specifically? I'm creating a store with ReduxPromise middleware right now and am using Axios to make the request. Or is there a server side configuration on Heroku that might change this?
Thanks for your help, pretty new to react/redux/node =)
This is a CORS issue, you need to add a cors configuration into your API request and accept the origins on the server side using access-control-allow-origin header and accepting the referrer url of the request.
These are my configuration options when using redux-api to make the requests, look at the documentation for your specific framework:
export const baseOptions = {
mode: 'cors',
credentials: 'include',
headers
}

Resources