[SalesForce] Calling REST API in Canvas with OAuth Webflow (GET)

I am developing a canvas application. For various reasons, I am hosting my application on a static host and use services as back-end. Therefore I am simply using OAuth Webflow (GET) instead of Signed Request (POST), specified at "Build > Create > Apps > Edit > Canvas App Settings > Access Method". My app redirects to request code and page redirects back to my app with code, I go to my back-end service to retrieve access token using code and secret.

Everything is working great for authentication. However, it turns out salesforce does not support CORS and I am unable to directly call REST API just by providing bearer token. I don't want to create my own proxy server as this is against what I am trying to do in the first place.

I found out about the Canvas Javascript SDK which handles the cross origin issues (probably using proxies and window.postMessage). Once authentication is complete, I am loading the hosted canvas-all.js file dynamically. However I am unable to find a way to provide access token and instance url to the SDK. Unfortunately all examples assume the Signed Request approach. I am starting to think that I am on mission impossible.

There is also AJAX Proxy but I guess that is only limited to Visualforce pages but I might be totally wrong.

In general, is there a way to make REST API calls without hosting a proxy script?

Best Answer

Sorry for the delay in answering.

You can do this today with the SDK. There are examples in the SDK on how to use the OAuth method. Essentially, you use canvas to establish the OAuth authentication, and then use the SDK and the OAuth token to get the context portion of the signed request. With that, you can then make the cross domain calls, just as you would with signed request.

An example would be:

<DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>

<title>Test Page for OAUth User Agent</title>

<script type="text/javascript" src="/sdk/js/canvas-all.js"></script>

</head>
<body>
<script>

    if (self === top) {
        // Not in Iframe
        alert("This canvas app must be included within an iframe");
    }

    function contextCallback(msg){
    var html,context;
    if(msg.status !== 200){
      console.log (msg.status);
      return;
    }
    Sfdc.canvas.client.autogrow(Sfdc.canvas.oauth.client());
        Sfdc.canvas.byId('canvas-request-string').innerHTML = jsonSyntaxHighlight(msg.payload);
    }

     function jsonSyntaxHighlight(json) {
        if (typeof json != 'string') {
           json = JSON.stringify(json, undefined, 2);
        }
        json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
        return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
            var cls = 'number';
            if (/^"/.test(match)) {
                if (/:$/.test(match)) {
                    cls = 'key';
                } else {
                    cls = 'string';
                }
                } else if (/true|false/.test(match)) {
                    cls = 'boolean';
                } else if (/null/.test(match)) {
                    cls = 'null';
                }
                   return '<span class="' + cls + '">' + match + '</span>';
             });
    }

    function clickHandler(e) {
        var uri;

        if (! Sfdc.canvas.oauth.loggedin()) {
            uri = Sfdc.canvas.oauth.loginUrl();
            console.log("Login URI: " + uri);
            // This uri is the outer parent window.
       Sfdc.canvas.oauth.login(
        {uri : uri,
        params: {
                  display : "touch",
          response_type : "token",
          client_id : "", //Add Your Consumer ID
          redirect_uri : encodeURIComponent("") //Add your Callback URL
        }});
        }
        else {
            Sfdc.canvas.oauth.logout();
        }
        return false;
    }

    // Bootstrap the pae once the DOM is ready.
    Sfdc.canvas(function() {
        // On Ready...
        var login    = Sfdc.canvas.byId("login"),
            loggedIn = Sfdc.canvas.oauth.loggedin();
        login.innerHTML = (loggedIn) ? "Logout" : "login";
        Sfdc.canvas.byId("access-token").innerHTML = (loggedIn) ? Sfdc.canvas.oauth.token() : "No Token";
        login.onclick=clickHandler;
    if(loggedIn){
      Sfdc.canvas.client.ctx(contextCallback, Sfdc.canvas.oauth.client());
    }
    });

</script>

<style>
    pre {white-space: pre-wrap;padding: 5px; margin: 5px; overflow:auto;width:auto;height:80%;}
      .string { color: green; }
      .number { color: darkorange; }
      .boolean { color: blue; }
      .null { color: magenta; }
      .key { color: red; }
</style> 

<h1 id="header">OAuth Canvas App</h1>
<div>
 <div>
    <a id="login" href="#">Login</a>
  </div>
  <div>
     Access Token: <span id="access-token"></span>
  </div>
  <div id="canvas-content">
    <h4>Once logged in through OAuth, you can see the JSON representation of Canvas Request below:</h2>
    <pre><code id="canvas-request-string"></code></pre>
 </div> 
 </div>
</body>
</html>