Cannot call Apache Airflow REST API using JavaScript Fetch API (CORs Error) - javascript

Working with Apache Airflow REST API, and having issues with CORS.
When calling the endpoint using the fetch API in JavaScript I get the following error:
Access to fetch at 'my_url/api/v1/dags/example_bash_operator/tasks' from origin 'my_url' 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. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
This is how I am calling it:
let url = "my_url/api/v1/dags/example_bash_operator/tasks";
let username = 'my_username';
let password = 'my_password';
let headers = new Headers();
headers.set('Authorization', 'Basic ' + btoa(username + ":" + password));
fetch(url, {
headers: headers,
method: 'GET',
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
});
I also tried adding mode: 'no-cors' but just get the "unexpected end of input" error.
For some background, the following works fine:
starting the airflow webserver and scheduler
accessing the airflow UI
accessing the SwaggerUI authenticating Swagger and calling the REST endpoints with this tool
calling my_url in the address bar of a new browser tab (returns the expected JSON)
I have set the auth_backend in airflow.cfg:
auth_backend = airflow.api.auth.backend.default
Although with the latest REST API version I don't think this makes a difference since everything is set to deny.
I have also set the access control headers in airflow.cfg as described in the docs:
access_control_allow_headers = origin, content-type, accept
access_control_allow_methods = POST, GET, OPTIONS, DELETE
access_control_allow_origin = my_url
...and also tried with wildcard for the access_control_allow_origin:
access_control_allow_origin = *
So the REST calls work fine through Swagger and through the browser address bar, but I cannot call it with fetch using JS. Note that the JS is in an index.html file on the same server (and same root directory) as the airflow files.

The described behavior makes sense, since CORS is used by the browser to prevent attacks from scripts of different resources.
You are still able to fetch via Swagger, Postman or other tools, even through the browser via address bar. But if the policy does not allow to fetch from a different origin, then the browser prevents fetching from your script, which is probably served on a different port. Origin contains host and port.
Your main issue, I cannot help with at the moment.
I've faced the issue of not being able to set the origin policy within the Airflow 2.0 server/API through the (docker-compose) environment variable AIRFLOW__API__ACCESS_CONTROL_ALLOW_ORIGIN.
Maybe it's related to your issue, since I can see from the url of your question (containing the v1), that you're are also using Airflow 2.x.
By the way, the message from chrome is CORS error: Preflight Missing Allow Origin Header, referring to the question in the comments of the original question.

Related

Enable Cross-Origin Requests (CORS) in POCO C++ Libraries

I'm building a C++ backend with heavy calculations that are meant to work as an JSON API for connecting clients. To accomplish this, I've used HTTPServer in Poco::Net from POCO C++ Libraries.
Unfortunately when building two different clients it turned out that a regular webpage (HTML+JS) can't use Fetch to communicate with the backend due to CORS error. My understanding is that they need to use the same localhost: and that's not the case when manually opening the HTML document on the computer that's also running the backend.
All I can come up with when searching is the generic advice that servers need to enable CORS and whitelist relevant domains. Unfortunately I can't find documentation on how to accomplish this. The only relevant result was an answer on a related question where he recommended the following:
response.set("Access-Control-Allow-Origin", "*");
Naturally whitelisting everything isn't recommended from a security point of view but the main goal here is to just get it running locally to continue the development. Unfortunately it seems to make no difference and the browser console still says:
Access to fetch at 'http://localhost:6363/' from origin 'null' 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. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Hovering the error in the Chrome Network tab I get the following:
Cross-Origin Resource Sharing error: PreflightMissingAllowOriginHeader
My current JavaScript call:
const data = { test: 'test' }
fetch('http://localhost:6363', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.text())
.then(message => {
console.log('Data retrieved:', message);
})
.catch((error) => {
console.error('Error:', error);
});
Any suggestions on how to proceed?

Keycloak Introspection Endpoint

I'm trying to access to introspect endpoint in my Keycloak server /openid-connect/token/introspect from my front app, but I get next error:
Access to fetch at 'http://localhost:8180/auth/realms/backoffice/protocol/openid-connect/token/introspect' 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.
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Using Postman, curl or Node app this request works fine, but from my front-app using fetch method thows this error. I'm not sure it's possible query for introspect endpoint from front-app in the browser or if it's only possible from server app.
Other endpoints like:
openid-connect/token:
openid-connect/userinfo:
Works fine using the Postman JS code.
Keycloak config
My client in Keycloak has set up Web Origins * and Access Type confidential.
Client Code
My front app is simply the Postman code JS, and I deploy it using node http-server.
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
var urlencoded = new URLSearchParams();
urlencoded.append("client_id", "my-client");
urlencoded.append("client_secret", "my-secret");
urlencoded.append("token", "eyJ...oCA");
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: urlencoded,
redirect: 'follow'
};
fetch("http://localhost:8180/auth/realms/backoffice/protocol/openid-connect/token/introspect", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
Header Response
The header response in userinfo endpoint comes with Access-Control-Allow-Origin and Access-Control-Allow-Credentials but is not present in introspect endpoint.
From the looks of it, the Keycloak server prevents the CORS headers to be set for the introspection endpoint. This could be a bug or by design. I tried it and I get the same error.
If you really want to access the introspect endpoint from the web app, you could set up a NGINX reverse-proxy in front of your Keycloak server and use it to add the missing headers.
That being said, according to oauth.com you should not leave the introspection endpoint available to the public, which is what you are currently doing since anyone can retrieve the client id and secret from your web app.
If the introspection endpoint is left open and un-throttled, it presents a means for an attacker to poll the endpoint fishing for a valid token. To prevent this, the server must either require authentication of the clients using the endpoint, or only make the endpoint available to internal servers through other means such as a firewall.
This could explain the decision not to allow CORS.
Another thing, it looks like you forgot to set the token_type_hint check out this stackoverflow post for more information.

REST API call succeeds from curl or python, but fails in browser

I'm trying to build a website that requests JIRA data from a company-owned JIRA Board via REST API and displays the requested data in different graphs. While developing with JavaScript I ran into the CORS header 'Access-Control-Allow-Origin' missing error and therefore could not access the data.
Here is some code:
var url = myUrl;
var auth_string = myJira_user + ":" + myJira_password;
var encoded_string = btoa(auth_string);
var http_headers = {
"Content-Type": "application/json",
Authorization: "Basic " + encoded_string
};
var headers = new Headers(http_headers);
fetch(url, headers)
.then(response => {
return response.json();
})
.then(data => {
console.log(data);
})
.catch(err => {});
This JS script throws the above-mentioned error in my browser console, while a simple python script or curl command with the same parameters works and outputs data. How is that possible?
Modern browsers first check CORS policy on the API server via an HTTP OPTIONS call. This is known as "preflighting a request".
CORS: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
HTTP OPTIONS: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS
In effect, the browser first checks with the API server whether it has permission to call and consume APIs from the origin (website where the user is browsing from, identified by domain, protocol, and port). If the API server expects calls to be made from the origin, it returns a success response to the OPTIONS calls and then the browser proceeds to make the GET/POST/etc call. However, if the API server does not recognize the origin from the OPTIONS call, it will return a fail response, and the browser will not proceed with the API call and instead generates the exception you've observerd.
CORS + HTTP OPTIONS is one mechanism in the arsenal of securing API access and mitigating the plethora of security and privacy issues arising from Cross-Site-Scripting.
But to your point, CORS does not impact calls made from non-browser applications. A non-browser application, such as CURL or any code you can write in Python, Node, etc. does not make the OPTIONS calls and therefore, the API server's CORS policies do not apply.
You could setup a middleman/proxy server that receives API requests from your web application, call the target API server, and marshal back the response, therefore circumventing the CORS policy.

CORS issue when calling a localhost API hosted on one port from another port

I have a localhost Rest API POST query hosted using Docker at port 8501: http://localhost:8501/v1/models/model:predict. I have an HTML file which runs with a JavaScript script and it is hosted at http://127.0.0.1:8887/ using WebServer for Chrome. I am able to call global Rest API POST queries from my HTML-JS files and my local Rest API works fine from POSTMAN. But I am unable to call my API from my HTML-JS files.
Below is my JS file:
function foo() {
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Access-Control-Allow-Origin", "*");
myHeaders.append("Access-Control-Allow-Methods", "POST");
var raw = JSON.stringify({"instances":[[0]]});
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("http://localhost:8501/v1/models/model:predict", requestOptions)
.then(response => console.log(response.text()))
.then(result => console.log(result))
.catch(error => console.log('error', error));
}
I get the below error:
Access to fetch at
'http://localhost:8501/v1/models/model:predict' from origin
'http://127.0.0.1:8887' 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. If an opaque response serves your needs, set the request's
mode to 'no-cors' to fetch the resource with CORS disabled.
Can anyone please help me?
You seem to have understood CORS slightly "backwards" -- the Access-Control-Allow-Origin and Access-Control-Allow-Methods headers are specified on HTTP responses, not requests. This obviously implies that it's the HTTP server that sets them, not the client.
It would make for a terrible cross site security model if a script of arbitrary origin could effectively dictate to the HTTP server what kind of requests and from which origins the latter must accept.
It's the HTTP service that ultimately controls access by specifying appropriate headers in the responses it serves, and the user agent validates the cross origin request where it refuses to proceed if the server didn't indicate it allows requests from the different origin the script was served from -- the response will be discarded and an error will be thrown.
In short, you need to add the headers to the responses your REST service at localhost:8501 generates, instead of specifying them for requests your client side script creates where they don't do anything.
Your server is missing header Access-Control-Allow-Origin in response, as it is responsible what to allow or not. See https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

How can I secure implement third-party API calls using JavaScript in a BigcCommerce store?

I want to make some API requests to the shipping carriers at the BigCommerce product page and I have some credential for that requests which I don't want to show in my JS code. According to the specific environment of BigCommerce I can't make any changes in back end code. I read a lot of similar questions and now have only one question.
Is it only one way to do that using my own API back end web-server which will store credential information and send POST request to the third party API? Then I will receive that information using a POST request via JS to my own API.
I have tried to run Ruby API application on the nginx web-server. However, it was unsuccessful because browser blocked my fetch() request according to the CORS Policy. I tried to add Access-Control-Allow-Origin: * parameter to the server response header writing it in ruby config file but browser didn't recognize it. Also I tried to set up configuration file on the nginx side but that didn't help me with CORS policy response. That's interesting because using Restlet Application I received response from my own API application with correct information and status "ok".
(async function application() {
let dataRuby = {
url: 'http://IP_address/index',
body: {"name": "21312", "year": "2019"}
};
function getApi(data) {
let myInit = {};
myInit.method = "POST";
myInit.body = JSON.stringify(data.body);
myInit.headers = {
'Content-Type': 'application/json'
};
let myRequest = new Request(data.url, myInit);
return fetch(myRequest).then(
res => res.json()
);
}
let response = await getApi(dataRuby);
console.log(response);
})()
Access to fetch at http://IP_address/index from origin 'null' 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.

Categories

Resources