So you have a fancy web app and want to integrate it with Salesforce in a secure and simple way. Salesforce Canvas Apps is the recommended approach to mash-up UI integrations. In this article, we are going to explore some of the options, considerations, constraints, and limitations of implementing Canvas Apps.
Signed Request vs. OAuth
Assuming Canvas App developers have control over both the back-end and front-end, the flows are different.
In the case of Signed Request, the back-end developer needs to perform these actions:
- Accept POST requests, expecting a signed_request parameter.
- Base-64 decode the signed_request.
- Verify the payload was not altered by decrypting the first part of the decoded signed_request (the string before the period character) using the HMACSHA256 algorithm and the Connected App’s Client Secret.
- Adapt the size of the Canvas App to fit the screen size available
- Make the size of the Canvas App autogrow when content expands or contracts
- Subscribe to and publish custom Canvas events
- Subscribe to and publish Platform Events
- Subscribe to any other Streaming API channel like Push Topics, Custom Events, and Change Data Capture
- Use the Canvas SDK AJAX module to make remote call-ins to Salesforce that don’t count against the daily API limits
TIP: For applications that do not implement the MVC pattern like Java Spring’s controllers that are annotated with @RESTController instead of @Controller, and that use React or Angular for the front-end, make sure the back-end REST Controller POST endpoint for the Signed Request stores the decoded and verified JSON object in memory or user session. In other words, make sure your application is stateful, so you can provide a separate endpoint for your front-end application to retrieve the JSON object.
In the case of OAuth, developers have two options:
- User-Agent OAuth Flow
- If you are using MVC, back-end developers need to expose a GET endpoint as the URL path to the Canvas App and render the View normally.
- Once the OAuth token is issued, the developer can request the client’s context which is the JSON object that contains the Salesforce details like parameters, record, environment, user, etc.
- Web Server OAuth Flow
- This flow mostly makes sense if you are using MVC. In which case, back-end developers need to expose a GET endpoint as the URL path to the Canvas App and then redirect the user to the Salesforce OAuth authorization endpoint, passing the Connected App’s Client Key, Client Secret, and Callback URL. This Callback URL is another back-end GET endpoint that receives an OAuth access code, which is used by the developer to obtain an OAuth access token by making a POST request to the Salesforce OAuth token endpoint. If the access token is granted, the developer may pass it to the View.
Consider using the Web Server Flow over the User-Agent Flow if you don’t want that pesky OAuth authorization screen to pop up for your users when launching the Canvas App. Although admins can set the OAuth Policies to “Admin approved users are pre-authorized”, the authorization popup is still displayed, though it auto-closes immediately.
Keep in mind that the User-Agent Flow is a less secure flow that is intended to be used in devices that can’t securely store the Client Secret like mobile or desktop native apps.
Admins that set Canvas Apps to use OAuth can also block OAuth usage for the Canvas App temporarily. This could come in handy when doing maintenance to the Canvas App. Admins can also revoke a token to a specific user.
Connected Apps that use Signed Request as their access method still need to enable OAuth to work. Even though admins can block the app’s OAuth usage and revoke access to the OAuth Connected App for a particular user, the Canvas App is still able to use the access token included in the Signed Request and make remote call-ins.
OPINION: Signed Request is often preferred over OAuth due to its overall development simplicity and level of security.
TIP: For Production environments, make sure you protect the Client Secret. For example, if you are hosting the application in AWS, you could use Parameter Store to securely store this secret.
TIP: It’s a good idea for developers to implement both GET and POST verbs on the application’s back-end even if only one verb is going to be supported. For example, if you don’t want to allow users to self-authorize, the GET response could render a message to contact the org admin rather than throwing an error that the GET verb isn’t supported.
If you are still not sure which one is the best option in your case, read on…
Aura vs. Visualforce
Other Canvas Application target locations include Chatter Feed, Chatter Tab, Console, Layouts and Mobile Cards, Mobile Nav, Open CTI, and Publisher.
TIP: By now, most Salesforce customers should already be using Lightning. Although LWC is preferred over Aura, the ability to launch a Canvas App from LWC is not yet available. In any case, consider using the Salesforce Lightning Design System within your Canvas Application to provide a seamless experience for your users. If you are using React, there is a Lightning Design System for React library that front-end developers can use to make this a lot easier.
The most important aspects of the Canvas App configuration can be done in the Connected App. Admins need to enable OAuth, configure the Canvas App URL, Access Method, and Locations. Optionally, developers can use a Canvas Lifecycle handler to control additional aspects of the Canvas App during run time like adding/modifying the parameters passed via signed request or changing the path portion of the Canvas App URL.
When it comes to packaging, ISVs are presented with a fundamental challenge: whether to package the Connected App or not. In the case of Canvas Apps, managed package Connected Apps don’t allow admins to change the Canvas App URL. This means that there is a single endpoint for the Canvas App that needs to be shared by all Salesforce tenants. This requires additional IT work to manage the routing of these tenants. Additionally, you might want to route to additional non-production environments for your Canvas App when using a Sandbox org.
It is important to know the Canvas App URL is also used to secure the origin of messages exchanged between the app and Salesforce via the browser. If the domain portion of the Canvas App URL could be overwritten, the app would break due to CORS violation.
To Package or Not to Package – That is the Question
You should package the Connected App if:
- You have a single front-end gateway server that knows how to verify and decode signed requests or use OAuth to authenticate requests from various Salesforce tenants; and
- The front-end server is the only system that stores the only Consumer Secret of the Connected App; and
- The front-end server can route the requests to multiple back-end servers that represent per-tenant environments, including production and non-production; and
- The front-end server can be hardened and scaled accordingly so it can mitigate DoD attacks and does not become a bottleneck.
You should not package the Connected App if:
- You want to support vanity URLs for your tenants; or
- You want to support one-to-one environment mappings, especially for production vs. non-production environment requirements; or
- You want to control access to your front-end servers by restricting requests to one single origin; and
- You can easily provision new environments to host your Canvas App.
Canvas Lifecycle Handler
Developers can further control the lifecycle of the Canvas App by implementing a Lifecycle Handler. This handler allows developers to exclude elements from the Canvas context object, for example, the details of context Salesforce User or the Salesforce organization details.
In addition, developers can also control the behavior of the Canvas App by implementing the code that is executed before the application is rendered.
TIP: Use a Lifecycle Handler to override the path portion (after the domain) of the URL or modify the parameters of the Canvas App if you need to point to a different environment when the current Salesforce org is a Sandbox or based on the tenant name.
Below is a link to a Canvas demo. It’s a quick and easy way to test drive the concepts explained in this article. Just follow the README file in the root of the project. There are references to two demo apps that use Heroku for hosting. One is written in NodeJS and the other in Java.
In the demo apps you will notice additional concepts that will most likely be necessary in your application. Below is a list of things to consider:
- Screen size on load: notice how the RealtyApp Lightning Aura Component only initializes the app with a width of 100% but the Canvas App front end code (realty.ejs) uses Sfdc.canvas.client.resize and Sfdc.canvas.client.autogrow to make the app take the whole available screen size and adapt to content expansion or contraction
- Use AJAX to update Salesforce records: this is noticeable in the qr.ejs View in the NodeJS app where a button click event makes an AJAX remote call-in to Salesforce to update a Contact record
- Subscribe to Platform Events: in the index.html View in the Java app, we subscribe to a Platform Event called CanvasEvent__e and we render the JSON string payload in the page. The Platform Event is fired from Visualforce by using the “Publish Platform Event” button which has an onclick event that uses an Apex @RemoteAction.