LockerService and Lightning Container Component: Securely Using Third-Party Libraries in Lightning Components

Regardless of the language, framework, or platform you use, modern applications are built by assembling discrete and composable components. On the Salesforce platform, the application composition infrastructure is provided by the Lightning Component Framework. That’s how the Salesforce Apps (Sales Cloud, Service Cloud, etc) are built, and that’s also how you can extend and customize these apps with your own components, or create completely new apps.

The components you assemble can come from different sources. For example, you can build an application (or a page) by assembling Salesforce components, third-party components, and your own components. Components can be built entirely from scratch or by leveraging third-party libraries. In this article, we discuss the different types of libraries you may want to use in a Lightning Component, and we compare two isolation mechanisms (LockerService and the Lightning Container Component) that allow you to use them securely and avoid security exploits.

Which Library Do You Need?

Third-party libraries usually fall in one of the following categories:

  • DOM manipulation libraries (jQuery, etc)
  • Specialized utility libraries (moment.js, numeral.js, etc)
  • UI libraries (Bootstrap, jQuery UI, etc)
  • Data visualization libraries (D3, Chart.js, Leaflet, etc)
  • MVC frameworks (React, Angular, etc)

Before we look at how you can use a third-party library in a Lightning Component, it’s useful to ask yourself why you are using it, and if you really need it. In some cases the answer will be a resounding yes: nobody wants to reinvent a data visualization library like D3 or a mapping library like Leaflet. In other cases the answer will be less clear. Let’s take a closer look at DOM manipulation libraries, UI libraries, and MVC frameworks in particular.

DOM Manipulation Libraries

JavaScript has come a long way in recent years. Many DOM manipulation utilities we couldn’t live without in libraries like jQuery are now standard in the language. Modern frameworks like the Lightning Component Framework also provide abstractions that make jQuery less relevant. For example, let’s say you have to set the value of a score input field to 100.

Using jQuery, you would:

  1. Assign a unique id to the input element
  2. Use a jQuery selector to find the input element in the DOM
  3. Assign the value to that DOM element
// Markup:
<input id="score" />

// Code:
var scoreElement = $("#score");
scoreElement.val(100);

Using the Lightning Component Framework, you would:

  1. Declare a score attribute
  2. Bind the input field to the score attribute
  3. Set the value of the score attribute: the bound input field will automatically display the new value.
// Markup:
<aura: attribute name="score" type="integer" />
<input value="{!v.score}" />

// Code:
component.set("v.score", 100);

In the Lightning approach, the model and the view are decoupled: your code doesn’t have to reach into the DOM. This leads to more robust and more maintainable code. Avoiding direct DOM manipulation is a best practice in the Lightning Component Framework (like in most modern frameworks), and you might just discover that you no longer need your DOM manipulation library.

UI Libraries

You may also want to reconsider the use of UI libraries like Bootstrap and jQuery UI. Although these libraries provide useful components, they have their own UI identity that can clash with the Lightning Experience identity. The Base Lightning Components and the Lightning Design System offer similar capabilities while providing a consistent user experience.

MVC Frameworks

At a high level, libraries like React and AngularJS have the same focus as the Lightning Component Framework: They provide code organization and utilities to create components. If you are looking for a component framework to develop applications on the Salesforce platform, you should use the Lightning Component Framework because it’s tightly integrated with the platform. But you can also use other frameworks if you so desire. All you have to do is choose the isolation mechanism (LockerService or Lightning Container Component) that works with your framework to avoid security exploits (see below).

Avoiding Security Exploits

To avoid security exploits, an application composition framework should provide the right level of isolation between components. For example, an untrusted component shouldn’t be able to gain access to the part of the DOM owned by another component. Without the right level of isolation, a vulnerability in one component can provide access to sensitive data in other components and jeopardize the security of the entire system. Just like breaking into one apartment would provide access to all the apartments in the building if each apartment didn’t have its own secured door. Each component you use in an application is an attack vector, and as a corollary, each library you use in any component is an attack vector as well. The Lightning Component Framework comes with two isolation mechanisms: LockerService and the Lightning Container Component (in Developer Preview in Spring ’17).

LockerService Isolation

LockerService is the primary and preferred isolation mechanism for the Lightning Component Framework. LockerService wraps standard objects like window, document, and element inside a secure version of these objects (SecureWindow, SecureDocument and SecureElement) as a way to control access to APIs and regions of the DOM. When components are loaded, they are provided with the secure wrappers (secureWindow and secureDocument) in lieu of the standard objects (window and document). When a component invokes a method on the document or window object, the secure wrapper can apply appropriate security restrictions. For example, access to the DOM of another component will be:

  • Granted if the other component is in the same namespace.
  • Denied if the other component is in a different namespace.

In addition to providing a sophisticated namespace-based access control mechanism, LockerService enforces a series of rules to further avoid security exploits:

  • JavaScript ES5 strict mode is automatically enabled. Libraries that do not support strict mode will not work with LockerService.
  • Content Security Policy (CSP). unsafe-eval and unsafe-inline are disallowed. Libraries using eval() or inline JavaScript code execution will not work with LockerService.

The rules enforced by LockerService are recognized as industry best practices. However some libraries may not yet work with these restrictions enabled. In that case, we recommend you ask the library author to support strict mode and CSP. In the meantime, you can use the alternative Lightning Container Component isolation described below.

LockerService Advantages

  • No iframe. Components live in the same DOM (better performance)
  • Straightforward, natural communication between components
  • Cohesive UI
  • Eliminates DOM scraping vulnerabilities
  • Mitigates the impact of developer mistakes such as the lack of proper escaping
  • Cross-site scripting (XSS) and template injection are no longer possible
  • Eliminate server-side action invocation/spoofing

LockerService Limitations

  • Non-compliant libraries will not work with LockerService

Lightning Container Component Isolation

The HTML Inline Frame Element <iframe> allows you to embed an HTML page in the current HTML page. The embedded HTML page is loaded in a different DOM (with limited access to the parent DOM) and runs in its own context. The Lightning Container Component (lightning:container) is a convenience component that wraps an iframe and provides a simple API to communicate between the iframe container and its content. The Lightning Container Component is in Developer Preview in Spring ’17. The Lightning Container Component can be used to sandbox components or apps built with libraries that don’t run with LockerService. For example, you can use a Lightning Container Component to embed an AngularJS app in a Lightning Component. Here is what it might look like:

<aura:component access="global" implements="flexipage:availableForAllPageTypes">
    <lightning:container aura:id="MyAngularApp"
                         src="{!$Resource.MyAngularApp + '/index.html'}"
                         onmessage="{!c.handleMessage}"/>
</aura:component>

The Lightning Container Component is not an optimal solution for fine grained application composition where you would embed multiple iframed AngularJS components on a page: In that case, each component would load the AngularJS framework, communication between components would be limited (postMessage only), and the UI would be constrained by the boundaries of each iframe.

Lightning Container Component Advantages

  • Native isolation mechanism in the browser
  • Support for any third-party library, including ones that are not LockerService compliant (libraries are sandboxed in their own DOM/context)
  • OK for coarse-grained apps

Lightning Container Component Limitations

  • Limited communication mechanism between components (postMessage)
  • Components are constrained to a rectangle area on the page. Content may be clipped, rich interactions like drag-and-drop between components may not work, etc.
  • Heavier/Slower. If there are multiple iframes on a page, each iframe loads its own version of libraries.
  • Not great for fine-grained app composition

Summary

Before you decide to use a third-party library in a Lightning Component, you should reevaluate if you really need that library. DOM manipulation libraries and UI libraries in particular may no longer be needed when working with the Lightning Component Framework.

LockerService is the preferred isolation mechanism and should be your first choice because it’s more tightly integrated: components run in the same DOM, resulting in a faster and more cohesive user experience. If your library doesn’t support LockerService, you can fall back to iframe-based isolation using the lightning:container component.

LockerService Lightning Container Component
iframe-based No Yes
Components are loaded in the same DOM Yes No
Supports all third-party libraries No. Only compliant libraries will work. Yes. Libraries are sandboxed in their own DOM/context.
Cohesive UI Components can grow and shrink. Surrounding DOM elements will reflow. Components are constrained by iframe boundaries
Inter-component communication Native LC communication postMessage
Performance Faster. All components are loaded in the same DOM. Slower/Heavier. Each iframed component has to load its dependencies.
Check out the LockerService and the Lightning Container Component documentation to learn more.
Follow @ccoenraets on Twitter.

Leave your comments...

LockerService and Lightning Container Component: Securely Using Third-Party Libraries in Lightning Components