Lightning Components are Salesforce’s successor to Visualforce for developing and customizing the UI, especially within Lightning Experience and App Builder.
New to Lightning Component development?
Check out these resources:
So it is only natural that as you develop Lightning components to create rich and dynamic UIs that you would want to take advantage of these APIs.
The focus of the rest of this blog post is about the backstory of how this component came about and why it’s needed.
A walk down memory lane
For a time…
Lightning Apex Sessions Are Not API Enabled
As it turns out, it was an oversight that the session id obtained in Apex (e.g. UserInfo.getSessionId()) in Lightning component contexts was API enabled. Salesforce subsequently pushed out an update and guidelines for making API calls from Apex. Namely, to call Salesforce APIs from Lightning components, developers should make the call from their component’s Apex controller and use Named Credentials.
“By security policy, sessions created by Lightning components aren’t enabled for API access. This prevents even your Apex code from making API calls to Salesforce. Using a named credential for specific API calls allows you to carefully and selectively bypass this security restriction.
The restrictions on API-enabled sessions aren’t accidental. Carefully review any code that uses a named credential to ensure you’re not creating a vulnerability.”
Named Credential Burden
A side effect of this change is that Peter’s component as-is would need tweaking. Developers wanting to use this approach would need to modify their Apex controller to use a Named Credential, which also meant that the developers needed to create a Connected App, an Auth. Provider, and Named Credential to allow code running in their org to make callouts (callins?) back to their own instance.
Not only that, but the developers now had the burden of determining who the context user should now be for these API calls. If they wanted the currently logged in user to be the context user of the API calls, then the developers have to grant access to the Named Credential to the users via profiles or permission sets, then the users themselves need to manually perform the OAuth authorization dance for that Named Credential to enter their credentials prior to interacting with the Lightning component. Hrm… not a great end-user experience.
Or, the developers could forego the users authorizing the Named Credential individually and instead the developers could use a named-user, but then all API calls would act as that single user and you lose some traceability of who actually is taking action.
Pros and cons and none ideal.
You may have read that in Winter ’19, Salesforce is streamlining making API calls to your org from Apex. When My Domain is enabled, Apex code that makes Http Callouts to the same domain no longer need Remote Site Settings or Named Credentials in synchronous or asynchronous code. This is great, however, the session restriction for Apex in Lightning Component contexts is unchanged and so this has no benefit for what we are talking about.
Visualforce Apex Sessions Are API Enabled
Despite the lockdown on sessions obtained from Apex used by Lightning components, sessions obtained from Apex used by Visualforce are still API enabled.
Wouldn’t it be interesting if we could get our Lightning components to communicate with a Visualforce page and take advantage of its API enabled session? We’ll come back to this idea in a minute.
Communicating between Lightning Components and Visualforce Pages
I’m not the first to think of trying to get Lightning components and Visualforce pages to communicate.
Christophe Coenraets blogged about embedding a Visualforce page via an iframe tag in a Lightning component then using window.postMessage() to communicate between the two. In his example, he built interactivity between clicking data in a Lightning component and updating positions in a Google Map in a Visualforce page.
Keir Bowden (also known as Bob Buzzard), blogged about using this same technique to toast a message from a Visualforce page. In his example, he was using Lightning Out (where you embed Lightning component within a Visualforce page) but standard Lightning Experience events like toasting messages or navigating to records aren’t supported in Lightning Out. So he used window.postMessage() to communicate to a Lightning component sitting in the Utility Bar that based on messages sent would take the appropriate action. In this case, show a toast message.
Calling REST API from Lightning Components using Visualforce Pages
Now that I know I can communicate between Lightning components and Visualforce pages, it’s time to plan how to post REST API requests from the component to Visualforce, Visualforce to make the request with its API enabled session, and then Visualforce to communicate back with the response.
Sounds simple, but there are real challenges to address before we get too far, such as:
- Sequencing messages
- Routing messages
Sequencing of Messages
The window.postMessage() is a fire-and-forget action. You do not get a synchronous response back or provide a callback. Likewise, to receive responses from the other window, your code has to add an event listener. Both sending and receiving messages are asynchronous, and are de-coupled without a built-in way to correlate the responses. The challenge this poses is that we will need a mechanism to ensure the response messages we receive from Visualforce are routed and handled for the correct request messages we sent from Lightning. Otherwise, for example, if our component sent two messages, one for POST /sobjects/Account and one for GET /ui-api/layout/Account, when our component’s event listener receives the responses from Visualforce, how would we know to which original request the response was for?
In Christophe’s blog, he mentions the One-to-One vs. One-to-Many messaging scheme:
“When you send a message from a Lightning component to the iframe it wraps using contentWindow.postMessage(), there can only be one Visualforce page loaded in that contentWindow. In other words, that Visualforce page is the only place where you can set up a message event listener and get the messages sent by the Lightning component in that fashion. This is a one-to-one messaging scheme.
When you send a message from an iframed Visualforce page to its Lightning component wrapper using parent.postMessage(), parent is a reference to your main window in Lightning Experience where other Lightning components may be loaded. If other Lightning components loaded in the same window object set up a message event listener, they will receive the Visualforce messages as well. This is a one-to-many messaging scheme, and it’s something to account for both when you send and receive messages.”
This means we not only have a message sequencing problem between a Lightning component to its iframe’d Visualforce page, we also have a routing problem when the Visualforce page posts a message back to its parent window. We don’t want, for example, all the components on your Lightning page to receive and perhaps incorrectly react to responses they never asked for, or process the same response multiple times redundantly.
The value in window.postMessage() is that it allows any window to communicate with any other window regardless their domain. The security risk in window.postMessage() is that it allows any window to communicate with any other window regardless their domain.
As described in this blog post The pitfalls of postMessage by Detectify Labs, a web security research group, this flexibility is a vector for cross-site scripting attack (XSS) where malicious code posts or receives messages that it shouldn’t. To guard against these kinds of attacks, your code must ensure it only sends messages to pages on a trusted domain and when receiving a message to verify it was sent to you from a trusted domain. Relatively easy tasks to do, but occasionally overlooked with grave consequences. Curious what not validating the sender’s and receiver’s domain is? Read this blog post about how an ethical hacker was able to obtain a private Slack token using postMessage.
I don’t know about you, but I don’t really want to write the code necessary to enforce sequencing, routing, and security around using window.postMessage(). No, I want to focus on the business value and cool apps I can build in Lightning components using the REST APIs and eliminating the need for writing Apex code and setting up Named Credentials.
Postmate, a powerful, simple, promise-based postMessage library
Postmate is a popular open source project created to address the problems around sequencing, routing, and security when using window.postMessage(). It uses a handshake pattern on initialization between the parent and child windows so that messages are sent to and received by the intended audiences. Postmate internally assigns correlation ids to route responses to the correct requests, and their promise-based functions make using window.postMessage() much more comfortable and predictable.
For these reasons, I use Postmate for communication between my Lightning component and Visualforce page. All of this, by the way, is bundled in my open source project and abstracted away from you when you use the <c:lc_api> service component.
JSforce, a powerful, simple, promise-based REST API library
For these reasons, I use JSforce for executing the REST API requests directly from Visualforce and then send the response back to your Lightning component via Postmate.
- Trailhead: Security for Lightning Components
- Trailhead: Develop Secure Web Apps
- Lightning Security
- Communicating between Lightning Components and Visualforce Pages
- window.postMessage() API documentation
- Postmate GitHub Project
I am not a “Security Expert”. I make no claims about the suitability or appropriateness of this solution for your organization. There are reasons that Salesforce implemented Locker Service for Lightning components. If you have any doubts or concerns about the approach outlined in this blog post, then please do not use it.