Newer Version Available

This content describes an older version of this product. View Latest

Filter SOQL Queries Using WITH SECURITY_ENFORCED

Use the WITH SECURITY_ENFORCED clause to enable field- and object-level security permissions checking for SOQL SELECT queries in Apex code, including subqueries and cross-object relationships.

Salesforce recommends that you enforce Field Level Security (FLS) by using WITH USER_MODE instead of WITH SECURITY_ENFORCED because it has fewer limitations. See Enforce User Mode for Database Operations.

Important

Apex generally runs in system context; that is, the current user's permissions and field-level security aren’t taken into account during code execution. Sharing rules, however, are not always bypassed: the class must be declared with the without sharing keyword in order to ensure that sharing rules are not enforced. Although performing field- and object-level security checks was possible in earlier releases, this clause substantially reduces the verbosity and technical complexity in query operations. This feature is tailored to Apex developers who have minimal development experience with security and to applications where graceful degradation on permissions errors isn’t required.

The WITH SECURITY_ENFORCED clause is only available in Apex. We don’t recommend using WITH SECURITY_ENFORCED in Apex classes or triggers with an API version earlier than 45.0.

Note

WITH SECURITY_ENFORCED applies field- and object-level security checks only to fields and objects referenced in SELECT or FROM SOQL clauses and not clauses like WHERE or ORDER BY. In other words, security is enforced on what the SOQL SELECT query returns, not on all the elements that go into running the query.

Insert the WITH SECURITY_ENFORCED clause:
  • After the WHERE clause if one exists, else after the FROM clause.
  • Before any ORDER BY, LIMIT, OFFSET, or aggregate function clauses.
For more information on SOQL SELECT queries, see SOQL SELECT Syntax in the SOQL and SOSL Reference.
For example, if the user has field access for LastName, this query returns Id and LastName for the Acme account entry.
1List<Account> act1 = [SELECT Id, (SELECT LastName FROM Contacts)
2   FROM Account WHERE Name like 'Acme' WITH SECURITY_ENFORCED]
There are some restrictions while querying polymorphic lookup fields using WITH SECURITY_ENFORCED. Polymorphic fields are relationship fields that can point to more than one entity.
  • Traversing a polymorphic field’s relationship is not supported in queries using WITH SECURITY_ENFORCED. For example, you cannot use WITH SECURITY_ENFORCED in this query, which returns the Id and Owner names for User and Calendar entities: SELECT Id, What.Name FROM Event WHERE What.Type IN (’User’,’Calendar’).
  • Using TYPEOF expressions with an ELSE clause is not supported in queries using WITH SECURITY_ENFORCED. TYPEOF is used in a SELECT query to specify the fields to be returned for a given type of a polymorphic relationship. For example, you cannot use WITH SECURITY_ENFORCED in this query. The query specifies certain fields to be returned for Account and Opportunity objects, and Name and Email fields to be returned for all other objects.
    1SELECT 
    2TYPE OF What 
    3   WHEN Account THEN Phone 
    4   WHEN Opportunity THEN Amount 
    5   ELSE Name,Email 
    6END 
    7FROM Event
  • The Owner, CreatedBy, and LastModifiedBy polymorphic lookup fields are exempt from this restriction, and do allow polymorphic relationship traversal.
  • For AppExchange Security Review, you must use API version 48.0 or later when using WITH SECURITY_ENFORCED. You cannot use API versions where the feature was in beta or pilot.

If any fields or objects referenced in the SOQL SELECT query using WITH SECURITY_ENFORCED are inaccessible to the user, a System.QueryException is thrown, and no data is returned.

To enforce object and field permissions on the User object and hide a user’s personal information from other users in orgs with Experience Cloud sites, see Enforcing Object and Field Permissions.

Example

If field access for either LastName or Description is hidden, this query throws an exception indicating insufficient permissions.
1List<Account> act1 = [SELECT Id, (SELECT LastName FROM Contacts), 
2   (SELECT Description FROM Opportunities)
3   FROM Account WITH SECURITY_ENFORCED]

Example

If field access for Website is hidden, this query throws an exception indicating insufficient permissions.
1List<Account> act2 = [SELECT Id, parent.Name, parent.Website 
2   FROM Account WITH SECURITY_ENFORCED]

Example

If field access for Type is hidden, this aggregate function query throws an exception indicating insufficient permissions.

1List<AggregateResult> agr1 = [SELECT GROUPING(Type) 
2   FROM Opportunity WITH SECURITY_ENFORCED 
3   GROUP BY Type]