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

I am working with CodeIgniter and Restfull API to structure a private web server API.

I started using CORS as required for some of the frameworks I use.

Working with JQuery I can see that 2 requests are being sent, first as type OPTION - as expected, but without my custom header (X-API-KEY for security by default in CodeIgniter Restful API).

Then I get the wrong API key error message as shown in the picture. Then right after sending the correct request with the correct headers, but at the same time the first requests called the .fail () function to handle the errors.

First Pre Flight call trigger invalid API KEY since we can't pass the X-API-KEY custom header in this callThe normal call working fine

What's the best practice to handle this scenario? I would like my ajax request to smoothly handle the first OPTION pre-request request without triggering an error in my application like it does today, and then makes a normal GET call with custom headers according to how CORS works and makes a successful call, never without causing an 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 with all my controllers (the whole controller extends one controller where the build 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');
}

      

+3


source to share


2 answers


Are you using Access-Control-Allow-Headers ?

Used in response to a pre-check 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 remember having similar problems, it looks like some of them are also browser specific ...

If it helps here, this is a snippet from some code that 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, consider that now I understand your question better. Added a little more code. First, yes, we are doing essentially the same thing. I'm just checking that the preflight check was trying to whitelist what it should have in terms of headers.

I think the part you are missing is that the preflight command should / should not have a custom header that you are trying to send. See the answer here: How do you send a custom header in cross-domain (CORS) XMLHttpRequest? ). So like me, you can check what are Access-Control-Request-Headers:

sent with a preflight badge, but you shouldn't check for the actual header present in that call.

It looks like you just need to move the server side code a bit - make the preflight look pretty vanilla and dumb and then do the actual authentication or validation of the custom headers after a successful preflight.

I am using an HMAC signature sent with the payload to authenticate things after preflight. I will also check that a custom x-app-sig is supplied and what I expect, although this is probably overkill.

+1


source


I have been dueling with this problem for two days. It turns out, in the end, there must be a flow for these applications, and this is simply impossible. First you have to allow sent header fields (with all 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 just needed to return a "204 no content" status whenever an OPTIONS request came in. You can use the condition operator like this:

if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    header('HTTP/1.1 204 No Content');
}

      

0


source







All Articles