Chrome was working until version 73. Now it is throwing me a CORB warning and stopping my chrome extension from running.
Here is my ajax jquery code, nothing special
$.ajax({
url: this.url + "api/users",
type: 'get',
data: { account_id: this.account_id(), user_id: this.user_id(), person_id: person_id },
success: function (data) {
//do stuff
}
});
I did notice that if I remove the x-content-type-options header so that it no longer reads "nosniff" I can get some Ajax requests to be returned but not others. Not sure if this means anything but I noticed that the json requests that returned an array worked but others did not.
remove_keys = %w(X-Content-Type-Options)
response.headers.delete_if{|key| remove_keys.include? key}
[{'id' : '123'}] <-worked
{'id' : '123'} <- did not work (not sure if means anything)
Full error from chrome
Cross-Origin Read Blocking (CORB) blocked cross-origin response https://ideas.test/api/users?token=W9BDdoiKcXLWSHXWySnwdCV69jz2y&account_id=3098355&user_id=john%40gmail.com&person_id=21046915&sync=false&new=true with MIME type application/json. See https://www.chromestatus.com/feature/5629709824032768 for more details.
Headers from response
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: x-auth_token
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD
Access-Control-Allow-Origin: chrome-extension://mhikhjencpecbhelhjgdcgpdhlhdlhjh
Access-Control-Expose-Headers:
Access-Control-Max-Age: 1728000
Request Headers
Provisional headers are shown
Accept: */*
Origin: chrome-extension://mhikhjencpecbhelhjgdcgpdhlhdlhjh
Referer: https://3.basecamp.com/
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36
How can I get the response body to be returned without chrome removing the body due to CORB?
I found a workaround. Might be an overkill for someone, but it took me 15 mins to fix everythiung. In your content script wrap all your ajax calls into a function:
Add ajaxGet function to your content script:
function ajaxGet(data){
return new Promise(function (resolve, reject) {
chrome.runtime.sendMessage({action: 'ajaxGet', data: data}, function (response) {
console.log(response)
if(response&&!response.statusText){//Might need some work here
resolve(response);
} else {
reject(response)
}
});
});
}
And in your background.js add a listener:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if(request.action=="ajaxGet"){
$.ajax(request.data).then(sendResponse,sendResponse)
return true //telling chrome to wait till your ajax call resolves
}
})
in stead of
$.ajax({
url: this.url + "api/user_boards",
type: 'get',
data: { account_id: this.account_id()}
})
call
ajaxGet({
url: this.url + "api/user_boards",
type: 'get',
data: { account_id: this.account_id()}
}).then(onSuccess, onError) //handle response from here
If you don't want to use jquery in your background.js you can make Xhr call in stead. Something like this:
var data = JSON.stringify(false);
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === this.DONE) {
console.log(this.responseText);
sendResponse(this.responseText)
} else {
//handle errors
}
});
xhr.open("GET", request.data.url);
xhr.send(data);
You'll have to work around headers on your own.
It looks like you're putting the CORS headers in the request. You need to put them in the response instead.
Chrome 73 inject some new security. Just try to move your xHTTP requests to your background script with chrome.runtime.sendMessage and get response with SendResponse callback.
In content or popup script replace ajax with :
chrome.runtime.sendMessage(
{ action: "check", data: {/* params for url */}},
// callback with url response
function(response) {
if( response.success ) {
var myDataFromUrl = response.data;
...
} else {
console.log('Error with `check`,', response.data);
}
}
);
From background script:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
var url = 'https://mysyte.com/';
if(request.action === 'check' ) {
url = url + 'check'
ajax( url, request.data,
success: function( d ) {
sendResponse({success: true, data: d});
},
error : function( d ) {
sendResponse({success: false, data: d});
}
);
}
});
function ajax( url, params, cbSuccess, cbError ) { ... }
After fixing the CSP & CORS issues, I was still getting the warning on the OPTIONS method call (which is done for cross-domain calls).
I fixed it on the server by setting the content-type for the OPTIONS method call (which doesn't return any data) to "application/octet-stream". No more warnings!
Related
I'm pretty new to Laravel, I've been trying to pass parameters to the destroy() action in my controller with Ajax, the action runs as the pictures data gets removed both from the database and the picture from storage as well, however it gives back 405 error in the console for some reason. I've tried multiple solutions posted here, but none has worked unfortunately. The destroy() action works if I'm using it purely with PHP, but I'm trying to learn a bit of Ajax as well, also I'm doing it this way because I want to do it like there are multiple photos on the site at once, and each one has its own delete button, and I want to delete the photo based on which delete button has been pressed.
It says in the response headers that only GET, HEAD allowed.
Any help would be greatly appreciated, and thank you all in advance!
Routes
Route::get('/photos/create/{albumId}', [PhotosController::class, 'create'])->name('photo-create');
Route::post('/photos/store', [PhotosController::class, 'store'])->name('photo-store');
Route::get('/photos/{id}', [PhotosController::class, 'show'])->name('photo-show');
Route::delete('/photos/{id}', [PhotosController::class, 'destroy'])->name('photo-destroy');
Controller
public function destroy($id)
{
$photo = Photo::find($id);
if (Storage::delete('/public/albums/' . $photo->album_id . '/' . $photo->photo ))
{
$photo->delete();
return redirect('/')->with('success', 'Photo deleted successfully');
}
}
Ajax
$(document).on('click', '.deletePhoto', function(e) {
var photo = $(this).val();
$('#deletePhotoId').val(photo);
$('#photoDelete').modal('show');
});
$(document).on('click', '.confirmDelete', function(e) {
var photo = $('#deletePhotoId').val();
$.ajax({
type: "DELETE",
url: "/photos/"+photo,
data: {
_token: '{{ csrf_token() }}',
},
/* success: function(response){
alert(response);
},
error: function(response) {
alert('Error' + response);
} */
})
});
Also if I have added the csrf as in the Laravel documentation like this:
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
Then it gave me 419 error no matter where I have placed it.
Here's the error
XHRDELETEhttp://photo-project.test/
[HTTP/1.0 405 Method Not Allowed 61ms]
DELETE
http://photo-project.test/
Status405
Method Not Allowed
VersionHTTP/1.0
Transferred8.43 kB (8.19 kB size)
Referrer Policystrict-origin-when-cross-origin
Allow
GET, HEAD
Cache-Control
no-cache, private
Connection
close
Content-Type
application/json
Date
Sat, 03 Jul 2021 20:21:15 GMT
Server
Apache/2.4.46 (Win64) PHP/7.3.21
X-Powered-By
PHP/7.3.21
Accept
*/*
Accept-Encoding
gzip, deflate
Accept-Language
en-GB,en;q=0.5
Connection
keep-alive
Content-Length
47
Content-Type
application/x-www-form-urlencoded; charset=UTF-8
Cookie
XSRF-TOKEN=eyJpdiI6IlR0bitkaitPMFhhWEdnQ1ZqL1VpTXc9PSIsInZhbHVlIjoibVREOG9JRGpOQjBqZG40QU9vWVJsS2xtT2J3OXZJK3ZjVzNzZHNKNWdQakowK1lMZ1o0RStSQWFzTVFYZ1R5cFEvNjQ2bm9ZNklYbW8xcW54ZVlzOG9sVXJXN1Z3dmU0Lys0UXRWNWZLY29Femxjb2EvS09qM0hzbm9SSndOYXIiLCJtYWMiOiI2MWJlOTc3YWFhY2NkY2VhZGM5YWZhYmE0MjcyYTc5MmRiNmQwMjU0ZmFlZmMxYzEzNTExMGU4ZjlhMTY3OTYwIn0%3D; laravel_session=eyJpdiI6IldCNU9MSHRGbnNJRlEvWDBrMmZzSmc9PSIsInZhbHVlIjoidzhoc0VFajBhZXk2dkFSa2VmNkU2UmVReVZaOFFUeGJPam1pOXI3T3gvR0FFM3crd21SODI1ZWFJZk44UThDM0VjNFdsL2V6bzNvcHk0NG9vQlpoTEtIRlNQOStxaDlvVFUvS01iOEJIUDJzODFyck11ckpZRTRzMHhVYXhHZlYiLCJtYWMiOiI5NGJmMTBlMDhlOWU0OTU4ZDkyZWRhMzlhYzIwNzFkOTAzZWI3M2RjOTEzNzI5NTYyOWFkZWIyOWMyM2E3MmM2In0%3D
Host
photo-project.test
Origin
http://photo-project.test
Referer
http://photo-project.test/albums/12
User-Agent
Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0
X-Requested-With
XMLHttpRequest
Thank you again if you can help me in any measure as I'm kind of stuck here unfortunately.
Double-check that your photo JS variable actually has a value. If it's null for some reason, then the route being constructed would end up being the 'index' one, "/photos/", which of course does not have a DELETE http method associated with it.
Check your browser Network history to be sure. You'll see the route that it's attempting to hit, and can also double-check that against the results of php artisan route:list.
public function destroy(Request $request, $id){
$photo = Photo::find($id);
if (Storage::delete('/public/albums/' . $photo->album_id . '/' .$photo->photo )){
$photo->delete();
//if request is via ajax
if($request->ajax()){
return response()->json(['status'=>true,'message'=>'Photo Deleted Successfully']);
}
//otherwise redirect
return redirect('/')->with('success', 'Photo deleted successfully');
}
}
On Ajax Side
$(document).on('click', '.deletePhoto', function(e) {
var photo = $(this).val();
$('#deletePhotoId').val(photo);
$('#photoDelete').modal('show');
});
$(document).on('click', '.confirmDelete', function(e) {
var photo = $('#deletePhotoId').val();
$.ajax({
type: "DELETE",
url: "/photos/"+photo,
data: {
_token: '{{ csrf_token() }}',
},
dataType:'JSON',
cache:false,
success: function(response){
if(response.status){
console.log('Action Successfull....');
console.log('Response from server : ', response.message)
}
},
error: function(response) {
alert('Error' + response);
}
})
});
Just check the request type while returning response from server, If it is ajax then return the response data otherwise redirect to the page.
I'm trying to call my WebAPI, but I'm always get the error No Access-Control-Allow-Origin header is present
Following the Using Cors article, we have the code below, but it always falls onerror method
// Create the XHR object.
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// XHR for Chrome/Firefox/Opera/Safari.
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// XDomainRequest for IE.
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// CORS not supported.
xhr = null;
}
return xhr;
}
// Helper method to parse the title tag from the response.
function getTitle(text) {
return text.match('<title>(.*)?</title>')[1];
}
// Make the actual CORS request.
function makeCorsRequest() {
// All HTML5 Rocks properties support CORS.
var url = 'http://localhost:56280/api/Meta/GetListMetas';
var xhr = createCORSRequest('GET', url);
xhr.withCredentials = true;
if (!xhr) {
alert('CORS not supported');
return;
}
// Response handlers.
xhr.onload = function () {
var text = xhr.responseText;
var title = getTitle(text);
alert('Response from CORS request to ' + url + ': ' + title);
};
xhr.onerror = function () {
alert('Woops, there was an error making the request.');
};
xhr.send();
}
I've tryied the JSONP also but without success again, falls on error method
$.ajax({
type: "GET",
dataType: 'jsonp',
url: "http://localhost:56280/api/Meta/GetListMetas"
}).success(function (data) {
alert(data)
}).error(function (da) {
});
WebAPI
I made a pretty simple API to do the sample
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.EnableCors();
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Controller
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class MetaController : ApiController
{
public string GetListMetas()
{
return "1";
}
}
I've tryied serialize the return also, bu without success.
Someone can help me ?
UPDATE
Below, I attach the request header
The problem, as Vaughn Okerlund mentioned, is in the request made by the client.
The request is blocked by the server cause it does not recognize the client's request.
You can enabled the origin white-listing the domain where the client is running:
public static void Register(HttpConfiguration config)
{
var corsAttr = new System.Web.Http.Cors.EnableCorsAttribute("http://localhost:7185", "*", "*");
config.EnableCors(corsAttr);
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
As you can see I've enabled all the request for every verb from this domain:
http://localhost:7185
If you configure everything there you don't need
[EnableCors(origins: "*", headers: "*", methods: "*")]
on your controller.
Considering your client is hosted in http://localhost:7185/ you can use jQuery to call the server simply using this syntax:
$.ajax({
type: 'GET',
url: 'http://localhost:56280/api/Meta/GetListMetas',
data: {},
dataType: "json",
// crossDomain: true,
success: function (result) {
alert(result);
},
//complete: function (jqXHR, textStatus) {
//},
error: function (req, status, error) {
alert(error);
}
});
Looking here, it seems the issue is that in your controller's response to the service's request you're sending the wildcard header (*) for the origin rather than specifying a domain to allow. In order to send a credentialed request, the server has to grant access to a specific domain:
Important note: when responding to a credentialed request, server must specify a domain, and cannot use wild carding. The above example would fail if the header was wildcarded as: Access-Control-Allow-Origin: *. Since the Access-Control-Allow-Origin explicitly mentions http://foo.example, the credential-cognizant content is returned to the invoking web content. Note that in line 22, a further cookie is set.
In code the request is sent as:
var ajaxOptions = {
url: 'someUrl',
method: 'POST',
success: function(response) {
...
},
failure: function(response) {
console.log(response);
}
};
var form = this.getView().getForm();
var submitAction = Ext.create('Ext.form.action.Submit', { form: form });
var formInfo = submitAction.buildForm();
ajaxOptions.form = formInfo.formEl;
if (form.hasUpload()) {
ajaxOptions.isUpload = true;
}
Ext.Ajax.request(ajaxOptions);
When a request is sent via Chrome, the 'Authorization' header presents:
Authorization:Basic YWRtaW46YWRtaW4=
When it is sent via Firefox, the header is not included.
Explicitely I don't set user/password. So it's not clear, why and how chrome sends such header. Are there any known issues?
The second, how to force firefox to send such header? Is it possible?
UPDATED
JavaScript does not now anything about login/password. The main question, how Chrome can use them, but other browsers cannot send such pair. So the question is how to force other browsers to send this cookie as Chrome does without appling headers manually via JavaScript.
On the server side, the Servlet API is used. in web.xml:
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>someName</realm-name>
</login-config>
if it does matter
Per the docs, just add a headers property to your ajaxOptions:
ajaxOptions = {
//...
headers : {
'Authorization': 'Basic YWRtaW46YWRtaW4='
}
}
What I'm doing wrong with addon builder? , it is not showing new request response, it show same response all the time, here is my code for main.js :
var tmr = require('timer');
var timus=5000;
var Request = require("sdk/request").Request;
function timer_restarting(){
gint = tmr.setInterval(function() {
Request({
url: "http://mysite.com/data.txt",
onComplete: function (response) {
if(response.text.indexOf('<show>true</show>')>-1){
timus = parseInt(response.text.substring(response.text.indexOf('<interval>')+10,response.text.indexOf('</interval>')));
show(response.text.substring(response.text.indexOf('<message>')+9,response.text.indexOf('</message>')));
tmr.clearInterval(gint);
timer_restarting();
}
}
}).get();
}, timus);
}
timer_restarting();
the addon show every 5 sec same message, it is not updating . I have an impression like it is not doing new request to server. I have changed message but it still show old message. what is the issue?
please
UPDATE:
if I will manually go to that link in browser and refresh it, then addon will refresh the respone as well. Why is that happening ?
Try adding a header for 'Cache-control' to your Request instance, and specify a value of 'no-cache' (or some 'max-age' value), to prevent getting a cached response.
E.g. in your example, between the lines
url: "http://mysite.com/data.txt",
onComplete: function (response) { /* ... */ }
insert the following lines:
url: "http://mysite.com/data.txt",
headers: {
'Cache-control': 'no-cache'
},
onComplete: function (response) { /* ... */ }
I'm having trouble with this code and I can't seem to get it to work. The typical error that I get back for this call is a "Failed to load resource: the server responded with a status of 401 (Unauthorized) " .
$('#btnZendesk').click(function () {
$.ajax({
url: "https://flatlandsoftware.zendesk.com/api/v2/topics/22505987.json",
type: 'GET',
crossDomain: true,
xhrFields: {
withCredentials: true
},
cache: false,
dataType: 'jsonp',
processData: false,
data: 'get=login',
timeout: 2000,
username: "test#test.com",
password: "test",
success: function (data, textStatus, response) {
alert("success");
},
error: function (data, textStatus, response) {
alert(data);
}
});
Problem is that the resource you are trying to access is protected with Basic Authentication.
You can use beforeSend in jQuery callback to add a HTTP header with the authentication details e.g.:
beforeSend: function (xhr) {
xhr.setRequestHeader ("Authorization", "Basic XXXXXX");
}
Alternatively you can do it using jQuery ajaxSetup
$.ajaxSetup({
headers: { 'Authorization': "Basic XXXXX" }
});
EDIT
A few links to the mentioned functions
jQuery.ajaxSetup
jQuery.ajax
EDIT 2
The Authorization header is constructed as follows:
Username and password are joined into a string "username:password" and the result string is encoded using Base64
Example:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
I too got this problem and somehow all solutions from internet either failed or were not applicable due to client webservice restrictions (JSONP, XDR, CORS=true)
For this, I added an iframe in my page which resided in the client;s server. So when we post our data to the iframe and the iframe then posts it to the webservice. Hence the cross-domain referencing is eliminated.
We added a 2-way origin check to confirm only authorized page posts data to and from the iframe.
Hope it helps
<iframe style="display:none;" id='receiver' name="receiver" src="https://iframe-address-at-client-server">
</iframe>
//send data to iframe
var hiddenFrame = document.getElementById('receiver').contentWindow;
hiddenFrame.postMessage(JSON.stringify(message), 'https://client-server-url');
//The iframe receives the data using the code:
window.onload = function () {
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function (e) {
var origin = e.origin;
//if origin not in pre-defined list, break and return
var messageFromParent = JSON.parse(e.data);
var json = messageFromParent.data;
//send json to web service using AJAX
//return the response back to source
e.source.postMessage(JSON.stringify(aJAXResponse), e.origin);
}, false);
}