Developer guides

Restrict access to specific documents

Create document-level access controls with Sanity

Ensure your editors can only publish content they have permission to by implementing document-level access control.

This is a paid feature

This feature is available on certain Enterprise plans. Talk to sales to learn more.

Some use cases need access control at the level of an individual document. You can build this by adding metadata to your documents and then filtering on it.

Prerequisites

  • A project on the Enterprise plan. Custom roles and custom content resources are Enterprise features.
  • Permission to manage members and roles in the project. The default Administrator role includes it.
  • The sanity-plugin-user-select-input plugin installed in your studio and registered in sanity.config.ts.
  • A deployed studio schema, or a GROQ filter you write by hand. The content resource builder reads your deployed schema to list document types.

Add the metadata

Start by adding a new field to your document called allowedEditors. Each document needs to track which users can edit it, so store an array of user IDs.

One approach is to create an array of strings:

This gives you the correct data structure, but it is a poor editing experience, because someone has to type user IDs by hand.

Loading...
Allowed Editors UI with "user_id1" in the input field

You can improve the editing experience by installing the User Select Input plugin. Following the plugin’s installation guide gives you access to the userSelect field type.

Update the field type to use it:

defineField({
  name: "allowedEditors",
  type: "array",
  of: [{ type: "userSelect" }]
})
Loading...
Allowed Editors Selector using the User Select Input Sanity Plugin

Now you can search for users with an intuitive interface.

Currently, all users can modify Allowed Editors on your document. Update the field type so that the field is hidden to everyone except administrators.

import {userHasRole} from 'sanity'

defineField({
  name: "allowedEditors",
  type: "array",
  of: [{ type: "userSelect" }],
  hidden: ({ currentUser }) => !userHasRole(currentUser, "administrator"),
})

The hidden property only controls whether the field appears in the Studio form. It is not a permission boundary: anyone who can write to the document can still set allowedEditors through the API. The custom role you create in the next section is what enforces access.

The metadata field is now set up. Next, create a content resource that lets you assign users to a role that can only modify documents they have been allowed to edit.

Create a content resource

Define a new content resource by visiting sanity.io/manage, or using the CLI with:

If you opened sanity.io/manage in a browser, select your project first. The CLI command opens your project directly. In either case, open the Access tab, then select Resources in the sidebar.

Loading...
The content resources manage page

From here, create a new content resource. Name it whatever you like; this guide uses Allowed to Edit. In the GROQ filter section, define the filter that returns the documents the user can edit. Use the identity GROQ function to achieve this. The identity function returns the project user ID of the current user. Because the document stores an array of user IDs, this filter checks whether the current user is one of them.

identity() in allowedEditors

Attach the content resource to a custom role

Now that the content resource exists, apply it to a role by selecting Roles in the sidebar.

Loading...
Access Roles

From here, if you already have a custom role you want to apply this resource to, select it. If not, create a new role.

In the Content permissions section, select Edit, then set the permissions for your content resource to Publish.

Loading...
Content permissions setting with "Allowed to Edit" content resource set to "Publish"

Users assigned this role can now publish only the documents an administrator has allowed them to edit.

A better structure

The data structure and the permissions are now in place. The last step is to customize the Structure Tool so that editors only see the documents they can edit.

This section requires some experience with Structure Builder. If you have not customized your structure before, see Get started with Structure Builder API.

The following function defines a new list item that shows all posts if the user is an administrator, and only editable posts if the user is not.

import {userHasRole} from 'sanity'
import {type StructureBuilder} from 'sanity/structure'

function posts(S: StructureBuilder) {
  let documentFilter = "";

  if (userHasRole(S.context.currentUser, "administrator")) {
    documentFilter = '_type == "post"';
  } else {
    documentFilter = '_type == "post" && identity() in allowedEditors';
  }

  return S.listItem()
    .title("Posts")
    .id("posts")
    .child(S.documentList().title("Posts").filter(documentFilter));
}

You now have a system for defining which editors can work on which documents.

Was this page helpful?