Related
I am developing app as fullstack, with architecture of frontend and backend in separate machine on production. I am using native php as web app server (frontend) and nodejs+expressjs as api server. I set up CORS to give extra security of cookies management on client. It has been tested successfully during development (same machine), but when I tried to test on production env, the request was blocked by client browser. There was no error shown by browser or api's server console (means request hasn't reached api server). But on specific developer based browser, it showed error that request blocked because of different domain. Here's my machine ip config during production test:
web app server using ip : http://192.168.1.200:3000 (non https)
api server using domain : https://apihost.mycompany.com , with endpoint https://apihost.mycompany.com/auth/info for testing.
I am using nginx as api server engine and proxy_pass to nodejs-express service run on same machine. Maybe this request isn't forwarded to nodejs from nginx.
And here's the code:
// WEB APP SERVER - jquery ajax request resides on php page
// // runs on machine http://192.168.1.200:3000
function getUserInfo(){
$.ajax({
type: "GET",
url: `https://apihost.mycompany.com/auth/info`,
xhrFields: { withCredentials: true },
dataType: "json",
success: function(response) {
// show the page
},
error: function(response){
// show error message here
},
});
}
// API'S SERVER express page on main page with CORS included
// runs on machine https://apihost.mycompany.com
const whiteListOrigin = ['https://livefrontend.server.com','http://192.168.1.200:3000'];
const whiteListReqMethods = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'];
var corsOptions = {
origin: function (origin, callback) {
if (whiteListOrigin.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
},
methods: whiteListReqMethods,
allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'],
credentials: true,
maxAge: 600,
exposedHeaders: ['*', 'Authorization' ],
preflightContinue: true,
optionsSuccessStatus: 204
};
app.options("*", function(req, res, next){
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Credentials', "true");
res.header('Access-Control-Allow-Methods', req.method);
res.header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Length, Content-Type, Accept, Authorization, csrf-token');
res.send(200);
});
app.use(cors(corsOptions));
// API'S SERVER - controller to send back the respond header
// runs on machine https://apihost.mycompany.com with end point GET-https://apihost.mycompany.com/auth/info
const whiteListOrigin = ['https://livefrontend.server.com','http://192.168.1.200:3000'];
const allowedOrigin = whiteListOrigin.includes(req.headers.origin)?reqOrigin:whiteListOrigin[0];
const whiteListReqMethods = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'];
const allowedReqMethod = whiteListReqMethods.includes(req.method)?reqMethod:whiteListReqMethods[0];
res
.status(statusCode)
.header("Access-Control-Allow-Origin", allowedOrigin)
.header("Access-Control-Allow-Credentials", "true")
.header("Access-Control-Allow-Methods", allowedReqMethod)
.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, csrf-token")
.cookie('token', token, options)
.json({
success: true,
token
});
Error message by Developer's Based Browser:
Error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://apihost.mycompany.com/auth/info. (Reason: CORS request did not succeed). Status code: (null).
How can I solve this?
All errors I got on browser:
I have a Koa and GraphQL API with an authentication resolver which adds sets an auth token to the response cookies.
My API runs on http://localhost:3000 and my front end runs on http://localhost:3001. In order to make this work, I needed to add withCredentials flag to my API request as well as configure my Koa/Cors options with { origin: false, credentials: true }. Without these settings I would get a CORS error which I find confusing because they are both on localhost so technically the same domain...?
There will be multiple front ends using this API and will need to authenticate and get the auth token set in the cookie. When I spoof my domain name as http://app1.dev & http://app2.dev, both calling the API on http://localhost:3000, the auth token gets set in the response cookies but the browser does not store the cookie like it did when everything was localhost.
On my API, I am using #koa/cors for my configuring my cors and then set the following:
app.use(cors({ origin: false, credentials: true }))
On my front end react app, I have a request function which I use for all my GraphQL queries:
const request = query => {
const request = axios.create({
baseURL: apiUrl,
headers: {'X-apikey': apiKey},
withCredentials: true,
});
return request.post('/graphql', { query })
.then(res => res.data)
.catch(console.error)
}
Why are the cookies not getting set in the browser?
EDIT: Updated my axios request to create instance of axios and attempting to set withCredentials. I am still not seeing the credentials param in my request in dev tools though. I suspect this could be the issue?
I am sending requests from the client to my Express.js server using Axios.
I set a cookie on the client and I want to read that cookie from all Axios requests without adding them manually to request by hand.
This is my clientside request example:
axios.get(`some api url`).then(response => ...
I tried to access headers or cookies by using these properties in my Express.js server:
req.headers
req.cookies
Neither of them contained any cookies. I am using cookie parser middleware:
app.use(cookieParser())
How do I make Axios send cookies in requests automatically?
Edit:
I set cookie on the client like this:
import cookieClient from 'react-cookie'
...
let cookie = cookieClient.load('cookie-name')
if(cookie === undefined){
axios.get('path/to/my/cookie/api').then(response => {
if(response.status == 200){
cookieClient.save('cookie-name', response.data, {path:'/'})
}
})
}
...
While it's also using Axios, it is not relevant to the question. I simply want to embed cookies into all my requests once a cookie is set.
You can use withCredentials property.
XMLHttpRequest from a different domain cannot set cookie values for their own domain unless withCredentials is set to true before making the request.
axios.get(BASE_URL + '/todos', { withCredentials: true });
Also its possible to force credentials to every Axios requests
axios.defaults.withCredentials = true
Or using credentials for some of the Axios requests as the following code
const instance = axios.create({
withCredentials: true,
baseURL: BASE_URL
})
instance.get('/todos')
TL;DR:
{ withCredentials: true } or axios.defaults.withCredentials = true
From the axios documentation
withCredentials: false, // default
withCredentials indicates whether or not cross-site Access-Control requests should be made using credentials
If you pass { withCredentials: true } with your request it should work.
A better way would be setting withCredentials as true in axios.defaults
axios.defaults.withCredentials = true
It's also important to set the necessary headers in the express response. These are those which worked for me:
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', yourExactHostname);
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
I am not familiar with Axios, but as far as I know in javascript and ajax there is an option
withCredentials: true
This will automatically send the cookie to the client-side. As an example, this scenario is also generated with passportjs, which sets a cookie on the server
So I had this exact same issue and lost about 6 hours of my life searching, I had the
withCredentials: true
But the browser still didn't save the cookie until for some weird reason I had the idea to shuffle the configuration setting:
Axios.post(GlobalVariables.API_URL + 'api/login', {
email,
password,
honeyPot
}, {
withCredentials: true,
headers: {'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json'
}});
Seems like you should always send the 'withCredentials' Key first.
You can use withCredentials property to pass cookies in the request.
axios.get(`api_url`, { withCredentials: true })
By setting { withCredentials: true } you may encounter cross origin issue. To solve that
you need to use
expressApp.use(cors({ credentials: true, origin: "http://localhost:8080" }));
Here you can read about withCredentials
What worked for me:
Client Side:
import axios from 'axios';
const url = 'http://127.0.0.1:5000/api/v1';
export default {
login(credentials) {
return axios
.post(`${url}/users/login/`, credentials, {
withCredentials: true,
credentials: 'include',
})
.then((response) => response.data);
},
};
Note: Credentials will be the body of the post request, in this case the user login information (Normally obtained from the login form):
{
"email": "user#email.com",
"password": "userpassword"
}
Server Side:
const express = require('express');
const cors = require('cors');
const app = express();
const port = process.env.PORT || 5000;
app.use(
cors({
origin: [`http://localhost:${port}`, `https://localhost:${port}`],
credentials: 'true',
})
);
Fatih's answer is still valid and great in 2022.
Also axios.defaults.withCredentials = true will do the trick.
It seems passing { withCredentials: true } to individual axios calls is deprecated.
How do I make Axios send cookies in requests automatically?
set axios.defaults.withCredentials = true;
or for some specific request you can use axios.get(url,{withCredentials:true})
this will give CORS error if your 'Access-Control-Allow-Origin' is set to
wildcard(*).
Therefore make sure to specify the url of origin of your request
for ex: if your front-end which makes the request runs on localhost:3000 , then set the response header as
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
also set
res.setHeader('Access-Control-Allow-Credentials',true);
for people still not able to solve it, this answer helped me.
stackoverflow answer: 34558264
TLDR;
one needs to set {withCredentials: true} in both GET request as well the POST request (getting the cookie) for both axios as well as fetch.
Another solution is to use this library:
https://github.com/3846masa/axios-cookiejar-support
which integrates "Tough Cookie" support in to Axios. Note that this approach still requires the withCredentials flag.
After trying for 2 days long and after trying out from the suggestions here this is what worked for me.
express:
cors: cors({ origin: "http:127.0.0.1:3000", credentials: true, })
Cookie : Make sure your cookie has secure: true, sameSite: "None"
Frontend(React)
axios.defaults.withCredentials = true;
(withCredentials : true did not work for me) to the places where you request the cookie as well as to the place where you send the cookie (GET/POST)
Hope this helps others as well.
You are getting the two thinks mixed.
You have "react-cookie" and "axios"
react-cookie => is for handling the cookie on the client side
axios => is for sending ajax requests to the server
With that info, if you want the cookies from the client side to be communicated in the backend side as well, you will need to connect them together.
Note from "react-cookie" Readme:
Isomorphic cookies!
To be able to access user cookies while doing server-rendering, you
can use plugToRequest or setRawCookie.
link to readme
If this is what you need, great.
If not, please comment so I could elaborate more.
For anyone where none of these solutions are working, make sure that your request origin equals your request target, see this github issue.
I short, if you visit your website on 127.0.0.1:8000, then make sure that the requests you send are targeting your server on 127.0.0.1:8001 and not localhost:8001, although it might be the same target theoretically.
This worked for me:
First, I had to make a new instance of axios with a custom config
Then, I used that axios instance to make a post request
See code below:
const ax = axios.create({
baseURL: 'yourbaseUrl',
withCredentials: true,
});
const loginUser = () => { const body ={username:state.values.email, password:state.values.password};
ax.post('/login',body).then(function(response){
return response}).then().catch(error => console.log(error));}
source:
https://www.npmjs.com/package/axios#creating-an-instance
This won't apply to everyone, but I was using a React frontend with Vite and it was serving the localhost as 127.0.0.1:5173, which is what I put as the CORS allowable domain. As soon as I both to localhost everything worked as expected!
// use this while creating axios instance
const API = axios.create({
baseURL: "http://localhost:4000", // API URL
withCredentials: true,
});
// USE THIS MIDDLEWARE in app.js of backend
first, install cors npm i cors
var cors = require("cors"); // This should be at the end of all middlewares
const corsOptions = {
origin: "http://localhost:3000",
credentials: true, //access-control-allow-credentials:true
optionSuccessStatus: 200,
};
app.use(cors(corsOptions));
In my case, the problem was with the cookie, not with Axios; although I was receiving and sending the cookie from / to the same domain / subdomain / host, I was expecting it to work with different resources in different paths - but my coookie was acting like I had set it to a single Path, even though I omitted that attribute. Explicitly setting Path=/; in the cookie solved the issue.
Set the proxy in package.json(Frontend) and restart the server again (problem solved)
I am using Express.js server. With cookie-parser I have opened this endpoint
app.get("/s", (req,res) => {
res.cookie("bsaSession", req.session.id)
res.send("set cookie ok")
})
When I manually use the browser to http://localhost:5555/s where I have the website running the browser debug console shows that the cookie have been applied.
But when I use fetch API to do the equivalent, it does not set the cookie.
async trySetCookie()
{
await fetch("http://localhost:5555/s",{
method: 'GET',
credentials: 'same-origin'
})
}
Why?
I have found the solution. The core of this problem being that my button to trigger the fetch is on http://localhost:3000/. The server is on http://localhost:5555/ (I am simulating real environment on my own machine)
The problem is that this fetch call
async trySetCookie()
{
await fetch("http://localhost:5555/s",{
method: 'GET',
credentials: 'same-origin'
})
}
Without credentials, the browser cannot send or receive cookies via fetch (https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials)
With credentials as same-origin I can see the cookies coming from the server in the Set-Cookie response header, but nothing is being stored in the browser. One strange thing is that this response always have HttpOnly tagged after the cookie string regardless of my {httpOnly : true/false} settings on the server. In the case of manually using the browser to the page to do GET request, HttpOnly is being respected as usual, and the cookies are set.
So the solution is to set credentials as include to allow cross-origin cookie sending.
async trySetCookie()
{
await fetch("http://localhost:5555/s",{
method: 'GET',
credentials: 'include'
})
}
Also, on the server side you need to allow a particular origin manually with new headers:
app.get("/s", (req,res) => {
res.cookie("bsaSession", req.session.id, {httpOnly:false})
res.header('Access-Control-Allow-Origin', 'http://localhost:3000')
res.header('Access-Control-Allow-Credentials','true'
res.send("set")
})
Not doing this results in
XMLHttpRequest cannot load http://localhost:5555/s. Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.
But the cookie will be set regardless of this error. Still nice to include that header to silence the error.
If you are using cors middleware for Express it is even easier. You can just use these options
var corsOptions = {
origin: 'http://localhost:3000',
credentials: true
}
app.use(cors(corsOptions))
And of course credentials: 'include' is still required at the client side.
5argon's solution was great for me otherwise, but I had to set origin in express cors to true. So in backend:
app.use(
cors({
origin: true,
credentials: true,
})
);
And in fetch:
fetch("http://localhost:5555/s", {
method: 'GET',
credentials: 'include'
})
I use wavesurfer, I get the following error:
XMLHttpRequest cannot load https://audiotemp.domain.net/RE65bbf6f0a2760184ab08b3fbf9f1d249.mp3.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://my.domain.net:3000' is therefore not allowed access. The response had HTTP status code 403.
The call is loaded, but the wave wasn't drawn, I check the network of requests and I found two requests for this call as the following:
403 Forbidden.
304 Not Modified.
The code of loading the call as the following:
scope.wavesurfer.load(scope.url);
For the second image I find there's cookies send with the request as the following:
Cookie:__zlcmid=TAePb8mwejYLug; calltrk_referrer=https%3A//app.gotomeeting.com/%3FmeetingId%3D306279333; calltrk_landing=https%3A//www.dentalmarketing.net/capture/; calltrk_session_id_150722382=c16eaa33-386f-4ab3-ba8d-b3d0cff070ef; __utma=52313532.1896763581.1423186152.1427741816.1431536946.4; __utmz=52313532.1431536946.4.3.utmcsr=bigleap.com|utmccn=(referral)|utmcmd=referral|utmcct=/utahs-best-brightest/; _ga=GA1.2.1896763581.1423186152; CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly9hdWRpb3RlbXAuZGVudGFsbWFya2V0aW5nLm5ldC8qIiwiQ29uZGl0aW9uIjp7IkRhdGVMZXNzVGhhbiI6eyJBV1M6RXBvY2hUaW1lIjoxNDMzMDE2ODQ5fX19XX0_; CloudFront-Signature=btJ4dYPe3Cv87mQZzb6dkYVOLRcKQbscJ3h-ZJgSWGikNi1nXLuYXCGIwsHJWbhdTRiP8Gjru0mIQyOJdCioOa4tP3sAOSGXl9Cy1T2bM1sahgWZZ3GSk6GMyi21TVy3YsxDEdTUoMipeE0b5CduzcpcquB3hjYtfOUwI6CIrsTXkhajrGAk1rg~6tItPqMtxgmwrRM1oM8th0UgxgPWwVD2pok1ecS5ylwOiXbnSETpQzgXqS0C37bT94KpvafCjaclqgQPNcXrZRqbK~HLh28Gd4IZ3pDzIr3GNe3lkDUVIBYbStDsGZtawnS53ASmGXl3rP~DrPKYlahYX~ajKg__; CloudFront-Key-Pair-Id=APKAJL5DFWOODOOKTH2A
I put this cookies using Node.js Code as the following:
res.cookie('CloudFront-Policy',encodedCustomPolicy,{domain :cookieDomainName , path:'/', httpOnly:true,secure:true});
res.cookie('CloudFront-Signature',customPolicySignature,{domain :cookieDomainName , path:'/', httpOnly:true,secure:true});
res.cookie('CloudFront-Key-Pair-Id',cloudFrontKeyPairId,{domain :cookieDomainName , path:'/', httpOnly:true,secure:true}
So, I need to put three cookies on the first request, to get the call and draw the wave of it.
How can I send cookies with first request ?
How can I put header when I call load function of wavesurfer ?
I faced a similar problem trying to get the wavesurfer waveform to render via a CloudFront CDN link. I was receiving 403 Forbidden errors and the message:
Access to fetch at 'https://cdn.example.com/path/to/audio/file' from origin 'https://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled
Assuming you are generating a CloudFront policy and setting your cookies server-side, you need to both enable CORS and ensure that the CORS request that wavesurfer sends to retrieve your file uses the appropriate credentials (i.e. your CloudFront cookies).
Add the following to your node server file to enable sending CORS requests with cookies:
app.use(cors({ origin: "example.com", credentials: true }));
On the client, the big thing i missed was configuring the xhr object correctly on wavesurfer.create()
this.wavesurfer = WaveSurfer.create({
container: this.waveForm.current,
waveColor: "#D8D8D8",
progressColor: "#ED2784",
barRadius: 3,
cursorColor: "transparent",
responsive: true,
xhr: {
cache: "default",
mode: "cors",
method: "GET",
credentials: "include",
headers: [
{ key: "cache-control", value: "no-cache" },
{ key: "pragma", value: "no-cache" }
]
}
});
The mode: "GET" attribute indicates that that we are sending a cross-origin request that includes Access-Control headers. The credentials: "include" attribute tells wavesurfer to include the CloudFront cookies in the request.
You need to set CORS headers for static server. You can use cors lib.
var express = require('express');
var proxy = require('express-http-proxy');
var cors = require('cors');
var app = express();
// Enable CORS
app.use(cors({
exposedHeaders: ['Content-Length',
'Content-Type']
}));
// Serve static
app.use('/', express.static('public'));
// Proxy to media server
app.use('/media', proxy('http://%MEDIA_SERVER_ADDRESS%', {
forwardPath: function(req, res) {
return '/media';
}
}));
// Start server
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('Server listening at http://%s:%s', host, port);
});