Spring ’15 Preview – CORS for the Force.com REST API

The Spring '15 release delivers support for CORS, allowing external web apps, outside the Force.com Platform, to directly access the Force.com REST API. Learn more about CORS and how to leverage it in your JavaScript apps.

Spring ’15 is shaping up to be a classic – it’s our biggest release ever in terms of ideas delivered, and, for front-end developers, one of the most eagerly awaited ideas has been support for Cross-Origin Resource Sharing (CORS).

What do we mean by ‘Cross-Origin Resource Sharing’? Normally, browsers enforce Same Origin policy, to guard against attacks such as cross-site request forgery (CSRF). CORS loosens the browser Same Origin policy in a controlled manner, allowing JavaScript loaded from one origin, for example, https://myapp.herokuapp.com, to access an API in another, for example, https://na15.salesforce.com, by allowing the API server to provide a list of origins that may be permitted access.

To illustrate the point, here is a Visualforce page that creates a Chatter File with a size of up to 500 MB by uploading binary data (a ‘blob’) via a multipart message, another REST API feature generally available in Spring ’15:

<apex:page docType="html-5.0" title="File Uploader">
  <h3>Select a file to upload as a new Chatter File.</h3>
  <input type="file" id="file" onchange="upload()"/>
  <p id="message"></p>
  <script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
  <script src="{!$Resource.forcetk}"></script>
  <script>
    var client = new forcetk.Client();

    // Get the token from Visualforce
    client.setSessionToken('{!$Api.Session_ID}');

    function upload() {
      var file = $("#file")[0].files[0];
      client.createBlob('ContentVersion', {
        Origin: 'H', // 'H' for Chatter File, 'C' for Content Document
        PathOnClient: file.name
      }, file.name, 'VersionData', file, function(response){
        console.log(response);
        $("#message").html("Chatter File created: <a target=\"_blank\" href=\"/" + 
          response.id + "\">Take a look!</a>");
      }, function(request, status, response){
        $("#message").html("Error: " + status);
      });
    }
  </script>
</apex:page>

(Read more about ForceTK and the createBlob() function in Ringing the Changes in the Force.com JavaScript REST Toolkit).

Since the Force.com REST API is exposed on the Visualforce server, JavaScript code can make API calls without a problem – the script and the API are in the same origin.

Let’s rewrite the sample to run on Heroku, outside the Force.com Platform:

<!DOCTYPE html>
<html>
  <head>
    <title>File Uploader</title>
    <style type="text/css">
      body {
        font-family: Sans-Serif;
      }
    </style>
  </head>
  <body>
    <h3>Select a file to upload as a new Chatter File.</h3>
    <input type="file" id="file" onchange="upload()"/>
    <p id="message"></p>
    <script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
    <script src="forcetk.js"></script>
    <script>
      var client = new forcetk.Client();

      // Using nforce (https://github.com/kevinohara80/nforce) for OAuth
      client.setSessionToken('<%= oauth.access_token %>', 'v33.0', '<%= oauth.instance_url %>');

      // Go direct to the Force.com REST API
      client.proxyUrl = null;

      function upload() {
        var file = $("#file")[0].files[0];
        client.createBlob('ContentVersion', {
          Origin: 'H', // 'H' for Chatter File, 'C' for Content Document
          PathOnClient: file.name
        }, file.name, 'VersionData', file, function(response){
          console.log(response);
          $("#message").html("Chatter File created: <a target=\"_blank\" href=\"<%= oauth.instance_url %>/" + 
            response.id + "\">Take a look!</a>");
        }, function(request, status, response){
          $("#message").html("Error: " + status);
        });
      }
    </script>
  </body>
</html>

Full code for the sample app is in GitHub.

By default, when it is running outside the Force.com Platform, ForceTK assumes that you have deployed a proxy to work around the Same Origin policy, and constructs an appropriate proxy URL. By setting the proxyUrl to null, we force the toolkit to directly access the Force.com REST API.

If you were to run this sample on Heroku, and try to upload a file, you would find that it doesn’t work. You’ll see something like this in the browser developer console:

XMLHttpRequest cannot load https://gs0.salesforce.com/services/data/v33.0/sobjects/ContentVersion/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://myapp.herokuapp.com' is therefore not allowed access.

This is the Same Origin policy in action – the browser does not allow JavaScript loaded from https://spring15rest.herokuapp.com to access https://gs0.salesforce.com/services/data/v33.0/sobjects/ContentVersion/.

Now it’s time for some CORS. From Spring ’15, you can go to Setup | Security Controls | CORS and add a Whitelisted Origin for your server. All you need is the protocol (which must be https), domain name and, optionally, a port – for example, https://myapp.herokuapp.com. You can use the wildcard character (*) as long as it precedes a second-level domain name. For example, https://*.example.com adds all the subdomains of example.com to the whitelist.

With that in place, the Force.com REST API endpoint will provide the correct CORS headers to allow access from the whitelisted origin, and the browser will permit the script to call the API. Our file is successfully uploaded to Chatter Files!

Caveats

There are a couple of limitations in the Spring ’15 CORS implementation:

  • Apex REST Methods are not accessible via CORS.
  • Describe Global (/sobjects) and Query (/query) resources are not accessible either – this is a known issue currently scheduled for resolution in the Summer ’15 release (safe harbor). This is now fixed – Describe Global (/sobjects) and Query (/query) resources are accessible via CORS in Spring ’15!

Sign up here for a Spring ’15 pre-release environment if you don’t already have one, try the sample app, and check back soon for more Spring ’15 features!

Published
January 20, 2015
Topics:

Leave your comments...

Spring ’15 Preview – CORS for the Force.com REST API