Canvas is a great and simple method of including your third party integrations within the Salesforce UI, and PHP is still one of the most commonly used web application languages on the planet – so how do you use these two great technologies together?  Using Canvas with the language of your choice is very similar to integrating with our API’s – the first real obstacle is authenticating the user.  After that you can access our API’s, and Canvas gives a speedy path to doing that with just JavaScript.

There are two ways to authenticate via Canvas: OAuth and a Signed Request.  My recommendation is to try using Signed Request.  If your Canvas app is configured to use it under Connected Apps, the Salesforce Platform will post the encoded authentication information to your application whenever it appears within a Canvas iframe.  So instead of the multi-step OAuth flow, there’s really one here: decode the POST information for the signed request.  Fortunately for me, Ryan Brainard had already added this functionality to the source of the excellent Workbench application – so I just needed to port it to a generic example.

I start the PHP application by turning on error decoding:

<?php
//turn on reporting for all errors and display
error_reporting(E_ALL);
ini_set("display_errors", 1);

Then I grab the Signed Request out of POST, decode and check the authenticity of the signed request by encoding it against your consumer secret:

//In Canvas via SignedRequest/POST, the authentication should be passed via the signed_request header
//You can also use OAuth/GET based flows
$signedRequest = $_REQUEST['signed_request'];
$consumer_secret = $_ENV['secret'];

if ($signedRequest == null || $consumer_secret == null) {
   echo "Error: Signed Request or Consumer Secret not found";
}

//decode the signedRequest
$sep = strpos($signedRequest, '.');
$encodedSig = substr($signedRequest, 0, $sep);
$encodedEnv = substr($signedRequest, $sep + 1);
$calcedSig = base64_encode(hash_hmac("sha256", $encodedEnv, $consumer_secret, true));
if ($calcedSig != $encodedSig) {
   echo "Error: Signed Request Failed.  Is the app in Canvas?";
}

//decode the signed request object
$sr = base64_decode($encodedEnv);

And that’s it – the application now has access to a valid access token and user information.  From here we actually have two choices: we can access the REST API via the Canvas JavaScript SDK, or we can make REST callouts via PHP itself.  The JavaScript SDK has the advantage that I don’t need any additional server-side code to bring information in from Salesforce.  The SDK resolves the usual JavaScript issue of cross-domain security by posting a message to the window itself, which the Canvas iframe knows to receive and respond with results.  This effectively creates a client-side proxy to the API, as JavaScript itself never leaves the current domain to receive data.

Here is some sample JavaScript using the SDK:

<script>
        var url = "/services/data/v26.0/query?q=SELECT+ID,NAME+FROM+ACCOUNT";
	var sr = JSON.parse('<?=$sr?>');

        $(document).ready(function() {
		console.debug(sr);
		$('#user-name').html(sr.context.user.fullName);

	//within a Canvas iFrame, AJAX calls proxy via the window messaging
        Sfdc.canvas.client.ajax(url,
            {	client : sr.client,
                method: 'GET',
                contentType: "application/json",
                success : function(data) {
						console.debug('Got Data');
						console.debug(data);
						$('#accountTable').append(ich.accounts(data.payload));
                }
            });
		});
</script>

Note that we only need to output the Signed Request from PHP – but everything else is being handled on the client.  Since we’ve got access to all the information needed to access the REST API, though, we could also use PHP itself to perform the callout.  Here’s an example of sending a REST query via a PHP library called Httpful, which is an excellent wrapper around curl:

//As of Spring '13: SignedRequest has a client object which holds the pertinent authentication info
$access_token = $req->client->oauthToken;
$instance_url = $req->client->instanceUrl;

//define your URI based on the user's instance
$uri = $instance_url."/services/data/v26.0/query?q=SELECT+ID,NAME+FROM+ACCOUNT";

//create REST request and decode result
$result = \Httpful\Request::get($uri)
    ->Authorization("OAuth ".$access_token)
    ->addHeader("Content-Type","application/json")
    ->send();
$result = json_decode($result);

And now, for either method, we’ll get an application like this in Canvas:

 

Which remember, as of Spring ’13 – you can include within your Visualforce pages.  Hopefully this will be a leg up for PHP developers looking to integrate screens within Salesforce itself. If you want to read more on Canvas in general, head over to the main Canvas wiki page, or check out the recent webinar.  If you would like to look at the full code for the PHP pages here, I’ve got the project up on Github.  That project also includes links to install the Canvas apps into your instance.

As usual, if you’ve got questions or comments – tack them onto the boxes below, or catch me on twitter @joshbirk.

 

tagged , , , , , , Bookmark the permalink. Trackbacks are closed, but you can post a comment.
  • Anonymous

    It should probably be mentioned that this implementation isn’t checking that the request is properly signed, which means it could be insecure.

  • http://xn.pinkhamster.net/ Christian G. Warden

    If you’re just using the token in the payload to call back to Salesforce APIs, you can ignore the signature as in the sample code. But if you want to extract the email address, for example, from the payload and use it to reference local data, you should verify that the signature in the signed request is valid, e.g. http://bit.ly/XSWF3f

  • http://twitter.com/joshbirk Josh Birk

    Excellent catch, guys. Updated the post, code sample and github repo.

  • John Marczak

    This works:
    $secret = ‘YOUR SECRET’;
    $key = ‘YOUR KEY’;

    $req = $_POST['signed_request'];
    $req = explode(“.”, $req);

    $record = json_decode(base64_decode($req[1]));

    $calculatedSig = base64_encode(hash_hmac(‘sha256′, $req[1], $secret, true));
    ($req[0] != $calculatedSig ? die(‘Not a valid request’) : ”);

  • Meeta Chawla

    Is it possible to debug the canvass request being sent out from the vf page …. Either in salesforce los or browser logs ?

  • http://www.openxcell.com/ arnoldgarrets

    I was working with JS as I found Canvas a little bit confusing but again I would like to give myself a chance to use it again with the help of your code. Thanks for this code.