How can I get my express server to redirect the client to a different domain using cors? - firebase

I have an express server that already has cors middlewear enabled.
https://myapi.com
app.use(cors({ origin: true }));
I have a single page application that makes a request and is suppose to get redirect to paypal after. (It gets served from a different origin as listed below)
https://myAngularApp.com (some service)
http.post('https://myapi.com/create-payment', data);
So back in in the express server, I want to send them off to paypal for authentication:
app.post('/create-payment', (req, res) => {
res.redirect('https://www.sandbox.paypal.com/somewhere..');
})
Back in the client I get the following error:
Failed to load https://www.sandbox.paypal.com/somewhere: Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'null' is therefore not allowed access.
Looking at the request my client makes to paypal, you can also see that the origin is null.
(Just to note, disabling app.use(cors({ origin: true })); won't allow the client to get a normal response from the server, so this already shows that the cors middleware is linked up.
Error when commenting out cors
// app.use(cors({ origin: true })); - Commented Out
Failed to load "https://myapi.com/create-payment": Redirect from
'https://myapi.com/create-payment' to
'https://www.sandbox.paypal.com/somewhere.' has been blocked by CORS
policy: No 'Access-Control-Allow-Origin' header is present on the
requested resource. Origin 'https://myAngularApp.com' is therefore not
allowed access.
What else do I need to setup on the express server so that the client can be redirected to paypal?

Redirects work like this:
Client makes HTTP request
Server makes HTTP response that includes an instruction to request a different URL
Client makes HTTP request to the different URL
Server (possibly a different server) makes HTTP response
If, at step 2, the server grants permission to read the response via CORS, then that grants permission for that request.
There is no way for the response at step 2 (which is being made by your server) to grant permission to read the response at step 4 (which is being made by PayPal's server).
If Paypal doesn't grant permission with CORS, then your JavaScript cannot read the response.
(Just imagine if that weren't the case: EvilHacker.Net grants permission with CORS, then redirects to GMail.com, and then EvilHacker can read all your email!)

Related

Request blocked by CORS policy only when app is hosted in Firebase

My Flutter web app needs to call an API when the user submits a contact form. The API is that of a discord bot that proceeds to post the message in a specific channel on my Discord server. This setup works fine for two other apps that are using the same dependencies and the same production environment (Firebase hosting), but on this specific app it throws the following error:
Access to XMLHttpRequest at
'https://discordapp.com/api/channels/689799838509957177/messages' from
origin 'https://autonet.tk' 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 I add the header 'Access-Control-Allow-Origin': '*' to the request, I just get a XMLHttpRequest error
My code:
import 'package:http/http.dart' as http;
var resp = await http.post(
"https://discordapp.com/api/channels/689799838509957177/messages",
headers: {
'Authorization': "Bot " + botToken,
},
body: {
"content": "NEW MESSAGE: " + body
});
Making it harder to triangulate the root cause is the fact that this runs fine on my local machine. It's only once I deploy the app to Firebase hosting that I get that CORS error.
Another thing worth noting is that on the Discord side, there is no configuration that I had to make in order to accept the incoming request for the other two web apps that work fine using the same code. (There is no list of allowed hosts).
'Access-Control-Allow-Origin' it's a header that must be in the response, not in the request. For more information, I suggest you this read:
https://stackoverflow.com/a/20035319/14106548
EDIT:
After a small research I think you are calling the wrong endpoint. This is why the response doesn't have the proper header attached.
The endpoint is:
https://discord.com/api
For more info look at: https://discord.com/developers/docs/reference

Nginx using CORS with credentials

I'm working on building a web application that communicates with a Laravell API through an Nginx server. I tried following the directions on the Nginx website for wide open cors, but it doesn't like the wild card response when sending credentials.
Access to fetch at 'https://api.***.com/' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '' when the request's credentials mode is 'include'.
The API server requires a Bearer access token to authenticate, and each endpoint is at its own path on the server. What is the correct way to configure Nginx in this scenario?
The error message is right, you can't use a wildcard origin and credentials:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
For requests without credentials, the literal value "*" can be specified, as a wildcard; the value tells browsers to allow requesting code from any origin to access the resource. Attempting to use the wildcard with credentials will result in an error.
Instead, just pass back the actual origin, the one that arrived in the Origin HTTP header, then it will always match:
add_header Access-Control-Allow-Origin $http_origin always;

Sec-Fetch-Mode instead of Preflight

I created login FE and finished it.
And as per usual my goto for ajax was Axios. My code is as follows.
const baseUrl = http://localhost:5000/project/us-central1/api
Axios.post(
`${baseUrl}/v1/user/login`,
{ ...data },
{
headers: {
Authorization: 'Basic auth...'
}
},
).then(r => console.log(r).catch(e =>console.log(e));
Now when i try to send request to my local firebase cloud function.
I get a 400 bad request.
after checking the request, I was wondering why it wasn't sending any preflight request, which it should do(to the best of my knowledge) but instead I saw a header named Sec-Fetch-Mode. I searched anywhere it's a bit abstract. And I can't seem to figure anything why my request still fails.
Is there anything Im missing in my config of axios?
My FE is running on a VSCode Plugin named live server(http://127.0.0.1:5500)
Also, my firebase cloud function has enabled cors
// cloud function expres app
cors({
origin: true
})
Any insights would be very helpful.
The OPTIONS request is actually being sent, because you are sending a cross-origin request with an Authorization header which is considered as non-simple. It doesn't show in developer tools because of a feature/bug in Chrome 76 & 77. See Chrome not showing OPTIONS requests in Network tab for more information.
The preflight request is a mechanism that allows to deny cross-origin requests on browser side if the server is not CORS aware (e.g: old and not maintained), or if it explicitly wants to deny cross-origin requests (in both cases, the server won't set the Access-Control-Allow-Origin header). What CORS does could be done on server side by checking the Origin header, but CORS actually protects the user at browser level. It blocks the disallowed cross-origin requests even before they are sent, thus reducing the network traffic, the server load, and preventing the old servers from receiving any cross-origin request by default.
On the other hand, Sec-Fetch-Mode is one of the Fetch metadata headers (Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site and Sec-Fetch-User). These headers are meant to inform the server about the context in which the request has been sent. Based on this extra information, the server is then able to determine if the request looks legitimate, or simply deny it. They exist to help HTTP servers mitigate certain types of attacks, and are not related to CORS.
For example the good old <img src="https://mybank.com/giveMoney?amount=9999999&to=evil#attacker.com"> attack could be detected on server side because the Sec-Fetch-Dest would be set to "image" (this is just a simple example, implying that the server exposes endpoints with the GET method with unsafe cookies for money operations which is obviously not the case in real life).
As a conclusion, fetch metadata headers are not designed to replace preflight requests, but rather to coexist with them since they fulfill different needs. And the 400 error has likely nothing to do with these, but rather with the request that does not comply with the endpoint specification.
You are missing a dot on your spread operator, this is the correct syntax:
{ ...data }
Note the three dots before “data”.
Please see the use of spread operators with objects here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax

How does a server know a request is sent with credentials?

I've been having a problem that just cropped up when requesting a resource from another server through my website.
I request the resource (a PDF file requested through Range Requests).
The browser (Chrome in this case) sends an OPTIONS request to the server.
The server returns 200 from the OPTIONS, but one of the headers is - Access-Control-Allow-Credentials: true
Since the server exposes the wildcard * for the Access-Control-Allow-Origin response header, Chrome then (I think) throws out the following responses from the server and gives me an error saying that the server can't expose the wildcard if the request is made withCredentials = 'include'.
Now, I'm not setting the withCredentials flag anywhere in my code that I can see and I can't seem to find out how the server knows if a request is sent withCredentials. My cookies aren't sent with the OPTIONS or following GET/PARTIAL CONTENT requests. There's no other special headers in the request that I can see.
So,
How does the server know and how can I tell on the client side wither credentials are set to include?
If I'm not setting credentials on my side, what could be causing Chrome to think that the mode I'm using is withCredentials = 'include'?
Should the server be sending back the Access-Control-Allow-Credentials: true header on ALL requests after the OPTIONS?
How does the server know and how can I tell on the client side wither credentials are set to include?
The receiving server doesn’t know anything about the client-side withCredentials setting. The server just receives some form of credentials in the request, or doesn’t. And as far as the CORS protocol goes, the receiving server doesn’t change its behavior based on whether the request includes credentials or not. The receiving server either just sends back the Access-Control-Allow-Credentials: true response header, or doesn’t.
If I'm not setting credentials on my side, what could be causing Chrome to think that the mode I'm using is withCredentials = 'include'?
The answer to that is, it’s subjective — it depends on who the server admin intends the response for. But best practice is, you probably only want to send back Access-Control-Allow-Credentials: true to particular origins that you know and explicitly want to allow. That’s why the CORS protocol has the restriction that it won’t allow your frontend code to access a response if the request has credentials and the response has the Access-Control-Allow-Origin: * (wildcard) header value.
Should the server be sending back the Access-Control-Allow-Credentials: true header on ALL requests after the OPTIONS?
Chrome would only think that if the mode actually really is withCredentials = 'include'. So either some part of your client code is actually setting withCredentials = 'include' — or else, withCredentials = 'include' isn’t actually getting set all but you for some reason just think it is.

Vue-Request not sending Authorization Header

I'm using VueJS with vue-request for http requests. I'm trying to subscribe an user to a Mailchimp list but Mailchimp uses BasicAuth, so I'm doing as such:
scope.$http.post('https://us15.api.mailchimp.com/3.0/lists/listid/members',
{...mydata...}, {headers: {Authorization: 'Basic myencodedAPIkey'}})
But I get an error from the API: 401 Unauthorized - Your request did not include an API key.
So I check the Network log on Chrome and the Authorization is on my headers like this: **Access-Control-Request-Headers: authorization** but it should be like **Authorization: myencodedAPIkey**
On the Console the error appears as:
XMLHttpRequest cannot load
https://us15.api.mailchimp.com/3.0/lists/listid/members. Response
to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://127.0.0.1:8000' is therefore not allowed
access. The response had HTTP status code 401.
When I use Postman it works just fine as the header is correctly sent.
This problem seems to have been solved here with setting the header on every request
https://laracasts.com/discuss/channels/vue/how-to-solve-the-allow-control-allow-cross-in-the-vuejs-header-request-setting?page=2
and here through setting it once
Vue-Request not sending Authorization Header
You are getting CORS error, when you are trying to request from one host to another, and the 'another' part does not allow it to happen. To prevent this error you can use webpack proxy configuration, so this way you do not have cross origin request, but I don't know how you will deal with this in production environment if your api does not allow cross origin requests.
In a project I'm working on, our devServer configuration is as follow
proxy: {
'/api': {
target: 'http://localhost:8080/'
}
},
with this, any request happening on /api/any/url will be redirect to localhost:8080/api/any/url

Resources