Having trouble with cross domain CORS request - javascript

I am using PHP for server-side and Backbone.js on front end. I can do GET and POST requests, but can't seem to get a PUT or DELETE to work.
I am not 100% sure where the best place to put headers are. I have them in two places. One in the .htaccess file as follows.
#Header always set Access-Control-Allow-Origin "http://www.fitnesstimer.dev"
#Header always set Access-Control-Allow-Methods "POST, GET, PUT, DELETE, OPTIONS"
#Header always set Access-Control-Allow-Headers "origin, x-requested-with, Content-Type,X-Custom-Header"
#header always set Access-Control-Allow-Credentials "true"
Then in the header of my Controller I have this.
header('Access-Control-Allow-Origin: http://www.fitnesstimer.dev');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Origin, Content-Type, Accept, Authorization, X-Request-With, X-Custom-Header');
header('Access-Control-Allow-Credentials: true');
My AJAX call looks like this.
$.ajax({
type: 'PUT',
url: 'http://www.fitnesstimerapi.dev/user/deleteuser/' + userId,
contentType: "application/json",
xhrFields: {withCredentials: true },
dataType: "text",
success: function() {
alert( 'User removed!' );
},
error: function( err ) {
console.log( "ERROR: ", err );
}
});
I have tried both PUT and DELETE as the type and neither works. I get the following error.
XMLHttpRequest cannot load http://www.fitnesstimerapi.dev/user/deleteuser/11. Response for preflight has invalid HTTP status code 404

Thanks to the comment by jfriend00:
The mention of "preflight" in the error means that the browser is sending the "OPTIONS" command and not getting the answer it expects from your server. Does your PHP properly implement the "OPTIONS" request as part of your CORS support
I found the answer.
I added this to my index.php file.
// respond to preflights
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
// return only the headers and not the content
// only allow CORS if we're doing a GET - i.e. no saving for now.
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']) &&
$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'] == 'GET' &&
isset($_SERVER['HTTP_ORIGIN']) &&
is_approved($_SERVER['HTTP_ORIGIN'])) {
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: X-Requested-With');
}
exit;
}

Related

405 Method Not Allowed when using headers in the fetch api

I am trying to Make an ajax request using fetch, and when I do, I get a 405 (Method Not Allowed) error.
I am executing it like this:
fetch(url, {
method: 'get',
headers: {
'Game-Token': '123'
}
});
And that is giving me an error. If I remove the headers, the request goes through. However, I need that header for validation on the server.
fetch(url, { method: 'get' });
I have the following setup in my .htaccess file:
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS, FETCH"
Header set Access-Control-Allow-Credentials "true"
Header set Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization, X-CSRF-TOKEN, Game-Token, developerKey"
Header set X-Frame-Options "SAMEORIGIN"
Header set Access-Control-Expose-Headers "Game-Token"
I am not sure what is causing this to not go through.
So, this had nothing to do with JavaScript or the .htaccess. Instead it has to do with Lumen. We need to catch the OPTIONS request and reply back. What we did was create a middleware file that checked for the OPTIONS method and responds with a 200.
use Closure;
class CorsMiddleware
{
public function handle($request, Closure $next)
{
if ($request->isMethod('OPTIONS'))
{
return response('',200);
}
return $next($request);
}
}

jQuery/Ajax Rest api request

I'm trying to make an rest api request to realla https://realla.co/api/#/ from localhost, I have an api key and can make the request via PHP but having issues using ajax:
var URL = "https://realla.co/api/v1/listings/search";
var usr = 'api';
var psw = 'hidden';
$.ajax({
type: "POST",
dataType: "json",
contentType: "application/json",
url: URL,
crossDomain: true,
beforeSend: function(xhr) {
xhr.setRequestHeader("Authorization", "Basic " + btoa(usr + ":" + psw))
},
success: function(result) {
console.log('success');
},
error: function(req, status, err) {
console.log('Something went wrong', status, err);
}
});
Returns the error
XMLHttpRequest cannot load https://realla.co/api/v1/listings/search. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://dw.dev' is therefore not allowed access. The response had HTTP status code 403.
I tried to fix that problem with https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi?hl=en but still get the same message.
I've added the following function (running Wordpress):
/*
* Modify HTTP header
*/
function new_headers($headers) {
if (!is_admin()) {
$headers['Access-Control-Allow-Origin'] = '*';
$headers['Access-Control-Allow-Methods'] = 'GET, POST, PATCH, PUT, DELETE, OPTIONS';
$headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, X-Auth-Token';
}
return $headers;
}
add_filter('wp_headers', 'new_headers');
I've tried other APIs (Facebook) and that works but I wonder if I'm missing something with this.
The documentation for Realla is pretty thin, but I wonder if I'm doing something wrong.
Thanks
let's try this
it will work
Add header in requested resource file
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token');
?>
Issue was realla's side. The API prevents requests being made via ajax.
In order to use the API, you are required to make requests from the server rather than the clients browser.
Sorry to waste people's time, hopefully this answer will help anyone else who feels like they're banging their head against a brick wall.

CORS Post Request Fails

I built an API with the SLIM Micro-Framework. I setup some middleware that adds the CORS headers using the following code.
class Cors{
public function __invoke(Request $request, Response $response, $next){
$response = $next($request, $response);
return $response
->withHeader('Access-Control-Allow-Origin', 'http://mysite')
->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
}
}
For my front-end, I used VueJS. I setup VueResource and created a function with the following code.
register (context, email, password) {
Vue.http({
url: 'api/auth/register',
method: 'POST',
data: {
email: email,
password: password
}
}).then(response => {
context.success = true
}, response => {
context.response = response.data
context.error = true
})
}
In chrome, the following error is logged to the console.
XMLHttpRequest cannot load http://mysite:9800/api/auth/register. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://mysite' is therefore not allowed access.
Oddly enough, GET requests work perfectly.
You half 1/2 the solution here.
What you are missing is an OPTIONS route where these headers need to be added as well.
$app->options('/{routes:.+}', function ($request, $response, $args) {
return $response
->withHeader('Access-Control-Allow-Origin', 'http://mysite')
->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
});
This happens because preflight request is of OPTIONS type. You need to make an event listener on your request, which checks the type and sends a response with needed headers.
Unfortunately i don't know Slim framework, but here's the working example in Symfony.
First the headers example to be returned:
// Headers allowed to be returned.
const ALLOWED_HEADERS = ['Authorization', 'Origin', 'Content-Type', 'Content-Length', 'Accept'];
And in the request listener, there's a onKernelRequest method that watches all requests that are coming in:
/**
* #param GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event)
{
// Don't do anything if it's not the master request
if (!$event->isMasterRequest()) {
return;
}
// Catch all pre-request events
if ($event->getRequest()->isMethod('OPTIONS')) {
$router = $this->container->get('router');
$pathInfo = $event->getRequest()->getPathInfo();
$response = new Response();
$response->headers->set('Access-Control-Allow-Origin', $event->getRequest()->headers->get('Origin'));
$response->headers->set('Access-Control-Allow-Methods', $this->getAllowedMethods($router, $pathInfo));
$response->headers->set('Access-Control-Allow-Headers', implode(', ', self::ALLOWED_HEADERS));
$response->headers->set('Access-Control-Expose-Headers', implode(', ', self::ALLOWED_HEADERS));
$response->headers->set('Access-Control-Allow-Credentials', 'true');
$response->headers->set('Access-Control-Max-Age', 60 * 60 * 24);
$response->send();
}
}
Here i just reproduce the Origin (all domains are allowed to request the resource, you should probably change it to your domain).
Hope it will give some glues.
Actually CORS is implemented at browser level. and Even with
return $response
->withHeader('Access-Control-Allow-Origin', 'http://mysite')
->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
chrome and Mozilla will not set headers to allow cross origin. So, you need forcefully disable that..
Read more about disabling CORS
Disable same origin policy in Chrome
CORS can be hard to config. The key is that you need to set the special headers in your server and your client, and I don't see any Vue headers set, besides as far as I know http is not a function. However here is some setup for a post request.
const data = {
email: email,
password: password
}
const options = {
headers: {
'Access-Control-Expose-Headers': // all of your headers,
'Access-Control-Allow-Origin': '*'
}
}
Vue.http.post('api/auth/register', JSON.stringify(data), options).then(response => {
// success
}, response => {
// error
})
Notice that you need to stringify your data and you need to expose your headers, usually including the Access-Control-Allow-Origin header.
What I did in one of my own apps was to define interceptors so I don't worry to set headers for every request.
Vue.http.headers.common['Access-Control-Expose-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, x-session-token, timeout, Content-Length, location, *'
Vue.http.headers.common['Access-Control-Allow-Origin'] = '*'

Angular http post request - No 'Access-Control-Allow-Origin' header is present on the requested resource

I am trying to send data to servlet using angular http post,
var httpPostData = function (postparameters,postData){
var headers = {
'Access-Control-Allow-Origin' : '*',
'Access-Control-Allow-Methods' : 'POST, GET, OPTIONS',
'Accept': 'application/json'
};
return $http ({
method : 'POST',
url : 'http://localhost:8080/json/jasoncontroller',
params : postparameters,
headers: headers,
data : postData
}).success (function (responseData){
return responseData.data;
})
}
But i am keep on getting error that No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
I did have set following headers on my servlet
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
response.addHeader("Access-Control-Max-Age", "3600");
response.addHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
if i remove data from http post it works fine, but no luck with data.
Actually what happens is in some frameworks two calls are made,
OPTIONS which checks what methods are available,
And then there is the actual call.
OPTIONS require just empty answer 200 OK
if(request.methord == 'OPTIONS'){
res.send(200);
} else {
next();
}
You can also install this chrome extension to enable cors, that is the easy way out !

How to handle custom headers with CORS Pre-flight request? AJAX - CodeIgniter

I'm working with CodeIgniter and the Restfull API to structure my web server private API.
I've started using CORS as per requirements for some framework I'm using.
Working with Jquery, I can see 2 requests are sent, first one as OPTION type - as expected - but without my custom header (X-API-KEY for security, by default in CodeIgniter Restful API).
I then receive an invalid API Key error message as shown in the picture. Then right after the proper request is being sent with correct headers but in the meantime, the first requests triggered the .fail() function to handle errors.
What's best practice to handle that scenario ? i would like my ajax request to smoothly handle the first preflight OPTION request without triggering an error on my app as it does today, then do the normal GET call with custom headers as per how CORS works and execute the success call without never triggering the error call in the first preflight request ?
triggerFriendsWall: function() {
//Get location
var options = {
timeout: 30000,
enableHighAccuracy: true,
maximumAge: 90000
};
//We need to check if user has disabled geolocation, in which case it makes the app crashes ! (from Cordova.js 1097)
var position = JSON.parse(localStorage.getItem("position"));
if (position == "" || position == null || position == "null" || typeof position == "undefined" ) {
// In this case we have never set location to anything and the user has enabled it.
navigator.geolocation.getCurrentPosition( function(position) {
home.friendsWall(position);
}, function(error) {
common.handle_errors(error, home.friendsWall);
}, options);
} else {
// in this case, user has disabled geolocatoin !
common.handle_errors(false, home.friendsWall);
}
},
friendsWall: function(position) {
$.when(UserDAO.getUsersNearby(position.coords.latitude, position.coords.longitude, home.Usr_radius, home.Usr_limit, home.Usr_offset))
.done(function(response) {
// Do stuff
})
}
getUsersNearby: function(lat, lng, radius, limit, offset) {
var key = localStorage.getItem("key");
return $.ajax({
type: "GET",
url: config.server_url + 'user/usersSurrounding',
headers: {
'X-API-KEY': key
},
data: {
lat: lat,
lng: lng,
radius: radius,
limit: limit,
offset: offset
},
dataType: 'json'
});
},
Many thanks
EDIT:
This is the constructor associated to all my controllers ( all controller extend a single controller where construct method is : )
public function __construct()
{
header('Access-Control-Allow-Origin: *');
header("Access-Control-Allow-Headers: X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE");
$method = $_SERVER['REQUEST_METHOD'];
if($method == "OPTIONS") {
die();
}
parent::__construct();
// $this->load->model('admin_model');
$this->load->library('session');
$this->load->library('key');
}
Are you using Access-Control-Allow-Headers?
Used in response to a preflight request to indicate which HTTP headers
can be used when making the actual request.
Try adding the following header to your preflight code.
header("Access-Control-Allow-Headers: content-type, origin, accept, X-API-KEY");
I recall having similar issues, seem to recall some of it being browser specific too...
If it helps here is a snippet from some code I know works:
// CORS and other headers. Make sure file is not cached (as it happens for example on iOS devices)
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Max-Age: ' . CORS_AUTH_MAX_AGE);
//CORS preflight
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
header("Access-Control-Allow-Headers: content-type, origin, accept, x-app-sig");
$acrh = explode(',', strtolower($headers['Access-Control-Request-Headers']));
foreach ($acrh as $k => $v) {
$acrh[$k] = trim($v);
}
if (! isset($headers['Access-Control-Request-Headers']) || ! in_array('x-app-sig', $acrh)) {
_log($h, '*** Bad preflight!' . PHP_EOL . print_r($headers, true) . PHP_EOL . print_r($_REQUEST, true));
header("HTTP/1.1 401 Unauthorized");
exit; //->
}
_log($h, '+++ Successful preflight.' . PHP_EOL . print_r($headers, true) . PHP_EOL . print_r($_REQUEST, true));
exit; //->
}
//Now we are past preflight. Actual Auth happens here, I check a signature that I post with payload.
Update: OK, think I better understand your question now. Posted a bit more code. First off, yes, we are doing essentially the same thing there. I just check that the preflight tried to white-list what it should have in terms of headers.
I think the part you are missing is that the preflight should/will not have the custom header you are trying to send. See the answer here: How do you send a custom header in a cross-domain (CORS) XMLHttpRequest?). So like I do you could check that the Access-Control-Request-Headers: are sent with the preflight, but you should not check for the actual header being present on that call.
Sounds like you just need to move a little of the code around server side - make the preflight pretty vanilla and dumb, then do your actual auth or checking of custom headers after successful preflight.
I use a HMAC signature sent with the payload myself to authenticate things after the preflight. I also check that the custom x-app-sig is supplied and what i expect though that is probably redundant.
I have been dueling with this issue for two days. It comes out, in the end, there must be a flux for this requisitions and it is quit simple. First you have to allow the header fields that are being sent (with all the CORS headers), in my case it was:
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Allow-Headers: authorization, content-type, x-requested-with');
Then I simply had to return status "204 No Content" whenever an request OPTIONS method came. You can use a condition statement like so:
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header('HTTP/1.1 204 No Content');
}

Categories

Resources