I´m trying to make a POST request from a vue web app to a ESP32 https server.
The request is always blocked by CORS because self-signed certificates.
ESP32 Starts in AP mode, generating his own Wifi network.
An https server, with SSL certificates generated by me, starts running (using this library: https://github.com/fhessel/esp32_https_server)
In a PC running locally the VUE app, I make the POST request to the ESP32 server (see code below)
(I´m using axios to make the request)
I have tried:
Adding a custom https aggent with rejectUnauthorized:
Adding same cert in POST request that ESP32 server uses
Adding CORS headers
Adding process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"
Starting vue dev server in https mode with ESP32 same certs
Try to obtain the OPTIONS request in ESP32 server to allow the POST request
Making the request with curl or postman works fine!! It´s like a problem with the browser or vue.
const httpsAgent = new https.Agent({
rejectUnauthorized: false,
});
axios.post(`https://192.168.4.1/config?ssid=${this.ssid}&ssidkey=${this.password}`, { httpsAgent })
.then((res) => {
...
});
The goal is to configure my WiFi credentials by conecting directly to the ESP32 in AP Mode first.
If I use curl or Postman to make the request its working fine, the ESP32 receive WiFi credentials and connect to my router, but it has been impossible to me to make the same with the vue web app...
You can find the full details of how the CORS mechanism works on the mozilla developers page: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
In summary, the browser automatically performs an HTTP request with the method OPTIONS with the same url of your original request to your server, and this request can't be avoid. The browser expect that this request responds with a status 200 and with the CORS headers ( for example:
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
)
Once the response to the method OPTIONS is receive and looks valid for the browser, your original request is performed by the browser.
So, you need to configure your server to responde to the options method with the cors headers.
Related
I'm developing a web app in Angular 10 that works as follows:
I'm dealing with CORS issue. I do not have permission to add code to the server I'm fetching.
I want to be able to:
Fetch the website
Parse the result, and put it in my database
I'm aiming to deploy the solution on an Apache server.
Here is the CORS error I'm dealing with:
Blocking a Cross-Origin Request: The "Same Origin" policy does not
allow viewing the remote resource located at
https://wwwfrance1.CENSORED.eu.com/api/?apikey=CENSORED.
Reason: "Access-Control-Allow-Origin" CORS header is missing. Status
code: 200.
Here is what i've tried:
Using MOSIF mozilla extension (works, but not sustainable for deployment, and for some reason, when I'm ignoring the CORS security, I cannot post on my DB any more)
Adding a header in my fetching request, such as:
/******API SEACH****/
/***Global Update***/
private updateClients() {
let xmlRequestPromise = fetch('https://wwwfrance1.CENSORED.eu.com/api/?apikey=CENSORED&service=list_clients', {
method: 'GET',
headers: {
'Access-Control-Allow-Origin': '*',
}
})
.then(async response => this.clients = this.regexSearchClient(await response.text()))
return xmlRequestPromise
}
But that doesn't work either. I've verified that the header appears in the request.
How to proceed?
What is CORS ?
Cross-origin resource sharing is a mechanism that allows restricted resources on a web page to be requested from another domain outside the domain from which the first resource was served. From wiki
In simple terms only an internal webserver can send Requests which are potentially dangerous to it's web server, and requests from other server's are simply blocked.
But few HTTP requests are allowed ,Few of the allowed methods are GET, HEAD, POST.
How do I resolve the issue ?
Apparently in this circumstance you cannot send a fetch request to a web server having CORS header. Instead you can do a GET request to the web server as a web server having CORS allows HTTP GET requests.
Note - If you send a GET request to a web server using angular in your browser it wouldn't work as browser's convert GET requests into fetch requests and fetch requests aren't allowed from a web server with CORS. Instead send a GET request from a webserver/local machine rather than a browser.
Create your own server and make a route which fetches that API. From your Angular application fetch that route on your server.
You have to use a package as a middleware. If you are using nodejs-framework expressjs.At first, you have to run npm install cors -force.Then add the code that is given bellow:-
const cors=require('cors')
app.use(cors({origin:true}))
I am using the imgur api to upload images via a node js app.
I am converting images to base64 strings and sending them via Postman works great.
I use node-fetch to make api calls.
const fetch = require('node-fetch')
...
async uploadImage(base64image) {
try {
const url = 'https://api.imgur.com/3/image'
const res = await fetch(url,
{
method: 'POST',
body: { image: base64image },
headers: {
'content-type': 'application/json',
'Authorization': 'Client-ID [my-client-id]',
'Access-Control-Allow-Headers': 'Content-Type, Authorization, Access-Control-Allow-Headers',
'Access-Control-Allow-Methods': 'POST',
}
}
)
console.log(res)
} catch(err) {
console.log(err)
}
}
Error:
Access to fetch at 'https://api.imgur.com/3/image' from origin 'http://localhost:3000' has been blocked by CORS policy: Request header field Access-Control-Allow-Headers is not allowed by Access-Control-Allow-Headers in preflight response.
I have tried many 'Access-Control-Allow-xxx' headers but none of them worked..
I assume it must be something simple that I am missing. I have been stuck on this for hours please help me.
Browser restricts HTTP requests to be at the same domain as your web page, so you won't be able to hit imgur api directly from the browser without running into CORS issue.
I am converting images to base64 strings and sending them via Postman
works great.
That's because Postman is not a browser, so is not limited by CORS policy.
I have tried many 'Access-Control-Allow-xxx' headers but none of them
worked..
These headers must be returned by the server in response - in your case by the imgur server. You can't set them in the request from browser, so it'll never work.
Error: Access to fetch at 'https://api.imgur.com/3/image' from origin
'http://localhost:3000' has been blocked by CORS policy: Request
header field Access-Control-Allow-Headers is not allowed by
Access-Control-Allow-Headers in preflight response.
Possible solutions to your problem:
If you have access to the backend api you can set the "Access-Control-Allow-Origin" header on the server and let your app access the api - but as you won't have access to the imgur server - you probably can't do that.
Disable CORS in the browser - you can use a plugin like: https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi?hl=en. This workaound should be fine for development. The plugin will disable your CORS settings and you will be able to hit imgur apis.
The third solution is using a proxy. You can setup a small node server using express. You will then hit your own node server, which in turn will hit the imgur api. As node server is not a browser environment, it won't have any CORS issue and you will be able to access imgur API that way. This is also the reason you were able to hit the API from Postman without any issues. As Postman is not a browser environment, it's not limited by CORS policy.
That's because Access-Control-Allow-Headers, Access-Control-Allow-Methods are the headers that is used by the server. The server appends the header by a middleware.
Now, imagine in the server(in this below example an express server) with CORS enabled this kind of (default) headers are getting set:
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With, Accept');
});
And you are sending Access-Control-Allow-Headers from the client side, and server sees that as a header that is not whitelisted.
So, in headers just use these:
headers: {
'content-type': 'application/json',
'Authorization': 'Client-ID [my-client-id]'
}
It should work fine.
Btw, I think it is working with postman because:
Postman cannot set certain headers if you don't install that tiny postman capture extension.
Browser security stops the cross origin requests. If you disable the chrome security it will do any CORS request just fine.
Also, according to this:
I believe this might likely be that Chrome does not support
localhost to go through the Access-Control-Allow-Origin -- see
Chrome issue
To have Chrome send Access-Control-Allow-Origin in the header, just
alias your localhost in your /etc/hosts file to some other domain,
like:
127.0.0.1 localhost yourdomain.com
Then if you'd access your script using yourdomain.com instead of
localhost, the call should succeed.
Note: I don't think the content type should be application/json it should be like image/jpeg or something. Or maybe don't include that header if it doesn't work.
I have some few observations in my own app that helped me solve this issue. I have a node app as a backend api service and a VueJS built front end. I set my node app with cors with a list of endpoints that are allowed to access my node app. Working on my local machine doesn't give me any errors until I upload it to my server.
here are my environments
Local Environment
Nodejs: 12.16.1
OS: Windows 10
DB: MySQL
NodeJS Server Framework: ExpressJS
Upload Module: Multer
Production Environment
Nodejs: 12.19.0
OS: Ubuntu 20.0.4
DB: MySQL
NodeJS Server Framework: ExpressJS
Upload Module: Multer
nginx
Here are my observations based on my production built app.
When I upload a form with image [500kb and above] [post or put], the cors error shows up but less than that, it all went fine.
If I use form data to send data to the server, I see 2 requests in my network tab, the OPTIONS and the actual request.
The actual request failed but I saw that my content-length is very high which leads me to the conclusion that my request is rejected due to the large amount of data that the client sent which my server may have limited. I know that may be misleading but the solution I did works so I don't know why cors issue is popping up even though the data limit is the issue.
MY SOLUTION:
In my nginx config file, I increased my client_max_body_size to 100M. I believe that nginx has a default of 1MB
Open /etc/nginx/sites-available/your-server-file where your-server-file can be like www.example.com or default.
Add the following line inside the server block. You can set it to any amount you want other than 100M.
server {
client_max_body_size 100M;
...
}
type in sudo systemctl restart nginx to restart nginx.
type in sudo nginx -t to check if change is successful.
Reload your app if you are using pm2 and you are done.
According to this article I used this command in linux and SOME OF(!) cross-origins fixed.
google-chrome --disable-web-security --user-data-dir="/tmp/YOUR_TEMPORARY_PATH"
This will not work if you pass headers from frontend. CORS policy is enabled by browsers. Browser blocks the response when they don't found the headers in response.
Possible Solutions:
You can pass the headers in response (If you have the access of backend or ask the API provider for this)
You can setup a middleware to resolve this.
You can get information from here
I'm having trouble understanding Apollo Client library as it does not work as intended. Instead of sending the GET HTTP method, it sends the OPTIONS HTTP method even though I've put to use GET only when retrieving data from GraphQL server.
const client = new ApolloClient({
link: ApolloLink.from([
new MeteorAccountsLink(),
new HttpLink({
uri: 'https://selo-comments.herokuapp.com/graphql',
useGETForQueries: true
})
]),
cache: new InMemoryCache()
});
Console log from the browser:
OPTIONS https://selo-comments.herokuapp.com/graphql?query=%7B%0A%20%20comments(id%3A%20%22TFpQmhrDxQqHk2ryy%22)%20%7B%0A%20%20%20%20articleID%0A%20%20%20%20content%0A%20%20%20%20userId%0A%20%20%20%20createdAt%0A%20%20%20%20commentID%0A%20%20%20%20votes%0A%20%20%20%20blockedUsers%0A%20%20%20%20__typename%0A%20%20%7D%0A%7D%0A&variables=%7B%7D 405 (Method Not Allowed)
Which obviously means that the HTTP method is incorrect even if it has the query parameter in the url. If you query that url using Postman or simply navigating to the url using browser's address bar, you will get GraphQL data. I have to use https://cors-anywhere.herokuapp.com/ in order to execute the query successfully.
What am I doing wrong?
The options request is probably a preflight request for CORS.
A CORS preflight request is a CORS request that checks to see if the CORS protocol is understood.
It is an OPTIONS request, using three HTTP request headers: Access-Control-Request-Method, Access-Control-Request-Headers, and the Origin header.
You probably need to configure your server to allow cross origin calls.
Maybe you can find some inspiration here to get u started. Allow CORS REST request to a Express/Node.js application on Heroku
I have a proxy set up for a third party service that at the moment looks like this:
app.use('/service', (req, res) => {
let url = `http://www.service.com/endpoint/${config.POSTCODER_KEY}${req.url}`
req.headers['Referer'] = 'my.domain.com'
console.log(req.headers['Referer'])
req.pipe(request(url)).pipe(res)
})
As you can see I am trying to add Referer header to the request and it seems to be working as console.log prints out 'my.domain.com' however request fails and the error I get back from the service is 403 unauthorised referring to Referer header. When I inspect network in inspector tools my referer is displayed as localhost.
I am testing this in Postman api client (https://www.getpostman.com) by setting Referer to my white listed domain and it works. I'm not sure why it uses localhost with express.
Piping streams together only transfers the data in those streams. Headers are not a part of that. When you req.pipe(request(url)) you're only writing the request body to the proxied request. If you want to set the headers used for the proxied request, you have to pass them to request, like:
req.pipe(request({ url: url, headers: req.headers })).pipe(res);
However, as noted in my answer to your previous question, you will also need to properly set the headers on res when the proxied response arrives.
I am writing a JavaScript client to be included on 3rd party sites (think Facebook Like button). It needs to retrieve information from an API that requires basic HTTP authentication. The simplified setup looks like this:
A 3rd party site includes this snippet on their page:
<script
async="true"
id="web-dev-widget"
data-public-key="pUbl1c_ap1k3y"
src="http://web.dev/widget.js">
</script>
widget.js calls the API:
var el = document.getElementById('web-dev-widget'),
user = 'token',
pass = el.getAttribute('data-public-key'),
url = 'https://api.dev/',
httpRequest = new XMLHttpRequest(),
handler = function() {
if (httpRequest.readyState === 4) {
if (httpRequest.status === 200) {
console.log(httpRequest.responseText);
} else {
console.log('There was a problem with the request.', httpRequest);
}
}
};
httpRequest.open('GET', url, true, user, pass);
httpRequest.onreadystatechange = handler;
httpRequest.withCredentials = true;
httpRequest.send();
The API has been configured to respond with appropriate headers:
Header set Access-Control-Allow-Credentials: true
Header set Access-Control-Allow-Methods: "GET, OPTIONS"
Header set Access-Control-Allow-Headers: "origin, authorization, accept"
SetEnvIf Origin "http(s)?://(.+?\.[a-z]{3})$" AccessControlAllowOrigin=$0
Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Note that the Access-Control-Allow-Origin is set to the Origin instead of using a wildcard because I am sending a credentialed request (withCredentials).
Everything is now in place to make an asynchronous cross-domain authenticated request, and it works great in Chrome 25 on OS X 10.8.2. In Dev Tools, I can see the network request for the OPTIONS request before the GET request, and the response comes back as expected.
When testing in Firefox 19, no network requests appear in Firebug to the API, and this error is logged in the console: NS_ERROR_DOM_BAD_URI: Access to restricted URI denied
After much digging, I found that Gecko doesn't allow the username and password to be directly in a cross-site URI according to the comments. I assumed this was from using the optional user and password params to open() so I tried the other method of making authenticated requests which is to Base64 encode the credentials and send in an Authorization header:
// Base64 from http://www.webtoolkit.info/javascript-base64.html
auth = "Basic " + Base64.encode(user + ":" + pass);
...
// after open() and before send()
httpRequest.setRequestHeader('Authorization', auth);
This results in a 401 Unauthorized response to the OPTIONS request which lead to Google searches like, "Why does this work in Chrome and not Firefox!?" That's when I knew I was in trouble.
Why does it work in Chrome and not Firefox? How can I get the OPTIONS request to send and respond consistently?
Why does it work in Chrome and not Firefox?
The W3 spec for CORS preflight requests clearly states that user credentials should be excluded. There is a bug in Chrome and WebKit where OPTIONS requests returning a status of 401 still send the subsequent request.
Firefox has a related bug filed that ends with a link to the W3 public webapps mailing list asking for the CORS spec to be changed to allow authentication headers to be sent on the OPTIONS request at the benefit of IIS users. Basically, they are waiting for those servers to be obsoleted.
How can I get the OPTIONS request to send and respond consistently?
Simply have the server (API in this example) respond to OPTIONS requests without requiring authentication.
Kinvey did a good job expanding on this while also linking to an issue of the Twitter API outlining the catch-22 problem of this exact scenario interestingly a couple weeks before any of the browser issues were filed.
This is an old post but maybe this could help people to complete the CORS problem. To complete the basic authorization problem you should avoid authorization for OPTIONS requests in your server. This is an Apache configuration example. Just add something like this in your VirtualHost or Location.
<LimitExcept OPTIONS>
AuthType Basic
AuthName <AUTH_NAME>
Require valid-user
AuthUserFile <FILE_PATH>
</LimitExcept>
It was particular for me. I am sending a header named 'SESSIONHASH'. No problem for Chrome and Opera, but Firefox also wants this header in the list "Access-Control-Allow-Headers". Otherwise, Firefox will throw the CORS error.