Permissions & Storage Lock

What permissions designers and users need, and how configuration writes are controlled.

Model Overview

The form configuration is a file in the Site Assets library, so it is protected by SharePoint permissions. The model has three layers:

  1. Who may open the designer (designer role in the configuration): site owners, or site owners and a designated designer group.
  2. Who may write the configuration: controlled by locking the SiteAssets/FormCustomizer folder (unique permissions).
  3. Who may read it: the form runtime reads the configuration as the signed-in user, so anyone opening the form must have read access.

The "editor lock" password in the designer is not a security boundary. It prevents accidental opening of the editor, not reading or overwriting the file. True protection is provided only by the folder lock described below.

The Form Settings button (gear icon) and the Custom form command are visible only to authorized users. The role is configured in the settings (securityConfig.allowedEditorRole, see Configuration Security and the user guide page on security).

allowedEditorRole ValueWho May Open the Designer
manageWeb (default for new forms)Site owners: users with the ManageWeb permission on the list.
groupSite owners and members of the SharePoint group specified in allowedEditorGroup (by group name).
manageLists (legacy value)Behaves like manageWeb: only site owners can modify the configuration. The "Manage Lists" permission alone is not sufficient because members with Edit level also have it.

Permissions are verified by checking the list's effectiveBasePermissions and group membership. If verification fails (REST error), the buttons are not displayed and the user is treated as unauthorized.

To lock the storage itself, the ManagePermissions permission on the site (site owner) is required. A designer from the designer group can save configurations, but cannot change folder permissions.

Creating Columns from the Designer

Only a user with the ManageLists (Manage Lists) permission on the list can create a new list column from the designer. This permission is read from pageContext.list.permissions and reported by the data provider as canCreateFields; without it, the create column action is not offered. This is separate from the designer role above: the column is created under the signed-in user's identity, so SharePoint would reject it without permissions anyway. The column is added only to the content types selected by the user. If adding to a content type fails (e.g., missing permission on the content type), the column remains in the list and the designer lists which types it could not be added to.

Locked Configuration Storage

While the storage is unlocked, the folder inherits permissions from the Site Assets library. Anyone with write access to the library (typically a site member with Edit permission) could then overwrite the configuration and inject custom actions. The lock prevents this.

What the Lock Does

The root folder SiteAssets/FormCustomizer receives unique permissions:

  1. Permission inheritance is broken copying existing permissions (readers do not lose access).
  2. The site owners group and the signed-in user performing the lock receive Full Control.
  3. All other principals retain only Read and Limited Access. Anything higher (Contribute, Edit, Design, or custom roles with Manage Lists or Manage Permissions) is replaced with Read. Roles are identified by type, not by localized name.
  4. Read is added for the entire organization (Everyone except external users, EEEU) and for guests (Everyone). Both can be disabled in the designer: Read configuration for entire organization (securityConfig.configReadAllUsers) and Read configuration for external users (guests) (securityConfig.configReadGuests); both are enabled by default.
  5. Nested items with unique permissions are reset to inherit from the root. List subfolders are an exception (see below).

Access is added first and removed afterwards during changes, so nobody temporarily loses read access. If locking fails, the original inheritance is restored. If restoring also fails, the designer displays a warning with a repair command.

Locking includes cleaning up nested items under FormCustomizer that have unique permissions (resetting them to inherit). The query finds nested items by FileRef and reads the unique permissions flag (HasUniqueRoleAssignments) for each item individually. If SharePoint's response omits this field for any item, locking fails with lockFailed and nothing is considered safe.

Designer, Security, "Configuration Storage" section: status "Locked: only site owners and the designer group can write", toggles for organization and guest read access, and the Lock Now button
Permissions page of the FormCustomizer folder in Site Assets (Manage Access) after locking: Full Control for owners, Read for others and the organization

When It Locks

An unauthorized user (a designer without permission to manage permissions) cannot lock the storage. After saving to an unlocked storage, the designer receives a warning "Configuration storage is not locked" with a command for the site owner.

Reconciling Permissions on Subsequent Saves

When the storage is already locked, a site owner saving the configuration does not re-apply permissions unless the inputs have changed: the signed-in owner, designer group, organization read access, guest read access, and tenant. Saving is therefore fast and does not trigger dozens of REST calls. A fingerprint of these inputs is stored in SiteAssets/FormCustomizer/<listId>.perm.json in the storage root, which only owners can write to, so it persists across page loads and cannot be spoofed by a designer. It is written only after a clean reconciliation with no errors or warnings.

Confirmation on First Lock

If the configuration file was created before locking (or imported from a legacy location), anyone could have modified it in the meantime. Before sealing, the designer displays a Lock and save this configuration? dialog with a security summary:

Only after clicking Confirm, lock and save is the storage sealed. When clicking "Lock Now" on an existing file, Lock and seal existing file is offered. If you do not trust the contents, cancel the dialog and save the form from the designer: the file will be overwritten with your draft. On the first lock, pre-lock history is also deleted and the backup is replaced with the sealed file.

"Lock and save this configuration?" dialog with summary: file source, scripts, action buttons, rules, role and designer group, and Confirm, lock and save / Cancel buttons

Designer Group

To allow people who are not site owners to edit the configuration, set the role to group and specify the SharePoint group name (allowedEditorGroup).

Rejected Groups

Saving is rejected and permissions are not changed if the designer group is:

A group that does not exist on the site is saved with a warning "designer group … does not exist on the site": it has no write access. Check the exact name.

License File

The license.txt file resides directly in the root FormCustomizer folder, so after locking it inherits its permissions: only a site owner can write to it; members and designers receive a 403 error and the license dialog explains why. Read access remains open to everyone (the license is also read by unauthorized users). While the storage is unlocked, anyone with write access to Site Assets can overwrite the file, but the key is signed so a valid key cannot be forged. See Licensing.

Manual Locking and Repair in PowerShell

The designer provides the command under Security, Configuration Storage section, and also displays it in post-save warnings (notLocked, lockFailed, rollbackFailed). The command is intended for PnP.PowerShell and a site owner. It includes the list subfolder if a designer group is configured. The example below uses placeholders; copy the actual command from the designer (it includes your site URL, <listId>, and group name).

Connect-PnPOnline -Url "https://<tenant>.sharepoint.com/sites/<site>" -Interactive
$web = Get-PnPWeb -Includes AssociatedOwnerGroup,CurrentUser,RoleDefinitions
$ctx = Get-PnPContext
$role = { param($t) $web.RoleDefinitions | Where-Object { $_.RoleTypeKind -eq $t } | Select-Object -First 1 }
$full = & $role "Administrator"; $read = & $role "Reader"; $contrib = & $role "Contributor"
function Grant($item, $principal, $def) {
  $b = New-Object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($ctx); $b.Add($def)
  $null = $item.RoleAssignments.Add($principal, $b)
}
# 1) FormCustomizer root: break inheritance WITH copy (readers keep access)
$root = (Get-PnPFolder -Url "/sites/<site>/SiteAssets/FormCustomizer" -Includes ListItemAllFields).ListItemAllFields
$root.BreakRoleInheritance($true, $true); Invoke-PnPQuery
# 2) owners and current user = Full Control
Grant $root $web.AssociatedOwnerGroup $full; Grant $root $web.CurrentUser $full; Invoke-PnPQuery
# 3) leave only Read and Limited Access for everyone else
$null = Get-PnPProperty -ClientObject $root -Property RoleAssignments
foreach ($ra in @($root.RoleAssignments)) {
  $null = Get-PnPProperty -ClientObject $ra -Property Member, RoleDefinitionBindings
  if ($ra.Member.Id -eq $web.AssociatedOwnerGroup.Id -or $ra.Member.Id -eq $web.CurrentUser.Id) { continue }
  $keep = @($ra.RoleDefinitionBindings | Where-Object { $_.RoleTypeKind -in 'Reader','Guest' })
  if ($keep.Count -ne @($ra.RoleDefinitionBindings).Count) {
    $ra.RoleDefinitionBindings.RemoveAll(); $ra.RoleDefinitionBindings.Add($read)
    foreach ($k in $keep) { if ($k.RoleTypeKind -eq 'Guest') { $ra.RoleDefinitionBindings.Add($k) } }
    $ra.Update()
  }
}
Invoke-PnPQuery
# 4) read access for entire organization and guests (omit what you don't want)
$eeeu = "c:0-.f|rolemanager|spo-grid-all-users/$(Get-PnPTenantId)"
foreach ($login in $eeeu, "c:0(.s|true") { Grant $root ($web.EnsureUser($login)) $read }
Invoke-PnPQuery
# 5) only when using a designer group: list subfolder (copy from root + Contribute)
$sub = (Get-PnPFolder -Url "/sites/<site>/SiteAssets/FormCustomizer/<listId>" -Includes ListItemAllFields).ListItemAllFields
$sub.BreakRoleInheritance($true, $true); Invoke-PnPQuery
Grant $sub $web.SiteGroups.GetByName("Form Designers") $contrib; Invoke-PnPQuery

The command generated by the designer has not been verified against a live tenant (a comment in the code notes this). Test it on a staging site before production use and verify the result on the folder permissions page.

Residual Risks

End-User Permissions

The form does not have its own data permissions: it operates with the signed-in user's permissions. Therefore, the user needs:

Fields that fail to load (e.g., due to permissions or a batch query error) are shown as read-only in the form and are not written on save. See Unloaded Fields. Display mode (DisplayForm) never reports unsaved changes.

Users Outside Site Groups

A user who lacks read access to the configuration folder (e.g., access only via item sharing, or a guest when guest read access is disabled) cannot read the configuration. For them, the form runs with the default layout generated from list columns, without your tabs, rules, or buttons. A site owner sees a warning about the unreadable configuration, while a regular user sees the message "The form could not be loaded completely. Please contact the site owner." The solution is to keep organization read access (and guest access, if needed) enabled, or add the user to a site group.

If you rely on form rules to hide sensitive fields or block saving, ensure all users can read the configuration. Rules are not a security mechanism: true data protection is provided only by SharePoint list and column permissions.

NoScript Sites

On sites with DenyAddAndCustomizePages (NoScript, default on modern sites), writing to the web property bag is not possible, even for owners. Therefore, the product does not write to the web property bag: both configuration and license are stored as files in Site Assets. Legacy keys in the property bag are still read. You do not need the AddAndCustomizePages permission.