The canvas App Previewer only has templates to create Ruby and Java versions in Heroku, so I though it would be a nice contribution to the community if I coded a Python example.
Heroku charges per time and based on Dynos that are containers running commands in a system. They provide free of charge use of 1 Dyno when you create an app.
To start with the Salesforce canvas Python implementation we need an account in Heroku. After we got an account it’s a good idea to install their toolbelt to be able to interact with Heroku from command line.
Creating an app
An app in Heroku can be created using command line or through the web UI. Let’s do it using the UI:
When the app is created this message is displayed providing more instructions to checkout the git repo on our workstation:
Running the following command will create a directory called
sfcanvaspythonapp with the contents of our Heroku app (empty at the beginning), and a remote called Heroku.
$ git clone firstname.lastname@example.org:sfcanvaspythonapp.git -o Heroku
Now that we got the Heroku app created, it’s time to start coding our app in Python!
To work with Python libraries we need a Python Package manager lile PIP.
We need to create a virtual environment in the directory that contains our Python app.
Virtualenv is an isolated Python environment. Allows you to control which packages are used on a particular project by cloning your main Python. Virtualenv can be installed using the script provided on the page or using system package managers (like homebrew in OSX or apt-get in Ubuntu).
To create a virtual environment we need to run this inside of our previously created Heroku app directory:
$ virtualenv --distribute venv
That will create a copy of our Python interpreter from the system to a folder called venv.
To activate this environment, we need to execute:
$ source venv/bin/activate
To avoid this virtual environment to be added to our git repo we need to edit our
.gitignore and add
Installing Flask and gunicorn
Installing Flask and gunicorn is pretty easy using pip:
$ pip install Flask gunicorn
Flask is the web framework and gunicorn the server needed to run that web app in Heroku.
Creating our first Flask app
Let’s create a file called
webapp.py with the following content to test our Flask installation:
from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello World!" if __name__ == "__main__": app.run()
Then run our python code with:
$ python webapp.py * Running on http://127.0.0.1:5000/
If we head to
http://127.0.0.1:5000/ using our browser we will see a “Hello World!”.
So far so good, now let’s add some logic to interact with Salesforce.
Decoding the Signed Request
Salesforce provides classes to decode the signed request in Ruby and Java along with their quickstart canvas templates.
I’ve ported the Ruby version of that signed request decoder class to Python.
So in order to use this class we need to create a file
canvas_signed_request.py with the following content:
import base64, hashlib, hmac class SignedRequest(object): # Construct a SignedRequest based on the stringified version of it. def __init__(self,consumerSecret, signedRequest): self.consumerSecret = consumerSecret self.signedRequest = signedRequest # Validates the signed request by verifying the key, then returns # the json string. def verifyAndDecode(self): # Validate secret and signed request string. assert self.consumerSecret != None,'No consumer secret found in environment [CANVAS_CONSUMER_SECRET].' assert self.signedRequest != None, 'Signed request parameter required.' # 1) Split the signed request into signature and payload. request_array = self.signedRequest.split('.') assert len(request_array) == 2, 'Incorrectly formatted signed request.' signature = request_array payload = request_array # 2) Verify the contents of the payload by first validating the authenticity # of the signature. decodedSignature = base64.b64decode(signature) this_hmac = hmac.new(self.consumerSecret,payload,hashlib.sha256) assert decodedSignature == this_hmac.digest(), 'Signed request has been tampered with.' # 3) Decode the base64 encoded payload of the canvas request. jsonString = base64.b64decode(payload) return jsonString
Create a connected APP in salesforce
Head to Setup/Create/Apps:
At the bottom of the screen there’s a section called Connected apps click on New:
Fill the required fields with information about your app, and check “Enable OAuth Settings”. Please note that the Callback URL field in the OAuth section must be filled because is required, but since we’re using Signed Request, that won’t be used. The reason to use OAuth Settings is to define a Scope (that will be applied when we login using a Signed Request), so the Select OAuth Scopes is relevant and must be set accordingly to our needs.
Set the URL of your Heroku app and where the app will be used. In this example I’m going to display a canvas app in a visualforce page.
After the app is created take note of the “Consumer Key” and “Consumer Secret”. The terminology is from OAuth 1.0 those now are called “Client Id” and “Client Secret”.
From the app detail we need to click the Edit button to change one OAuth default option in order to use this app with canvas. The default is “All users will self authorize”, must be changed to “Admin approved users are pre-authorized”
Also we need to grant access to this app to profiles , editing them and checking the app name under the section “Connected App Access”
Connecting the dots
Now it’s time to connect our Heroku app with our Salesforce app to see the magic of Salesforce Canvas.
Using Heroku command line we will add our connected app secret to an environment variable called
To do so we need to execute the following command from the Heroku app directory in our workstation:
$ Heroku config:set SECRET=<ConnectedAppSecret>
After that we are going to modify our
from flask import Flask, request from canvas_signed_request import SignedRequest app = Flask(__name__) import os @app.route("/") def hello(): return "Hello World!" # add a new route @app.route('/canvas', methods=['POST']) def canvas(): secret = os.environ.get('SECRET') sr_param = request.form['signed_request'] srHelper = SignedRequest(secret,sr_param) canvasRequestJSON = srHelper.verifyAndDecode() return canvasRequestJSON if __name__ == "__main__": app.run()
Heroku looks for a
Procfile to know what to execute, let’s create a file called
Procfile and add the following content:
web: gunicorn webapp:app
To check that everything is looking OK, we can run
foreman start (this will emulate what Heroku will do to start the app)
Finally Heroku must know which libraries we had installed in our environment, so we need to save them to a file called
This can be accomplished with the following command:
$ pip freeze > requirements.txt
And that’s it. Now we need to store everything and commit to our local repo:
$ git add . $ git commit -m "ready for canvas"
And deploy it to Heroku:
$ git push Heroku master
Trying out your canvas app
I’ll not dig deeper into how to use this SDK to keep this post lighter. See the additional projects listed below for more
The project I describe in this post:
For a more elaborated Salesforce canvas Python project check out the Super Powered Recycle Bin:
This was the project I’ve created to show in the presentation and explores the use of some of the information contained in the JSON object (the token) to interact with other APIs from Python.
The app is also an example of how the platform functionality can be extended using these tools.