10

Here is the situation (And I am still new to this platform)... We are building a product using the Force.com Embedded (OEM) model. For clients with existing salesforce org, we would like to have seamless UI integration with the OEM product we built. Deploying in the same org is not an option due to contractual limitations.

Here are the options I can think of so far - 1. Connect the two orgs (Single Sign On), and use an iFrame to display the page in the org. 2. Use Force.com canvas

Force.com canvas is preferred as we get more integration options. However, I come across an issue using SignedRequest authentication. The signed request is not being sent to the org mainly because there is a 'redirect' in the original request, which leads to loss of the posted data. Any pointers on how to solve the issue?

Any pointers on how to do org to org integration using Force.com canvas? I haven't been able to find any resources which show a salesforce app embedded using canvas.

  • Is Oauth acceptable, though? Canvas does support that as an authentication mechanism. – sfdcfox Jul 1 '14 at 15:18
  • The redirect of the signed request will be Salesforce rejecting the request and redirecting to the login page due to the lack of authentication. Are you hosting the canvas app in a public site? – Daniel Ballinger Jul 3 '14 at 20:05
  • Daniel, no the app is not a public site. I used SAML for SSO authentication. – Zuber Jul 19 '14 at 16:38
11
+25

Here is a start. I'll try and flesh it out as time permits.

In the Org that will be the target of the Canvas App

  1. Create an Apex Class to be the Controller. This controller will handle the processing of the signed_request POST data.
    Connected App Controller to process the Signed Request:

    public class CanvasSignedRequestController {
    
        public String signedRequest { get; set; }
        public String jsonEnvelope { get; set; }
    
        public CanvasSignedRequestController() {
            this.signedRequest = ApexPages.currentPage().getParameters().get('signed_request');
    
            if(!String.isBlank(this.signedRequest)) {
                string[] signedRequestLst = signedRequest.Split('\\.');
                System.assertEquals(2, signedRequestLst.size());
    
                // The hashed Based64 context signed with the consumer secret
                string encodedSig = signedRequestLst[0];
                Blob decodedSig = EncodingUtil.base64Decode(encodedSig);
    
                // The context and authorization token JSON encoded in Base64
                String encodedEnvelope = signedRequestLst[1];
                this.jsonEnvelope = EncodingUtil.base64Decode(encodedEnvelope).ToString();
    
                // Continue decoding - to be completed. 
                // Currently left as an exercise for the reader.
                // http://www.salesforce.com/us/developer/docs/platform_connectpre/Content/canvas_app_unsigning_code_example.htm
    
                // Use the HMAC SHA-256 algorithm to hash the Base64 encoded context and sign it using your consumer secret.
                Blob encodedEnvelopeHash = Crypto.generateDigest('SHA-256', Blob.valueOf(encodedEnvelope));
                // How do you sign this with the consumer secret using SHA-256?
                //Crypto.sign('SHA256', encodedEnvelopeHash, EncodingUtil.base64Decode(consumerSecret));
    
                //TODO: Throw an exception here if the hashes don't match. Tampered request.
    
                // Read the Session details and other useful information 
                JSONParser parser = JSON.createParser(this.jsonEnvelope);
            }
        }
    }
    
  2. Create a Visualforce Page and connect to the Controller. Currently this is just to show that the signed_request has been processed.
    Very simple VF page for Site:

    <apex:page controller="CanvasSignedRequestController">
      <h1>Congratulations</h1>
      <p>signedRequest:[<apex:outputText >{!signedRequest}</apex:outputText>]</p>
      <p>jsonEnvelope:[<apex:outputText >{!jsonEnvelope}</apex:outputText>]</p>
    </apex:page>
    
  3. Create a Public Site

    1. Username > Build > Develop > Sites
    2. Set the Clickjack Protection Level to "Allow framing by any page (no protection)"
    3. Add the Visualforce page to the Site Pages
    4. Change the Active Site Home Page to the Visualforce Page
    5. Activate the Site

In the Org that will be hosting the Canvas App

  1. Create a Canvas App
    1. Username > App Setup > Develop > Create > Apps [New: Connected Apps]
    2. Under API (Enable OAuth Settings)
    3. Enabled OAuth Settings
    4. Callback URL: https version of the Sites URL
    5. Selected OAuth Scopes: Full access (full)
    6. Under the Canvas App Settings
    7. Check Force.com Canvas
    8. Enter the Site URL from the other Org using https
    9. Set the Access Method to Signed Request
    10. Locations: Visualforce Page
  2. Create a Visualforce page to host the Canvas App.
    Visualforce page content:

    <apex:page>
        <apex:CanvasApp developerName="SitesApp"/>
    </apex:page>
    
  3. Manage the Connected App

    1. Set OAuth policies to Permitted Users: Admin approved users are pre-authorized
    2. Under Managed Profiles, add System Administrators (or whichever profiles suit)
  4. Preview the Visualforce Page. You should be able to see the Sites page from the first Org receiving the Signed Request Post data.

enter image description here

  • Daniel, is creating a public site mandatory? – Zuber Jul 20 '14 at 7:08
  • @Zuber The POST request that contains the signed data won't include valid session information in the header for the hosting org. Without it the request will be redirected to the login page. Using a public site bypasses this requirement. You could verify that the OrgId specified in the signed request was the expected Org to prevent others from accessing the Canvas App. I don't know how SAML and SSO would affect this flow. I suspect it isn't applicable for a canvas app, but I can't confirm this. – Daniel Ballinger Jul 20 '14 at 9:05
  • @DanielBallinger, i am trying to follow the instructions you have provided in your answer, for the apex crypto sign function EncodingUtil.base64Decode(consumerSecret) your instruction says use the consumer secret, this line did not work since we need the 3rd parameter to be a PKCS8 format key. do we need to use the consumer secret from the connect app to generate the PKCS8 key?. i am not even sure if thats possible – RedDevil Jul 7 '18 at 10:52
  • @RedDevil I see you've created a dedicated question for verifying the signed request. That was the best way to go. – Daniel Ballinger Jul 11 '18 at 23:52

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.