Building Secure Screen Flows For External User Access

Adam White
Salesforce Architects
8 min readJul 17, 2024

--

A diagram explaining the client and server data exchange that occurs within a Screen Flow embedded within an Experience Cloud site
A diagram explaining the client and server data exchange that occurs within a Screen Flow embedded within an Experience Cloud site

Screen Flows are an important part of any implementer’s toolset when building forms within Salesforce. A screen flow is a flow type that requires user interaction as it includes screens, local actions, steps, choices, or dynamic choices. Fundamentally you can use a screen flow to collect data or display data to users.

But like anything built using Well-Architected guidance, ensuring the security of your solution is the first priority. If your Screen Flows are not built with security in mind, you may be unintentionally exposing fields and data. This is especially true when providing an external user access to that screen flow on an Experience Cloud site.

Within the Well-Architected Framework we provide specific guidance to not mix system and user context data operations in the same transaction within any automation. Coupling this guidance with continual evaluation of your org’s security model (profiles, permission sets, field level security) is critical and is a general best practice when embedding flows within external sites on the platform.

In this article we’ll be covering a critical topic of keeping your data secure in Screen Flows — specifically looking at an anti-pattern related to external user permissions, system context mode and several patterns you can introduce when embedding a Screen Flow on an Experience Cloud site. To find a comprehensive list of Patterns and Anti-Patterns take a look at the Well-Architected Pattern & Anti-Pattern Explorer.

How Permissions and the Flow Engine drive the behavior of a Screen Flow

There are two key considerations that can drive the introductions of anti-patterns within your screen flow.

  • Screen Flows and Autolaunched flows start by default in user context — the permissions of the running user. When saving a screen flow or autolaunched flow, you can optionally decide to override that context to System — Without Sharing or System Context — With Sharing. This is an easy option to select, and can have further reaching ramifications that will introduce all kinds of anti-patterns into your solution.
  • Between flow screens, the flow engine sends results back into the UI, and the components on the screen consume that data and display it to your users. The introduction of features like Reactive Screen Components and our Data Table component have improved the user experience by making more data and processing available without any server calls. In that same token, it means more data will be available in your users’ browsers and also adds an extra element of risk. Malicious actors could go into their browser’s developer tooling to see all of the fields or manipulate them in the client

These considerations introduce the potential for an anti-pattern when you automatically store all fields when overriding flows from user context to system context for external users. Let’s talk about the kind of impact this anti-pattern could have.

Many releases back, the Flow team improved the overall Flow experience by allowing a Flow builder to automatically grab all fields for an object. This fantastic ease of use enhancement made it easier to automatically grab any field you might need downstream in your Flow and prevented errors that would arise when you’d reference a field in your Flow that wasn’t in the data set you retrieved. The flow engine attempts to look at all of the references you make to the Get Records element downstream, then grabs only the fields needed in the Flow.

However, when passing in the results of a Get Records to a Subflow, Invocable Action, or Lightning Component, the Flow engine can’t know what fields are actually needed, since there is no platform-level mechanism to tell the engine what fields are required.

Here is where the Anti-Pattern is introduced.

Debug details for when a Get Records element is passed into an LWC, Subflow, or Action
Debug details for when a Get Records element is passed into an LWC, Subflow, or Action

For screen flows, when the flow engine returns the results back into the UI and they are passed to a component that takes that record collection, like to a Data Table or the new Action Button component, all of the data is present within the browser regardless of whether it is used in the screen flow. This is because, again, Lightning Web Components have no generic way to communicate to Flow what fields are needed, so the Flow engine must retrieve all fields available to the running user when the flow is configured to retrieve all of the fields for the set of records.

This means if you run your flow in system context, perform a Get Records with ‘Automatically store all fields’ enabled, and use that data in a screen component, then all of the the fields and their values are available to be viewed or modified in the browser’s developer tooling regardless of whether those fields are used in the actual screen. To be clear, you cannot directly edit the record itself, only the representation of the record in your browser. What happens to the record is up to you, the flow builder.

Given this case, there are patterns you can introduce into your solution to ensure anti-patterns are avoided.

Secure Patterns to Introduce to Screen Flow For External User Access

When designing your solution with Screen Flows especially with external users, introduce the following patterns into your solution to ensure that the anti-pattern above does not allow a malicious actor to access data when they should not:

Use system context with screen flows sparingly, if at all, with external users

  • Plan your external user permission model (especially field-level and record-level security) properly and carefully. Return the absolute minimum amount of information back into your flow if you absolutely need to run in system-context.

Provide subflows elevated access with system context versus using system context within the parent flow only

  • Do not elevate your entire screen flow in system context mode. If you need to grant a user access to specific fields, objects, or setup data, perform those actions with subflows that have elevated access. Do not grant access to the elevated subflow to the external user profile.

Specify which fields will be used by Get Records within system context

UI for selecting specific fields in a Get Records element
UI for selecting specific fields in a Get Records element
  • Because a Get Records element will return every field that a user has access to when called in a subflow, we recommend being more specific working with Get Records element. Be extremely careful about what fields you query for when putting that data into an externally facing website. As noted above, if a flow is operating in system context and you aren’t specifying fields to return, you could be exposing far more data than intended.
  • If you’re executing a system context-enabled subflow to get data, only get what you need in the Get Records element in your subflow. Use the ‘Choose fields and let Salesforce do the rest’ option for those elevated-permission Subflows so that any parent flow that calls it doesn’t retrieve too much data.
  • It’s important to note that with this approach, if you try to run a flow and it references a field not specified in the Get Records element, your flow will likely hit an error. Good security practices often come with tradeoffs, and this is certainly a consideration to account for.

Performance Pattern to call out: Specifying fields within Get Records will improve performance by bringing less data from the server

  • Not only will the following approach of selecting fields improve the security of your screen flow, but it will also improve performance. Because you are no longer passing potentially massive amounts of data into the client, you’ll also see major improvements from components like Data Table that consume it and output that data.

Do not perform DML on records using record collections as a source

Transform UI for creating a safe collection of records to update from a Data Table’s editedRows output
Transform UI for creating a safe collection of records to update from a Data Table’s editedRows output
  • When configuring a Create, Update, Delete element using the “Use the IDs and all field values from a record or record collection” setting, and that collection is an output from a screen component, you’re essentially allowing a user to insert a value into any field on the record that they have access to, even if it wasn’t a field that was intended for them to edit in the flow. Malicious users could modify the structure of the ‘selectedRows’ output using their browser’s developer tools to modify the fields and values. If you perform an update using a ‘selectedRows’ output (or, in some components, ‘editedRows’), fields you didn’t intend to be edited could be modified by the user if they have the right permissions.
  • An example here would be a Data Table that outputs an ‘editedRows’ output. Instead, use the screen component’s record collection output to then create a separate record collection that only contains the fields you expect them to edit.

Best Practice Guidance on Screen Actions in Experience Cloud (In Beta)

Our newest Action Button component is arriving as a Beta in the Summer ’24 release. It allows users to trigger autolaunched flows within the screen and pass the results to components on the same screen to create bigger, more dynamic screen flows. We call these screen actions.

Since the action button is a Lightning Web Component, it operates in the client and gets results from the server. Because of this, every input and output into the action must be available in the browser’s client for it to work, even if they aren’t shown directly on the screen. Knowing this, let’s look at some best practice guidance when working with Action Buttons that trigger Screen Actions:

  • Never put sensitive information in the inputs or outputs of a screen action. Instead, put it into the autolaunched flow and don’t mark it as ‘Available for input’ to prevent the information from being viewed. An example anti-pattern would be somebody passing an API key into the input of a screen action that runs an external callout to a secure system. That API key could easily be seen by anybody and those are often critically important to keep confidential.
  • Re-run the action button’s autolaunched flow again between screens when you depend on the output of an action. An example of this might be a shipping calculation callout that grabs a total shipping price, then returns the results back into the screen for the user to view. A bad actor could in theory manipulate the results of the action in their browser, and that value would then be saved in the database. To prevent this risk, re-execute the pricing callout between screens and use that action’s results elsewhere in your flow instead.

Wrap Up

Screen Flows are becoming more and more powerful thanks to the advent of reactivity, screen actions, and more data processing capabilities like the new Transform element. As you create bigger, more complex screens, the amount of data present in the browser will also increase. Be mindful of this and plan for malicious actors snooping around the innards of your flows.

Evaluate your external user permissions and use system context sparingly and carefully. Follow the patterns provided in this post to ensure your organization’s data is safe and secure.

Additional Resources

--

--

Adam White
Salesforce Architects

I’m a Flow Product Manager & former Solution Architect at Salesforce based in Richmond, Virginia and a contributor to UnofficialSF.com.