How to read custom header in CORS middleware - javascript

I have created CORS middleware using CORS package. This middleware will be called before each call. Here is my implementation.
const corsMiddleware = async (req, callback) => {
const { userid } = req.headers|| req.cookies {};
let whiteList = await getWhiteListDomains(userid)
return callback(null, {
origin: whiteList,
credentials: true,
allowedHeaders: ["userid", "authorization", "content-type"]
});
};
And added this middleware before route initialization as
app.use(cors(corsMiddleware));
app.options("*", cors(corsMiddleware));
app.get("/user", (req, res, next)=>{
// code
})
From Browser I am trying to call the API as
axios({ method: "get", url: "http://localhost:3000/user", headers: {userId:"1234"} });
While debugging on the server I see
access-control-request-headers:"userid"
in the headers of the request object.
I am not able to read the custom header. This might be happening because I am trying to read the custom header before CORS initialization. But still, I want to read that custom header.

You have mainly two problems in your code.
First one, and easier to solve is that you are missing access-control-allow-origin in the option that sets the Access-Control-Allow-Headers:
return callback(null, {
origin: whiteList,
credentials: true,
allowedHeaders: [
"access-control-allow-origin",
"authorization",
"content-type",
"userid"
]
});
The second one is the most important because it is related to how CORS works.
This problem you are having is that CORS is already rejecting the petition in the pre-flight OPTIONS request. It never allows the browser to execute the GET request.
You say that you want to read the custom header userId in the pre-flight OPTIONS request but you can't. The reason is because the pre-flight OPTIONS request is created by the browser automatically and it won't use the custom headers you are setting up in the Axios call. It will only send these headers for the CORS:
Origin // URL that makes the request
Access-Control-Request-Method // Method of the request is going to be executed
Access-Control-Request-Headers // Headers allowed in the request to be executed
Because your custom header is not being sent so in the pre-flight OPTIONS when you try to access the value of userId, you get an undefined value:
const { userid } = req.headers|| req.cookies;
console.log(userid); // undefined
And because you are using that value that is not matching in your async function getWhiteListDomains probably getting another undefined, the value set up in the origin option of the CORS middleware is undefined that provokes the CORS middleware rejects the pre-flight OPTIONS request.
let whiteList = await getWhiteListDomains(userid); // userid === undefined
console.log(whitelist); // undefined
return callback(null, {
origin: whiteList, // undefined
credentials: true,
allowedHeaders: ["userid", "authorization", "content-type"]
});
I am not totally sure which is your goal trying to use your custom header as CORS check, but my advise would be when dealing with customised CORS configuration to only check the Origin header because that's its purpose: to limit and control which URLs can access to your server and resources.
If you are interested in creating any kind of authorisation or limited by user implementation in the requests received by your server, I suggest you to use a different custom middleware and not involve CORS at all like you are trying now.

you must parse your request
try this
npm i body-parser
and
const bodyParser = require('body-parser');
app.use(bodyParser.json())

Related

Cors not allowed while using Axios

Hey I was working with fetch a normal fetch to request my API in my server, and It was working, but I found out that Axios is easier, so I tried to replace every fetch, but it looks like xhr doesn't work because of cors,
I already allowed cors when I was working with fetch, but for some reason cors is not allowed for xhr (Axios) requests
here is Axios defaults:
import axios from "axios"
import store from "#/store"
import { getLocalStorage } from "#/services/functions.services.js"
axios.defaults.baseURL = store.getters.getData("serverLink")
axios.defaults.withCredentials = true
axios.defaults.headers.common["Authorization"] = `Bearer ${getLocalStorage("access_token")} ${getLocalStorage("refresh_token")}`
and here is how I use Axios:
function fetchF(link, body, method) {
return new Promise(async (resolve, reject) => {
axios[method.toLowerCase()](link, body)
.then(rsp => {
setLocalStorage("access_token", rsp.access_token);
setLocalStorage("refresh_token", rsp.refresh_token);
resolve(rsp);
}).catch(error => {
setLocalStorage("access_token", "");
setLocalStorage("refresh_token", "");
store.dispatch("changeData", {
option: "alertEl",
value: "Please log in, and try again",
});
return resolve({ isSuccess: false, err: "Please log in, and try again" });
})
});
}
and here is how I enabled cors on the server-side:
app.use(cors({
credentials: true,
}));
here is the error:
error in the network page
If a browser sends a CORS request with credentials: true, the browser will not accept a response with 'Access-Control-Allow-Origin:*. You must sepecify Access-Control-Allow-Origin to one domain. Such as Access-Control-Allow-Origin:example.com.
Ref. https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#requests_with_credentials
This is an error with your server, because for every request, a preflight request is sent before the actual one..
And from the error 'PreflightWildcardOriginNotAllowed' basically means that in your server configurations you have the list of domains set to the wildcard '*'
So just change your cors options in the server for 'Access-Control-Allow-Origin' to your domain instead of '*'
And if your server has it's own way of handling preflight requests, which are basically requests sent with 'OPTIONS', you can set the domains handled by that to your domain
Just to clarify how to change the Access-Control-Allow-Origin to a specific domain (#Cypherjac)
here is how:
app.use(cors({
origin: "http://localhost:8080",
credentials: true,
}));

axios : How exactly to preserve session after successful authorization and send with subsequent request - while testing without browser

In this test case am sending an axios post request with userId and password to ExpressJS server running with passportjs local. Server respond with status code 200, and send appropriate header with set-cookie.
I need subsequent request to be treated as authorized request, for that tried following options, but none seems to be working. It getting rejected with status code 401.
First call with userid and password, responded with status 200
const userDoc = {
userId: 'test-user-1',
userName: 'Test User 1',
emailId: 'test.user.1#abc.xom',
password: 'test-password'
} ;
let resp
resp = await axios({method : 'post', url : 'http://localhost:4040/auth/local', data : {userId: userDoc.userId, password: userDoc.password },withCredentials: true })
following options are used to send next request
send cookies received as part of 1st request
const headers = { headers : {Cookie: resp.headers['set-cookie'][0] } };
send header as it is received as part of 1st request
const headers = { headers : resp.headers};
send withCredentials: true along with above headers.
Second call is made with either of above option
resp = await axios({method : 'post', url : 'http://localhost:4040/v1/master/account', data : accountDoc , headers, withCredentials: true})
used httpAgent, keepAlive with axios instance
const axios = require('axios')
const http = require("http")
const httpAgent = new http.Agent({keepAlive : true , timeout :1000})
const instance = axios.create({httpAgent})
const resp1 = await instance({method : 'post', url : 'http://localhost:4040/auth/local', data : {userId: userDoc.userId, password: userDoc.password, } , withCredentials: true })
const resp2 = await instance({method : 'post', url : 'http://localhost:4040/v1/master/account', data : accountDoc , withCredentials: true })
Rejected with status code 401
-- Error: Request failed with status code 401
at createError (/home/Projects/FinAccounts2003/node_modules/axios/lib/core/createError.js:16:15)
at settle (/home/Projects/FinAccounts2003/node_modules/axios/lib/core/settle.js:17:12)
at IncomingMessage.handleStreamEnd (/home/Projects/FinAccounts2003/node_modules/axios/lib/adapters/http.js:269:11)
at IncomingMessage.emit (events.js:412:35)
at endReadableNT (internal/streams/readable.js:1334:12)
at processTicksAndRejections (internal/process/task_queues.js:82:21)
Server code is standard passport-js local code, which working well with browser.
It may be duplicate of some of the questions, solutions given are 1) withCredentials: true, already tried above 2) Authorization: Bearer ${token} - not applicable in this case, in passport js, cookie is directly set, and not getting token.
One solution that worked for me was using the modules tough-cookie and axios-cookiejar-support. I combined them in a persistent-client.js file, and then I was able to maintain the session between requests (commonJS):
const axios = require('axios').default;
const { CookieJar } = require('tough-cookie');
const { wrapper } = require('axios-cookiejar-support');
module.exports = function () {
const jar = new CookieJar();
const client = wrapper(axios.create({ jar }));
return client;
}
There are two different ways to send the session authorization token from the server to the client (web browser)
Via (HttpOnly) response headers.
Via the response body.
And there are two different ways to authorize client requests (send the session token from the web browser to the server.)
A. Automatic: HttpOnly headers
B. Manual: Authorization: Bearer [TOKEN]
Usually method 1 is used with method A, and method 2 is used with method B. I think you are mixing them up.
If the server is using Set-Cookie to send the session token, then I think the browser automatically sends the session token automatically on all future requests (to the same domain).
Can you confirm what the actual contents of the set-cookie header are from the server? Note you will probably not be able to check this via JS if these are HttpOnly cookies; inspect the dev console "Network" tab. You can also check to see if any new cookies were set from the "Application" tab.
If the client does actually need to manually send the token via headers, the header needs to fit a specific Authorization cookie format. (Which you are not doing. You are simply echoing the headers received from the server.)
See my response to a similar question.
I don't believe you should be using any third party packages for this, especially not if they're directly accessing the cookies using javascript (which is an XSS security vulnerability). Cookies should be set using secure and http-only and never be accessed using Document.cookie directly.
Make sure that passport is actually setting your cookie and that you're correctly sending back the cookie on the login. Verify that it's been set in your browser.
Make sure that you have CORS enabled in express, that you've specified the domain you're making requests from and that you've enabled credentials in CORS.
Make sure that you're using withCredentials on your axios requests.
Make sure that you've set the cookie using the correct domain and path.

How do I Submit data from a vuejs form to zapier webhook using axios?

I am using vuejs / axios and I want to post data to a zapier webhook.
I've tried a number of things. However I continually get the following error:
Access to XMLHttpRequest at 'https://hooks.zapier.com/hooks/catch/7349379/owviy9/' from origin 'http://localhost:8080' has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.
Here is my code:
submit(){
axios
.post(
'https://hooks.zapier.com/hooks/catch/7349379/owviy9j/',
this.formData,
{headers: {"Accept": "application/json"}}
)
.then(function(response) {
console.log(response);
});
}
I've also tried this but can't seem to get it to work:
var data = this.formData
axios.create({ transformRequest: [(data, _headers) => JSON.stringify(data)] })
.post('https://hooks.zapier.com/hooks/catch/7349379/owviy9j/', data)
}
How do I resolve this CORS issue?
From your first example just remove:
{headers: {"Accept": "application/json"}}
Zapier dosn`t allow headers to be sent.
Complete example:
const res = await this.$axios.post('https://hooks.zapier.com/hooks/catch/xxx/xxx/',
JSON.stringify(data)
)
When you try to make requests from the same machine but with different ports. Example: Your front end is running on port 3000, while your nodejs app is running on port: 8080. For security concerns the browser doesn't let you make the api requests.
Solution: Add Cors to your NodeJs App.
npm install cors
const cors = require('cors')
const app = express();
just add a line below this: app.use(cors()); //use it as a middleware and this will resolve your issue.

axios delete call returns 405

We are running a Tornado service in the background which accepts post and delete calls from the React application.
Our axios.post call works perfectly fine but it fails on our delete request with the following error message
405 (Method Not Allowed)
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.
export const StopBuild = (action$) =>
action$.ofType(STOPBUILD)
.mergeMap((action) => {
return Observable.fromPromise(axios.delete(action.stopRequest))
.map(response => PlatformBuildSuccess(response))
.catch(error => Observable.of(PlatformBuildFailure(error, action.platform)))
})
From server side, this is what we have currently set,
self.set_header("Content-Type", "*")
self.set_header("Access-Control-Allow-Origin", "*")
self.set_header("Access-Control-Allow-Headers", "*")
self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT, DELETE')
Something else we noticed that when on the server side we only set the following header POST call still works. I think we are going wrong on the server end.
self.set_header("Access-Control-Allow-Origin", "*")
Any help would be appreciated.
What you can do is, allow cors in your API request:
Like this :
axios.delete(url, { crossdomain: true }) // an example
you can take this as an example and add crossdomain:true in your delete request.
let me know if cors error still persists.
This is a CORS issue. As the error states it, the DELETE operation is not allowed yet. Here is explained that you have to add the DELETE method to the Access-Control-Allow-Methods response header of the preflight request.
On server side, using nodejs, allowing CORS would look like this:
const corsMiddleware = (req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Headers', '*')
res.header('Access-Control-Allow-Methods', 'OPTIONS, POST, DELETE')
next()
}
app.use(corsMiddleware)
The request can be made from all origins with all headers, and OPTIONS, POST and DELETE methods are allowed.
For CORS request there is one package available called cors. You can try that by installing it as,
npm install cors --save
then you can require it and use that as middleware here the example,
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
More on cors npm package here

React: Axios Network Error

This is my first time using axios and I have encountered an error.
axios.get(
`http://someurl.com/page1?param1=1&param2=${param2_id}`
)
.then(function(response) {
alert();
})
.catch(function(error) {
console.log(error);
});
With the right url and parameters, when I check network requests I indeed get the right answer from my server, but when I open console I see that it didn't call the callback, but instead it caught an error.
Error: Network Error
Stack trace:
createError#http://localhost:3000/static/js/bundle.js:2188:15
handleError#http://localhost:3000/static/js/bundle.js:1717:14
If Creating an API Using NodeJS
Your Express app needs to use CORS (Cross-Origin Resource Sharing). Add the following to your server file:
// This should already be declared in your API file
var app = express();
// ADD THIS
var cors = require('cors');
app.use(cors());
For fuller understanding of CORS, please read the Mozilla Documentation on CORS.
my problem was about the url I was requesting to. I hadn't inserted http:// at the beginning of my url. I mean I was requesting to a url like 92.920.920.920/api/Token instead of http://92.920.920.920/api/Token. adding http:// solved my problem.
It happens when you work on localhost and forgot to add http://
Wrong Usage
const headers = {
"Content-Type": "application/json",
Authorization: apiKey,
};
const url = "localhost:5000/api/expenses/get-expenses";
axios.get(url, { headers });
// NETWORK ERROR
The correct one is
const headers = {
"Content-Type": "application/json",
Authorization: apiKey,
};
const url = "http://localhost:5000/api/expenses/get-expenses";
axios.get(url, { headers });
// WORKS FINE IF YOU HANDLED CORS CORRECTLY IN THE SERVER SIDE
In addition to #jacobhobson answer, I had also used some parameters to made it work.
app.use(cors({origin: true, credentials: true}));
I was having same issue on production on digital ocean droplet. I was using axios in ReactJS to call Node.js API.
Although I included cors
const cors = require('cors');
app.use(cors());
But I still had to add
res.header( "Access-Control-Allow-Origin" );
before calling out my controller. And it worked for me. There I realized that cors is not working properly. So I uninstalled and installed them again and It Works!
Complete code is here.
So either you use
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
res.header("Access-Control-Allow-Headers", "x-access-token, Origin, X-Requested-With, Content-Type, Accept");
next();
});
or use
app.use(cors());
It's the same.
I received a network error with axios 0.27.2 when I was trying to upload an image to our server. After I set headers like below no error is received.
headers:{"Accept":"application/json, text/plain, /","Content-Type": "multipart/form-data"}
and you need to check with your api request's body type in your collection like if it's form-data or x-wwww-form-urlencoded or ..etc.
Make sure you have the same port number in cors({ origin : [ "http://localhost:3001"]}) and the .env file.
In my case I used "https" instead of "http", check that too.
I just want to let you know that after searching for a solution for two days, I was able to solve my error.
Since the proxy was the source of the issue, I must configure a proxy in the package.json file, and I have to follow these instructions in the function that uses Axios:
try { await axios.post("user/login", formData).then((res) => { console.log(res.data); }); } catch (error) { console.log(error.response.data.message); }
and in package.json file need to add a proxy:
"proxy": "http://localhost:6000",
for better understand check this documentation: enter link description here
If you are running react native in development while using real device connected via USB(and the API server is being accessed via development machine IP), ensure the development machine and the device are both connected to the same network
This is happening because of restrict-origin-when-cross-origin policy.Browser sends a pre-flight request to know whom the API server wants to share the resources.
So you have to set origin there in API server and send some status.After that the browser allow to send the request to the API server.
Here is the code.I am running front-end on localhost:8000 and api server is running on port 6000.
const cors = require("cors");
app.options("*", cors({ origin: 'http://localhost:8000', optionsSuccessStatus: 200 }));
app.use(cors({ origin: "http://localhost:8000", optionsSuccessStatus: 200 }));
I have set origin as my front-end url, If You set it to true , then it will allow only port 8000 to access rosource, and front-end running on port 8000 can not access this resource. Use this middleware before route in api server.
I have resolved my issue by adding this header.
var data = new FormData();
data.append('request', 'CompaniesData');
var config = {
method: 'post',
url: baseUrl, headers:{"Accept":"application/json, text/plain, /","Content-Type": "multipart/form-data"},
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
i'm using axios in react-native as android and .net as backend, i have same issue but i can't solve the problem. I think it is security problem when i type the url in chrome it warns me about that in emulator.
axios("http://10.0.2.2:5001/api/Users/getall")
.then((result) => setUsers(result.data.data))
.then((json) => {
return json.data;
})
.catch((error) => {
console.error(error);
})
.then((response) => response.parse());
In my case, I'm using Hapi.js as the backend, so all I had to do is set the cors value to true as in the code below;
const server = Hapi.server({
port: 4000,
host: 'localhost',
state: {
strictHeader: false
},
routes: {
cors: true
}
});
change the port number of your node server.
It took more than 3 hours to solve this error. Solution ended with changing port numer which was initially set to 6000, later set to 3001. Then it worked. My server localhost base url was:
"http://localhost:6000/data"
I changed port number in app.listen() on server and from frontend I call that GET route in async function as
await axios.get('http://localhost:3001/data').
It is working fine now.
If you face the address issue: address already in use :::#port
Then on command prompt: killall -9 node

Categories

Resources