Navigator.sendBeacon not working with Authorization Headers - asynchronous-postback

Recently chrome stop support for synchronos xmlhttprequest on page unload or beforeunload event https://www.chromestatus.com/feature/4664843055398912
i try this solution Perform an asynchronous service get when closing browser window / tab with Angular but not seems to be working in latest chrome versions
Now i am using navigator.sendbeacon api like this
let headers = {
type: 'application/json; charset=utf-8',
'authorization': `bearer ${token}`
}
let blob = new blob([json.stringify({a:"9"})], headers);
navigator.sendbeacon(uri, blob);
Api is throwing 401 so seems like authorization is not working,
Is there any other alternative to navigator.sendBeacon

At time of this writing, no. Chrome (and probably other browsers too more sooner than later) will disallow XHR-sync because of bad UX to the user (the browser hangs if user is closing the tab and an XHR-sync request is made).
There are a few workarounds though, but each have their drawbacks as well
Use the new (and experimental) sendBeacon API - sendBeacon simply "queues" the request and this guarantees that the request will be fired even on page unload. That too without blocking the UX. Some limitations with this are that you cannot change request headers by default. If you DO need to add custom headers, you will have to use a blob, and that too the headers should be CORS-friendly. And will not work on older browsers (looking at you, IE)
Use fetch() API + keepalive flag - but this again works if you request headers are on the CORS-safelist. Basically if your fetch() request has certain request headers, then a preflight request can be made for security reasons. If such a preflight request is made, then the fetch() + keepalive is disallowed by some browsers. Basically you need to keep your request simple for this to work. For example, Such as you cannot use a content-type=application/json here. One workaround for this is to send data as text/plain and get your server to handle it accordingly.
Some more info on CORS simple vs preflight requests can be found here.
Chrome does allow a temporary workaround but this will work only till Oct 2020. More info on that here.

Related

CSRF and CORS: Why allow the request to happen if we know there will be a cors error?

I am confused by why the cors package allows the request to be processed even if the origin in the request header isn't white-listed. For example, res.status(202).send(await User.find()) returns a response with status code 202, but the data can't be loaded in the Chrome console.
Also, doesn't the browser send preflight OPTIONS requests to know what's allowed; why would it send cookies/credentials along a request with a disallowed origin?
Edit: Tried a post request on jsfiddle and the post request doesn't happen server side. When I said "why the cors package allows" it would be better to say why the browser allows.
CORS is enforced in the browser, not in your server. The server participates in setting headers that the browser can then use to determine whether the request should be allowed or not. But, it is the browser that ultimately decides whether the CORS request satisfies the requirements or not and the result should be passed through to the Javascript in the browser.
Thus, the request is sent to the server, response is received and THEN the browser decides whether the Javascript in the page is allowed to see the result or not.
In some cases where the request is likely to have side effects on the server (based on a set of criteria in the request), the browser will send a pre-flight request to get just the CORS info first.

CORS header Allow-Origin missing in POST requests

I know there are tons of CORS related questions but I can't seem to find the answer to this one.
This is my server side golang code (We are using github.com/rs/cors go module):
We basically have set of apis that require an authorization header and some apis that don't (think checkout vs checkout as guest functionality)
allowedOrigins := []string{"http://localhost:3000", "http://localhost:3001"}
//allowedHeaders := []string{"Authorization"}
c := cors.New(cors.Options{AllowedOrigins: allowedOrigins, AllowCredentials: true})
handler := c.Handler(r)
What i found is the following:
// if allowcredentials is set to true, then all non auth requests go through but all auth requests return cors error
// if allowedHeaders: authorization is set then all **authenticated and NON authenticated** POST requests fail. GET works fine for both cases.
More specifically: The error I get is that AllowedOrigins is not set (??.. I get this in the PRE-FLIGHT OPTIONS response headers) when I try to execute a POST request and I set the AllowedHeaders:authorization option above.
If I comment that line (As you see above) then the non auth requests go through perfectly and the AllowedOrigins hader is sent back in the OPTIONS request..
Fixed it....
https://github.com/rs/cors
Has a nice CorsOptions Debug:true. I used that to inspect what was going on and the moment i hardcoded that I allowed Authorization to come into my server then the POST request was complaining afterwards because I was also sending content-type (automatically sent by client (axios), I didn't specify it).. and Server was saying pretty much "I only recognize authorization header"... I added Content-Type and it now works!
allowedHeaders := []string{"Authorization", "Content-Type"}

Retrieving http response headers information from any website

I'm using Vue CLI and axios.
I have a searchbar where the user can input (potentially) any website and read info about the HTTP request and response.
Some of the information I need to get are: HTTP protocol, Status code, Location (if redirected), Date and Server.
What I'm doing is a simple axios GET request taking the input from the searchbar.
I'm trying to get my head around the CORS domain issues, but even then, when I input a CORS supported site like myjson I can access only the CORS-safelisted response headers which are not what I'm looking for.
This is the axios call:
axios
.get(url)
.then((r) => {
console.log(r);
console.log(r.headers.server); //undefined
})
.catch((e) => {
console.error(e);
});
Is the brief I'm presenting even possible?
UPDATE
I've then tried removing the chrome extension I used to enable CORS requests and installed Moesif Origin & CORS Changer extension. After restarting my PC I have now access to the remaining response headers.
I don't really know exactly what went wrong with the previous extension, but hopefully this helps somebody.
It's also worth pointing out that at the current date I'm writing this edit, myjson site has been flagged by chrome as non-safe for privacy issues. I've simply made HTTP requests to other sites and got the response headers as described.
The response to a cross-origin request for https://myjson.dit.upm.es/about contains the CORS-related headers
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, PATCH, PUT, DELETE, POST, OPTIONS
but no Access-Control-Expose-Headers. Without that, a cross-origin client cannot access the Server header, because it is not CORS-safelisted.
It would work if you had your server make the request and evaluate the headers, not the axios client.

Is there any way to block HTTP requests made by Postman in .NET Core?

I just wanted to know whether is there any way block to HTTP requests made by POSTMAN? Just like browser with the help of CORS allows only specific origins to access a resource. Thanks in advance.
No.
In CORS, it's browser job to block request (or answer), your server does not know "truth" about request. If some power user will disable "following CORS rules" in browser settings/flags - your CORS settings will be ignored.
And even if you will find some "special headers" that POSTMAN will "understand" and refuse to work - there are many other "clients" that can send http(s) requests to server (curl, Fiddler, ...).
I am not aware of anything that gives away the fact that the request is made via Postman.
At the end of the day, Postman is a simple client so the fact that the request is coming through it, or any other client as a matter of fact is irrelevant. Postman's job is to help talk to APIs and even automate this process.
If you are worried about security then secure your API first. Then you wouldn't really care how you get a request, as long as it's authenticated and actually allowed to talk to your API.
This is maybe old for this question but one of the easiest way to handle such situation is to
app.Use(async (context, next) =>
{
if (context.Request.Headers["Referer"].ToString() != "http://localhost:4200/")
{
byte[] data = Encoding.ASCII.GetBytes("Not Recognized Request");
await context.Response.Body.WriteAsync(data);
return;
}
await next();
});
This is useful for .Net core and must set in startup--> configure section.
via this approach you will restrict your API to "Http://localhost:4200" which would be the "Referer" that you want to restrict to.
So because postman has no "Referer" it will get "Not Recognized request" as response.

how to skip Preflight Requset in vue with content-type:application/json

error :"405 not allowed Method" in post method type call in request command vue
i need call api function with content-type:application/json and post Method type with request command in vue ,but browser add preflight request with options method type and it causes this error :"405 not allowed Method"
var options = {
method: "POST",
url: "http://api.sample.com/login",
headers: {
"Access-Control-Request-Method":"POST",
"cache-control": "no-cache",
"content-type": "application/json",
},
body: '{ Username: "demo", Password: "demo", Domain: "test" }'
};
request(options, function(error, response, body) {
if (error) throw new Error(error);
body.data;
alert("ok");
});
The OPTIONS call is done whenever you do a cross-origin request. This means the domain your application is running on is different from the domain where the api is. A pre-flight request is mandatory for these requests, because the browser needs to figure out if you are allowed to do these requests. A 405 error means that the server thinks you are not allowed to make that request.
To solve this problem you can move your api to the same domain as your frontend. Please note that it cannot be on a subdomain.
A different way of solving this, is by sending back the correct headers. In your case you seem to at least miss the Access-Control-Allow-Methods response header. Make sure to send this header and either dynamically figure out which methods are allowed, or do something like the following. That would allow the most common methods to work.
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
In the comments you said that you do not have control over the api, and as such cannot change the response header. In that case your best bet is to contact whoever maintains the api and ask how to best use their api.
In the comments you said that this worked fine when you did the same thing in ASP.NET. ASP.NET is a server-side language, which means that requests in that context do not have a concept of "cross-origin". Cross-origin only comes into play in the browser, where the application runs on an actual domain.
Assuming you can set up a proxy on your application domain, you can also create a proxy that proxies all requests to the api you actually want to communicate with. You would deploy your domain on https://example.com and do your requests to https://example.com/api/endpoint. Your proxy will listen for requests that begin with https://example.com/api and proxy it to https://whatever.the.api.is/ with the appropriate endpoint and data.
Please keep in mind that while some api's might just be configured incorrectly, a lack of cross-origin response headers might just mean that the api is nog meant to be consumed through the browser. Part of this could be that the request contains a secret that should not be exposed to users that use your application, but should instead only be on the server. Using a proxy in that case would set you up for impersonation attacks, because you would expose the secret to your application, but defeat the cross-origin headers by making it appear to the application that the api is on the same domain.