CORS in OAuth: Response to preflight request doesn't pass access control check - node.js

So I'm trying to implement the OAuth 2 flow, while my webapp is the server that give away authorization code/access token.
Cors error happens when sending the code back to third-party website(zapier in this case):
XMLHttpRequest cannot load https://zapier.com/dashboard/auth/oauth/return/App505CLIAPI/?code=somecode&state=somestate. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://myurl' is therefore not allowed access.
If I manually open a new tab, paste that zapier uri, everything works perfectly.
Seems like a typical CORS issue, but none the popular solutions works for me:
Add Access-Control-Allow-Origin: I'm using this oauth2orize
library, and sending reponse to preflight seems also part of the
library. So I cannot add header.
Using cors: Have tried app.use(cors()) and app.options('*',
cors()) which should apply to all routes, but simply not working.
My webapp sits on a node express server, in front of which there's an nginx proxy server.
Any idea where the problem might be is appreciated.

The issue that error message indicates isn’t caused by the app code running at https://myurl/. Instead it’s just that https://zapier.com/dashboard/auth/… doesn’t seem to support CORS.
Specifically, the response from that https://zapier.com/dashboard/auth/… URL doesn’t include the Access-Control-Allow-Origin response header, so your browser won’t let your frontend JavaScript code access the response.
It seems like that is all intentional on the part of Zapier—they don’t intend for that auth endpoint to be accessed from frontend AJAX/XHR/Fetch code running in a browser. Instead I guess it’s intended that you only access that auth endpoint from your backend code. Or something.
Anyway there is no way from your side that you can fix the fact the response from that Zapier API endpoint doesn’t include Access-Control-Allow-Origin.
And as long as it doesn’t include Access-Control-Allow-Origin, your browser blocks your frontend code from being able to get to the response—and there’s no way to get your browser to behave otherwise as long as your frontend code is trying to hit that API endpoint directly.
So the only solution is to not hit that API endpoint directly from your frontend code but to instead either set up a proxy and change your frontend code to make the request through that, or else just handle it in some other way in your existing backend code, as mentioned above.
The answer at Why does my JavaScript code receive a "No 'Access-Control-Allow-Origin' header is present on the requested resource" error, while Postman does not? gives some details on how you can set up a special CORS proxy, if you want to go that route.

Related

Keep on getting Unauthorize Web API

I have a project, It's a web application that requires Windows Authentication.
I've setup an Active Directory at home using my NAS virtualization. Then I've created a VMWare Server for IIS which is a member of that domain on my desktop which I also use for development. I've created the Web API and installed it into that VMWare server. When I call a routine directly, it works and return results but when I use the Web API routine from my javascript web application I keep on getting 401 error. I then put the code on the IIS server and the web application works.
I've seen a lot of solutions like changing the sequence of the Provider in IIS Authentication. Added Everyone read/write permission on the folders. I've also added entry on the web.config. But none of them work.
*****Update as per request on the comment *****
Below is when I run directly from Web API
Calling the Web API from Javascript
Here's the error I'm getting
Just FYI, I tried running the web api from Visual Studio on the same machine but also with 401 error
Is there anything I could add to AD to make my development machine as trusted?
********************A new issue after the code change **********
****************Another Update******
This is definitely weird, so I installed Fiddler 4 to see what's going on. But still no luck.
Then I made changes on the IIS HTTP Response Header
The weird thing is when I run Fiddler the error is gone but when I close it it comes back.
There are two things going on here:
A 401 response is a normal first step to Windows Authentication. The client is then expected to resend the request with credentials. AJAX requests don't do this automatically unless you tell it to.
To tell it to send credentials in a cross-domain request (more on that later), you need to set the withCredentials option when you make the request in JavaScript.
With jQuery, that looks like this:
$.ajax({
url: url,
xhrFields: {
withCredentials: true
}
}).then(callback);
These problems pop up when the URL in the address bar of the browser is different than the URL of the API you are trying to connect to in the JavaScript. Browsers are very picky about when this is allowed. These are called "cross-domain requests", or "Cross-Origin Resource Sharing" (CORS).
It looks at the protocol, domain name and port. So if the website is http://localhost:8000, and it's making an AJAX request to http://localhost:8001, that is still considered a cross-domain request.
When a cross-domain AJAX request is made, the browser first sends an OPTIONS request to the URL, which contains the URL of the website that's making the request (e.g. http://localhost:8000). The API is expected to return a response with an Access-Control-Allow-Origin header that says whether the website making the request is allowed to.
If you are not planning on sending credentials, then the Access-Control-Allow-Origin header can be *, meaning that the API allows anyone to call it.
However, if you need to send credentials, like you do, you cannot use *. The Access-Control-Allow-Origin header must specifically contain the domain (and port) of your webpage, and the Access-Control-Allow-Credentials must be set to true. For example:
Access-Control-Allow-Origin: http://localhost:8000
Access-Control-Allow-Credentials: true
It's a bit of a pain in the butt, yes. But it's necessary for security.
You can read more about CORS here: Cross-Origin Resource Sharing (CORS)

Setting cookies when CORS Access-Control-Allow-Origin is set to (*)

I'm building an API with Node.js and Express, with the intention that it will be accessed by users via API calls, as well as act as the RESTFUL back-end for my web application (React.js).
For this reason, I've set the CORS Access-Control-Allow-Origin to the wildcard (*), as it should be able to be accessed from external sources.
Currently, the API is running on localhost:8000, and the web-app is running on localhost:3000.
Now, I want to set a cookie when my /login endpoint is accessed. The cookie is not used for authentication, so I'm not worried about CSRF.
I'm setting the cookie like this:
res.cookie('token', 'value', {httpOnly:true});
The cookie gets set no problem when I use Postman, however when trying to access it with my webapp through the browser, the cookie does not get set.
After reading several posts on Stack Overflow, I understand that I need to set credentials: include when doing the fetch operation from my web application.
The problem is that this is not allowed on browsers, if Access-Control-Allow-Origin is set to (*).
Is there any way to set the cookie on the browser under these conditions?
Thanks
https://security.stackexchange.com/questions/198714/access-control-allow-origin-wildcard-doesnt-allow-sending-session-cookies See this answer on stackexchange for some reasoning behind this, but basically no, you cannot.
If you need an api with credentials, you should also know all of the possible sources that can get these credentials. Set the allow-origin to your localhost and or development server, and your eventual production address as outlined below.
I'm assuming you're using the cors package on your NodeJS server.
It should be configured to accept the cookies too. It would look something like this in the case of an Express Server.
app.use(cors({
origin:["http://localhost:3000"],
credentials:true
}));
Additionally, as you correctly said, you would send the credentials along with any UI side calls to the API.
I got around it by doing this (I am using the cors package from NPM):
app.use(cors({
origin:true,
credentials:true
}));
According to the docs:
origin: Configures the Access-Control-Allow-Origin CORS header.
Possible values:
Boolean - set origin to true to reflect the request origin, as defined by req.header('Origin'), or set it to false to disable CORS.
This makes it so that cors will essentially accept any origin based on the request header, while also allows the setting of cookies.

Access to XMLHttpRequest ' from origin xxxx blocked by CORS policy: Response to preflight. Redirect is not allowed for a preflight request

I am using Azure maps and have no problem with FF or Edge. However Chrome is giving me the following error when trying to display the map:
Access to XMLHttpRequest at atlas.microsoft.com/map/tile/pbf?&view=Unified&language=NGT&api-version=1.0&layer=basic&style=main&zoom=16&x=12586&y=26496&subscription-key=xxxx' from origin mysite.org has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
I have added mysite.org to allowed origins in Azure. Some of the map contents do display such as the location marker and map style selector etc. Only the actual map is being blocked.
How do I resolve this issue?
The CORS error is most likely a red herring here.
What's happening is that your request for some reason receives a redirect from the server, which therefore gets blocked by CORS
It's likely that this particular URL is not intended to be consumed by Ajax but is meant to be rendered directly by a browser. It could, for example, be an authentication issue where the server is redirecting you to a login page or the map server may just want to reformat your URL.
In any case, open the developer tools in a browser, tell it to not clear the requests on navigation and try to put that URL in; you'll see that some kind of redirect happens in the network tab You then need to figure out how to avoid that redirect - or, if this URL type is not intended for Ajax, rethink your design.
I wrote a bit about how CORS works here; https://www.lytzen.name/2016/02/20/playing-with-cors.html, which may or may not help.

Cors and web resource

Im trying to figure what is cors.
In MDN it describe as :
A resource makes a cross-origin HTTP request when it requests a resource from a different domain than the one which the first resource itself serves.
Im not sure I know what is a web resource.
In addition, I understand thats cors allows me to use web resource from another domain in my domain by putting the domain in the header, but is it just convention or something more than that?
Let me try to give a short explanation.
Web resource
A web resource is anything you request on the web. That could be an image, a json payload, a pdf, an html-page etc. There's not more to it than that.
CORS
When you want to do an ajax-request in a browser (typically from javascript), you are typically limited to making requests to resources (url's) on the same domain. Eg. www.x.com can only request resources from www.x.com. Let's imagine you have a web page on www.x.com that want's to get a resource from api.x.com. This will not be possible unless the server (api.x.com) has CORS enabled.
So how does it work? Well, the flow is like this (simplified a lot).
When you do a ajax-request, for instance a GET request for a json payload, the browser sees this and issues an OPTIONS request to server in which it states who it is (www.x.com in the Origin header). The server is then supposed to answer with a response with a header saying that it is ok for www.x.com to do the GET request. The server does this by adding a header Access-Control-Allow-Origin: www.x.com. If the allowed origin matches the origin in the request, the browser issues the GET request and the json payload is returned by the server. If the allowed origin does not match, the browser refuses to do the request and shows an error in the console.
If you are doing the client (www.x.com), and are using - lets say jquery - you don't have to do anything. Everything happens automatically.
If you are doing the server (api.x.com), you have to enabled CORS. How this is done varies a lot but http://enable-cors.org/server.html has a nice guide on how to do it on different server types. They also have some more in depth guides on how it works. Specifically you might wanna take a look here https://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/
I hope this helps you out a bit

chrome app w/node.js back end. App is issuing OPTIONS request in place of PUT

I'm working on a chrome app and finally got to the point of issuing a PUT to the node.js server. My GET logic is working fine. My PUT however gets hijacked into a OPTIONS request. My requests are made to
http://localhost:4000/whatever
I read about the OPTIONS pass asking permission to do the PUT. I was under the impression that BROWSERS issue OPTIONS when CORS is requested, but didn't realize that a chrome app would also do this for me.
Is the app doing this because I didn't and I'm supposed to, or is this SOP that chrome will issue the OPTIONS request and I just issue my PUT that triggers it?
My PUT never makes it to the server. I've tried issuing my own OPTIONS just ahead of my PUT but so far nothing is working. The OPTIONS request makes it to the server (the default one or mine), but that's the end of the conversation.
At the server, all I'm doing to satisfy the OPTIONS request is as follows:
case 'OPTIONS':
res.writeHead(200, {'Access-Control-Allow-Methods': 'OPTIONS, TRACE, GET, HEAD, POST, PUT',
'Access-Control-Allow-Origin': "*"});
break;
When I try issuing my own OPTIONS & PUT requests, I'm doing them with separate XMLHttpRequest objects. I don't see where the permission hand off from OPTIONS to PUT is made.
This is called "preflighting", and browsers MUST preflight cross-origin requests if they fit specific criteria. For example, if the request method is anything other than GET or POST, the browser must preflight the request. You will need to handle these OPTIONS (preflight) requests properly in your server.
Presumably, your page is hosted on a port other than 4000, and the call to port 4000 is then considered cross-origin (in all browsers other than IE). Don't issue the OPTIONS request yourself. Chrome will then preflight your request. Your server must respond appropriately. The browser will handle the response to this OPTIONS request for you, and then send along the PUT as expected if the OPTIONS request was handled properly by your server.
There is an excellent article on Mozilla Developer Network that covers all things CORS. If you plan on working in any cross-origin environment, you should read this article. It will provide you with most of the knowledge necessary to understand the concepts required to properly deal with this type of an environment.

Resources