Building Flows for the Guest User Profile

Mitch Lynch
Salesforce Architects

--

Flows are a terrific way to offer multiscreen automations and wizards in Experience Cloud sites. However, when it comes to external users — especially unauthenticated guests — there are always org resources and actions they shouldn’t (or can’t) have direct access to. You may need to include these resources for your flow to be fully-functional, but the last thing you want to do is provide direct access to them.

By following the approach in this post, you can provide great Flow experiences to guest users while granting them the least amount of privilege in your Salesforce org. What’s the secret? Autolaunched subflows.

Before diving into the suggested pattern, let’s look at the type of hypothetical you’ll want to avoid. Let’s say a food pantry runs its client service operations on Salesforce, and their solution includes an appointment system that’s built with Flow. Their operations revolve around a custom object called Pantry Event, which is used for planning and managing food distribution for specific days. Most appointments (Signup records) are created by internal users, but some clients request appointments online to receive services.

Food pantry appointment management data model
Food pantry appointment management data model

In the guest user flow, a client needs to view a list of upcoming Pantry Events that still have availability, select one, and submit their appointment request (Case). Since architects often need to respond to client and operational changes, this flow is tied into a custom metadata type (Pantry Config) that makes it possible to configure the behavior of all of the flows in the org on the fly, without modifying them directly. This particular flow only touches a few of those settings:

  • Whether or not online scheduling is enabled
  • How far out clients can request appointments
  • Seasonal text content that displays on some of the flow screens
A flow with too much access granted to the guest user profile
A flow with too much access granted to the guest user profile

The flow above, running in user context, gets the job done… but at what cost?

  1. It requires access to the highly sensitive Pantry Config custom metadata type, where there aren’t any field- or record-level restrictions to apply.
  2. It requires Read access on the Pantry Event object, managing field-level access, and setting up a sharing rule.
  3. It requires Create access on the Case object (which is actually pretty standard).
  4. It also requires Read access on the Case object and a sharing rule (or maybe I should call it an “oversharing” rule), all just to retrieve and display the Case Number (since it’s a calculated value, it’s not available without retrieving the new Case afresh).

We’ve broken so many rules, and now someone will have to manage all of that in perpetuity. Additionally, relative to the sensitive data and metadata in the org, we’ve eroded our adherence to the principle of least privilege, meaning our focus with this Experience Cloud site will always be keeping things locked down, not opening access up where needed.

Subflows can turn all of this around.

Encapsulating sensitive actions in subflows

By moving sensitive Data Manipulation Language (DML) operations and other actions to an autolaunched subflow (no trigger) running in system context, you never have to provide any of those permissions to your guest users. They have the benefit of greater levels of access only from within the situational context you have deliberately engineered for the flow.

The revised flow with higher level functions encapsulated in subflows
The revised flow with higher level functions encapsulated in subflows

Following this pattern, the revised appointment request flow shown above no longer has any of its own DML statements, and thus needs no permissions or sharing rules:

  1. To get the Pantry Config metadata type and the list of available Pantry Event records, it calls on a subflow that’s running in system context. This call doesn’t require any input variables, but provides several specific outputs such as a list of Pantry Events and some text and Boolean variables for making decisions and displaying screen text.
  2. To create the case and retrieve the case number, it again calls on a subflow running in system context. The subflow receives the draft case record variable as an input and returns the case number as an output.

Moving the sensitive actions to subflows also simplifies the parent flow and achieves a separation of concerns. Visually speaking, it’s much easier to recognize when we’re using elevated permissions and when we aren’t, and that makes for a much safer flow that’s easier to maintain over time. For more on the value of creating composable solutions like this, check out Salesforce Well-Architected — Composable.

Guaranteeing situational context

The downside to this approach is that you aren’t managing user permissions in the traditional places (permission sets and sharing rules). But assigning access based on who is performing the action isn’t appropriate here, since doing so lacks the specificity of context. And context is everything when it comes to the guest user. Those traditional mechanisms for permissions can’t offer the right flavor of conditionality — it’s all or nothing.

If you assign bespoke access within the situational context of your flow, you provide a process focus that isn’t possible using traditional permissioning. This is simpler, safer, and more secure, because it’s much easier to build an automation if we’re thinking about what the process needs the user to have, rather than all the implications of opening up guest user permissions across the entire org.

How is it possible to guarantee situational context for sensitive actions? Never give your guest users direct access to subflows. Guest users have to be explicitly granted access to a flow to use it directly, but this isn’t necessary for using subflows (and in fact, it’s a best practice to keep them restricted). Subflows inherit access from their parent flow(s), so the top-level parent flow is the only one that determines access. As a result, the guest user is prevented from using subflow superpowers outside the intended, situational context: the parent flow.

Let’s look at the subflow used in the revised flow above.

The subflow for the various sub-processes requiring higher level permissions
The subflow for the various sub-processes requiring higher level permissions

Surprise! It’s one, single, autolaunched flow. Subflow proliferation can be a challenge, so rather than creating a separate subflow for each action, we put all the genies into a single bottle.

Every time this subflow is called, we tell it which set of actions to perform in an input variable called inputSubProcess (where possible values are “GetConfigData” or “CreateCase”) and provide any necessary input variables. The flow executes the designated branch and returns output variables to the parent flow. This reduces the number of flows we have to manage down to just two: the parent flow (running in user context) and its related subflow (running in system context).

If you’re a developer, you might notice something familiar about this approach. It mimics an Apex class, where a class usually contains multiple methods that can be called upon to perform different but related tasks. Flow is only all too happy to work the same way.

Since I just said the word “Apex”, maybe now is a good time to note that your guest user will still need to be directly assigned access to any Apex actions that are executed in the parent flow or any subflows. All Apex access is based on the running user, even when the flow is running in system context.

Redacting subflow output variables

It would be very easy to think that lifting and shifting your sensitive actions to subflows is enough to keep you safe and secure, but it’s important that you don’t undermine your efforts by including too much data in the output variables returning to the parent flow. One of the key elements of this solution is redacting your subflow’s output variables by removing any information that is not strictly necessary.

Why is this important? Flows are executed server-side, so for the most part the processing occurring in your flows is hidden from the user’s browser. However, during flow execution, some network requests might reveal data to a browser’s developer tools. For example, in Chrome you can use the Network tab of the Developer Tools to see what’s going back and forth from your flow.

In the examples above, we have three Get Records elements. By default, these were set to automatically store all fields when we first created them. In reality, we only need a handful of fields from each, but if we pass full records or record collections back to the parent flow, those unused field values could become discoverable to browser developer tools.

What if that exposed personally identifiable information (PII) or internal notes that shouldn’t be public? Only ever return the data you actually need. The easiest way to ensure this is to choose specific fields to store in your Get Records elements.

This approach is also more performant. Reducing the data in your output variables reduces the size of the response payload coming back to the browser. That’s always a good thing!

Conclusion

With a little bit of thoughtful planning, you can create rich experiences for Experience Cloud guest users that keep your org safe and sound. You only have to keep in mind the secret recipe:

  • Create situational context for the guest user by defining specific flow-subflow experiences.
  • Always run flows for guest users in user context.
  • Keep all sensitive DML operations and actions in subflows running in system context.
  • Minimize the outputs from subflows to just the data required.

If you’re paying attention, this approach opens up the possibility of over-utilizing the guest user concept in Experience Cloud. This post is not meant to provide carte blanche for doing anything and everything in subflows or circumventing the intended use of the unauthenticated guest user.

As always, it’s important to temper whether you can do something with whether you should. Contractual restrictions are just as important as governor limits, and architects have a responsibility to design solutions that use the right licenses for the right use cases.

Additional resources

--

--

Mitch Lynch
Salesforce Architects

I'm a Distinguished Solution Engineer at Salesforce, based in the DC metro area. Join me on my occasional blog at https://thisoldcloud.app/.