Salesforce Node.js Canvas Signed Request

Introduction

In this blog, I am going to explain how to set up salesforce canvas signed the request with node js. Signed Request is default authentication for canvas app. The signed request authorization flow varies depending on whether the canvas app’s Permitted Users field is set to “Admin approved users are pre-authorized” or “All users may self-authorize”. The signed request containing the consumer key, access token, and other contextual information is provided to the canvas app if the administrator has allowed access to the canvas app for the user or if the user has approved the canvas app via the approve/deny OAuth flow

Setup Connected App
First, we are going to create a connected app to obtain the consumer secret key which we will be used in the signed request.

  1. In Salesforce, from Setup, enter Apps in the Quick Find box, then select Apps.
  2.  In the Connected Apps related list, click New.
  3. In the Connected App Name field, enter NodeJsSignedRequest.
  4. In the Contact Email field, enter your email address.
  5.  In the API (Enable OAuth Settings) section, select the Enable OAuth Settings field.
  6. In the Callback URL field, enter https://localhost:8443/sdk/callback.html.
  7. In the Selected OAuth Scopes field, select Full Access.
  8. In the Canvas App Settings section, select Force.com Canvas.
  9. In the Canvas App URL field, enter https://localhost:8443/
  10. In the Access Method field, select the Signed Request (Post).
  11. In the Locations field, select Chatter Tab and save it.

On the detail page for the canvas app, next to the Consumer Secret field, click the link Click to reveal. The consumer secret is used in the app to authenticate. We will configure the this later in the Heroku apps.

Configure Who Can Access the Canvas App

Now you need to configure the canvas app for the user access

  1. go to “NodeJSSignedRequest” managing connected apps and click on manage and edit policies
  2.  In the Permitted Users field, select “Admin approved users are pre-authorized.” and then save it 
    3. In the Profiles related list, click Manage Profiles.
    4. Select the System Administrator profile and click Save.

Time to Code

1 .create a package.json file as shown below

{
  "name": "signedrequest",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "express": "~4.8.1",
    "body-parser": "~1.2.2",
    "request": "~2.36.0",
    "ejs": "~1.0.0",
    "crypto-js": "^3.1.6"
    },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

2.create index.js file as shown below

var express = require('express'),
  bodyParser = require('body-parser'),
  path = require('path'),
  CryptoJS = require("crypto-js");
var app = express();
var consumerSecret = process.env.CANVAS_CONSUMER_SECRET;

app.use(express.static(path.join(__dirname, 'views')));
app.set('view engine', 'ejs');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ entended: true }));
app.get('/', function (req, res) {
  res.render('index');
});
app.post('/', function (req, res) {
  var signed_req = req.body.signed_request;
  var hashedContext = signed_req.split('.')[0];
  var context = signed_req.split('.')[1];
  var hash = CryptoJS.HmacSHA256(context, consumerSecret);
  var b64Hash = CryptoJS.enc.Base64.stringify(hash);
  if (hashedContext === b64Hash) {
    res.render('index', { req: req.body, res: res.data });
 } else {
    res.send("authentication failed");
  };
})
var port = process.env.PORT || 9000;
app.listen(port);
console.log('Listening on port ' + port);

3. create the index.ejs view inside the “views” folder.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
</head>

<body>
    <%- JSON.stringify(req) %>
</body>

</html>

4. Create Profile to host it to Heroku

web: node index.js

5. Run the following commands from the terminal.

heroku login 
git init
git add .
git commit -m "Commit"
heroku create 
git push heroku master

6. Set the environmental variable to Heroku app. we are going to set the environmental variable with consumer secret which we got in the connected app.

heroku config:set CANVAS_CONSUMER_SECRET=2781208380818866866

7. Now you will see Heroku app URL some think like https://arcane-woodland-93536.herokuapp.com/ .

8. Go to the connected app and edit and replace OAuth callback URL and canvas app URL with Heroku app URL.

9. Now if you can go to chatter page you see the canvas app is returning the response with SignedRequest.

What Signed Request Contains?

The signed request is a string of the following elements concatenated

  • The canvas app consumer secret encrypted with HMAC SHA–256 algorithm
  • A period (“.”)
  • The context and authorization token JSON encoded in Base64

When using a signed request, Salesforce delivers the user context and authentication information to your canvas app URL. To ensure that the signed request is valid, you must verify that the signed request was signed using your specific canvas app consumer secret as explained below.

  1. Receive the POST message that contains the initial signed request from Salesforce.
  2. Split the signed request in the first period. The result is two strings: the hashed Based64 context signed with the consumer secret and the Base64 encoded context itself.
  3. Use the HMAC SHA-256 algorithm to hash the Base64 encoded context and sign it using your consumer secret.
  4. Base64 encode the string created in point 3.
  5. Compare the Base64 encoded string with the hashed Base64 context signed with the consumer secret you received in point 2.

 

 

 

Node Js Heroku Canvas Integration

Introduction

In this blog, I am going to explain Heroku and node js canvas integration. Canvas allows you to integrate web applications with Salesforce in the different places of salesforce like Chatter Tab, Visualforce Page, Lightning Component, Publisher, Chatter Feed, Console, Open CTI, Mobile Navigation, Layouts, and Mobile Cards. In this post, I am going to show the simple table that contains the product master data which is a simple HTML table.

Prerequisites

  1. Git installed on your local machine
  2. Heroku account
  3. Heroku toolbelt installed.
  4. Node.js installed on your local machine

Creating Connected App

In order to integrate the salesforce with Heroku application, you need to create the configure the salesforce connected app as shown.

  • Setup -> Create -> Apps -> Connected Apps-> New
  • Fill  Connected App NameAPI Name, and Contact Email as shown in the image 
  • Check the Enable OAuth Settings in the API (Enable OAuth Settings) section
    • Callback URL: add as https://localhost.com/_callback. Later after deploying node js app to Heroku, we need to change this.
    • Selected OAuth Scopes: select the scopes you want for the NodeJS app access token to grant.  Select as ” Full Access ” for this testing app.
  • In the section, Canvas App Settings type Check Force.com Canvas
  • Canvas App URL: https://localhost.com/canvas/callback.Later after deploying the apps Heroku, we are going to change this URL.
  • Access Method: Signed Request (POST)
  • Locations: choose Chatter Tab and publisher as of now. but you can select one or more based on the where you want to show this the canvas on salesforce.
    Now finally connected App is looking as shown below.

Now you need to enable the app for the profiles or users. To do this,

  • Click on the Manage on the connected App which you created in above .click edit policies
  • In the OAuth policies select Admin approved users are pre-authorized for the Permitted Users field and save it
  • In the Profiles related list add the profiles or permission set which you wanted to grant access

Building Your App on Node Js

Let’s set up the basic app structure. We will use Express to set up a basic Node application. Here is the below package.json file which we will use to build the node js application. The application folder structure is shown below.

package.json

{
    "name": "canvasquickaction",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "dependencies": {
        "body-parser": "^1.15.0",
        "crypto-js": "^3.1.6",
        "express": "^4.13.4"
    },
    "devDependencies": {},
    "scripts": {
        "start": "node index.js",
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "",
    "license": "ISC"
}
  • Once you have created your file structure, set up a Git repo in this directory by running from command. $ git init your command line within this directory.
  • Inside of the public > views > index.html file, add the following code
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        https://code.jquery.com/jquery.js
        <style>
            * {
                margin: 0;
                padding: 0;
                font-family: Lato;
            }
            
            body {
                padding: 0px;
                background: #f6f3f7;
            }
            
            h1 {
                display: block;
                width: 100%;
                background: #64e0ef;
                height: 55px;
                padding-left: 10px;
                color: #fff;
                font-size: 20px;
                line-height: 55px;
                text-shadow: 1px 1px 1px rgba(0, 0, 0, .3);
                box-sizing: border-box;
                text-align: center;
            }
            
            table {
                width: 100%;
                min-width: 500px;
                border-collapse: collapse;
                font-weight: bold;
                color: #6b6b6b;
            }
            
            thead {
                background-color: rgba(29, 150, 178, 1);
                border: 1px solid rgba(29, 150, 178, 1);
                font-weight: normal;
                text-align: center;
                color: white;
            }
            
            tr {
                height: 50px;
                text-align: center;
            }
            
            td {
                box-sizing: border-box;
                text-align: center;
            }
        </style>
    </head>
    
    <body>
        <div>
            <h1>Product Manangement Node Js Heroku Integration </h1>
            <table>
                <thead>
                    <tr>
                        <th>Item Name </th>
                        <th>Master Price </th>
                        <th>Discount </th>
                        <th>Product Description </th>
                        <th>Discount Approval </th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                      
                        <td>Product ACT 100</td>
                        <td>$279.00</td>
                        <td>$29.00</td>
                        <td>Affordable and easy to use, Act!</td>
                        <td>Yes</td>
                    </tr>
                    <tr>
                        
                        <td>Product ACT 200</td>
                        <td>$279.00</td>
                        <td>$29.00</td>
                        <td>Affordable and easy to use, Act!</td>
                        <td>No</td>
                    </tr>
                    <tr>
                        
                        <td>Product ACT 300</td>
                        <td>$279.00</td>
                        <td>$10.00</td>
                        <td>Affordable and easy to use, Act!</td>
                        <td>No</td>
                    </tr>
                    <tr>
                        
                        <td>Product ACT 400</td>
                        <td>$279.00</td>
                        <td>$39.00</td>
                        <td>Affordable and easy to use, Act!</td>
                        <td>Yes</td>
                    </tr>
                    <tr>
                        
                        <td>Product ACT 500</td>
                        <td>$279.00</td>
                        <td>$21.00</td>
                        <td>Affordable and easy to use, Act!</td>
                        <td>Yes</td>
                    </tr>
                    <tr>
                        
                        <td>Product ACT 600</td>
                        <td>$279.00</td>
                        <td>$19.00</td>
                        <td>Affordable and easy to use, Act!</td>
                        <td>No</td>
                    </tr>
                </tbody>
            </table>
        </div>
    
    </html>

     

  • From the command line in the main app directory initialize your Node app by running -.npm install which will load all the node modules from the package.json

In the index.js file add the following code:

var express  = require('express'),
  bodyParser = require('body-parser'),
  path       = require('path'),
  CryptoJS   = require("crypto-js");

app        = express(),
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'bower_components')));

app.use(bodyParser.json()); // create application/json parser
app.use(bodyParser.urlencoded({ entended: true })); //create application/x-www-urlencoded parser

var views = path.join(__dirname, 'public/views');
 var   consumerSecret = process.env.CONSUMER_SECRET;

app.get('/', function (req, res) {
  res.sendFile(path.join(views, 'index.html'));
});

app.post('/', function (req, res) {
  // Desk secret key	
  var shared = consumerSecret;
  // Grab signed request
  var signed_req = req.body.signed_request;
  // split request at '.'
  var hashedContext = signed_req.split('.')[0];
  var context = signed_req.split('.')[1];
  // Sign hash with secret
  var hash = CryptoJS.HmacSHA256(context, shared); 
  // encrypt signed hash to base64
  var b64Hash = CryptoJS.enc.Base64.stringify(hash);
  if (hashedContext === b64Hash) {
    res.sendFile(path.join(views, 'index.html'));
  } else {
    res.send("authentication failed");
  };  		
})

var port = process.env.PORT || 9000;
app.listen(port);
console.log('Listening on port ' + port);

Run the following from the command line within your main app directory to push your changes to Heroku.

  1. heroku create
  2. git add .
  3. git commit -m "pushing to heroku"
  4. git push heroku master
  5. heroku open
    Open the URL which looks like as a random string like https://limitless-retreat-55750.herokuapp.com/

Now go to the Connected app which you created in earlier, update the callback URL and Canvas App URL with the Heroku app URL  example https://limitless-retreat-55750.herokuapp.com/

Now We have to add the Environmental variable Consumer secret from the connected app to Heroku by runs the following command.

heroku config:set CONSUMER_SECRET=xxxx

Now you can see the Canvas app on the chatter tab as shown below.

Github URL for complete code: https://github.com/rajamohanvakati/Canvas-App-Quick-Action