4

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?

3

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>
| improve this answer | |
3

The only thing I would add to the above is that I also found it tricky to understand how to get the OAuth flow to work in the client. The last step is what to do on the callback URL. For me, this is the same page as the initial URL and I run the following code on load:

// on load, check if this is the popup window, pass the hash to the opener and close
if (window.opener) {
  try {
    window.opener.Sfdc.canvas.oauth
      .childWindowUnloadNotification(window.location.hash);
  } catch (e) {
    // do nothing
  }
  window.close();
}

This grabs the OAuth token from the popup window's URL and saves it before closing the popup and refreshing the main window.

In the SFDC Canvas JavaScript SDK, there is an example callback file, which does that, but not from the same page: https://github.com/forcedotcom/SalesforceCanvasJavascriptSDK/blob/master/callback.html

Once the main page has the OAuth token, that's when you can do the authenticated requests shown above e.g.:

var client = Sfdc.canvas.oauth.client();

// get the context object
Sfdc.canvas.client.ctx(function(ctx) {
  // do something
}, client);

// send a custom event to Salesforce where this Canvas app appears
Sfdc.canvas.client.publish(client, {
  name: 'myevent.snapshotCompleted',
  payload: {some: 'update'}
});
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.