Access-Control-Allow-Origin error

SOLVED
Marius
Conversationalist

Access-Control-Allow-Origin error

I am trying to use meraki-dashboard-api-express (Github) and a get the GET request working fine, but when i try to POST i get

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8085' is therefore not allowed access. The response had HTTP status code 404. 

What i found out via google is that something is missing on the Meraki server side, to allow this?

Am i way off, or am i just missing something? 

1 ACCEPTED SOLUTION
HodyCrouch
Building a reputation

You've run into something called the 'Same Origin Policy'.  The policy is implemented in almost every web browser to prevent untrusted web pages from doing bad things to end users.  (see https://en.wikipedia.org/wiki/Same-origin_policy)

 

The header you mention is part of CORS (see https://en.wikipedia.org/wiki/Cross-origin_resource_sharing).  CORS is a way for a server to basically say that it's cool with a specific origin (or any origin) calling the resource/API/whatever.

 

I don't know what Meraki's plans are regarding CORS, but I wouldn't blame them if they decided that permitting CORS across the board would be a bad idea.  It's just too broad of an attack vector with few reasonable use-cases that I can see.

 

Here's how you can get around this:

- figure out how to disable the 'same origin policy' in your browser.  Don't visit normal web sites in this mode.  Really.  I'm not going to help you do this, but Google will 🙂

- Use a plug-in that injects the CORS header on requests (a little less dangerous, but you still need to be careful)

- don't use a browser.  Call the API from server-side code or from the client side using Node, python, whatever

View solution in original post

4 REPLIES 4
HodyCrouch
Building a reputation

You've run into something called the 'Same Origin Policy'.  The policy is implemented in almost every web browser to prevent untrusted web pages from doing bad things to end users.  (see https://en.wikipedia.org/wiki/Same-origin_policy)

 

The header you mention is part of CORS (see https://en.wikipedia.org/wiki/Cross-origin_resource_sharing).  CORS is a way for a server to basically say that it's cool with a specific origin (or any origin) calling the resource/API/whatever.

 

I don't know what Meraki's plans are regarding CORS, but I wouldn't blame them if they decided that permitting CORS across the board would be a bad idea.  It's just too broad of an attack vector with few reasonable use-cases that I can see.

 

Here's how you can get around this:

- figure out how to disable the 'same origin policy' in your browser.  Don't visit normal web sites in this mode.  Really.  I'm not going to help you do this, but Google will 🙂

- Use a plug-in that injects the CORS header on requests (a little less dangerous, but you still need to be careful)

- don't use a browser.  Call the API from server-side code or from the client side using Node, python, whatever

PhilipDAth
Kind of a big deal
Kind of a big deal

@HodyCrouch is 100% right.

 

You can't use client side javascript to reach back to the Dashboard API to make a request.  You can use client side javascript to call back to your server, and then have your server call the Dashboard API and then return the data back to you.

DexterLaBora
Meraki Employee
Meraki Employee

 

Hello,

 

What @HodyCrouch said is correct, and the reason for that github project was to find a solution to build your own front-end application using the Meraki API.

 

I wrote this app to act as a basic starter project and avoid some of the initial challenges I had getting started. I'm working towards a better demo app, but the server/client communication was this focus for this project.

https://github.com/dexterlabora/meraki-dashboard-api-express

 

The NodeJS w/ Express app serves a web page and provides an API proxy to Meraki.

The web page (index.html ) calls '/api/' as the root URL, which the Node server.js handles. 

The Node server will then apply any additional headers, parse the data and make the API request. 

 

So instead of calling the Meraki API directly from the browser, the page would call the local server.

https://api.meraki.com/api/v0/organizations --> http://localhost:8085/api/organizations

 

Additionally, aspecial wrapper for making requests, request-meraki.js , is used to handle multiple redirects in the API call from https://api.meraki.com to the organization's shard, https://n123.meraki.com/

 

More info on the app inner workings:

The sample code was updated to demonstrate a post by creating a new network.

 

Client-Side (create a network by posting data)

 

createNetwork (orgId, data) {
           axios.post('/api/organizations/'+orgId+'/networks', data )
           .then(response => {
             console.log('createNetwork response', response.data);
             this.merakiData = response.data;
           })
           .catch(e => {
            this.errors.push(e)
          })
         },

 

 

Server Side

 

// API Route - Will be proxied through Meraki Dashboard API
app.use('/api', jsonParser, function (req, res){

  // Use client supplied API key or default to server config.
  var apiKey = '';
  if('x-cisco-meraki-api-key' in req.headers){
    apiKey = req.headers['x-cisco-meraki-api-key'];
    console.log("New headers sent", apiKey );
  }else{
    apiKey = configs.apiKey; 
  }
 
  var options = {
    qs: req.query,
    url: configs.apiUrl + req.url,
    method: req.method,
    body: JSON.stringify(req.body), 
    //followAllRedirects: true, // Does not work as intended with PUT,POST,DELETE (returns a [GET] on final location)
    headers: {
        'X-Cisco-Meraki-API-Key': apiKey,
        'Content-Type': 'application/json'
    } 
  }

  requestMeraki(options, function(err, response, data){
    if(err){
        console.log("requestMeraki err ", err)
        res.status(response.statusCode).send({
            message: 'err'
         });
        res.send(err);
    }
    console.log('FINAL res.statusCode ',response.statusCode);
    console.log('FINAL res.body ',response.body);

    res.setHeader('content-type', response.headers['content-type']);
    res.status(response.statusCode).send(data);
    
    
  });

});

 

 

request-meraki.js

 

// Handles Meraki API requests. Has additional logic to follow the HTTP redirects properly.

var request = require('request');
var JSONbig = require('json-bigint')({"storeAsString": true});

// Recursive function to follow Meraki API redirects
var requestMeraki = function (options, callback){  
    request(options, function(error, res, data) {
        if (error) {
            return callback(error);
        } else {
            if ((res.statusCode == '308' || '307' || '302' || '301') && res.headers.location){
                //console.log('REDIRECT: (recursive function)')
                options.url = res.headers.location;
                return requestMeraki(options, function(err, res, data){return callback(err, res, data)});
            }else{       
                // parse the large integers properly if data exists
                try {
                    var parsedData = JSONbig.parse(data);
                    return callback(error, res, parsedData);
                } catch (e) {
                    console.log('error: no data returned ',error)         
                }
                 //console.log("FINISHED")
                return callback(error, res, data);
            }
        }
    });
}

module.exports = requestMeraki;

 meraki vue basic starter.png

Basically, using ajax with local resources doesn't work.

Chrome and Safari has a restriction on using ajax with local resources. This error means that you are trying to perform Ajax on a local file. This is forbidden for security reasons.

In order to solve this problem, you can use firefox or upload your data to a temporary server. If you still want to use Chrome, start it with the below option;

--allow-file-access-from-files

 

Also, this kind of trouble is now partially solved simply by using the following jQuery instruction:

 

<script>
$.support.cors = true;
</script>

 

Get notified when there are additional replies to this discussion.