Get current user inside validation for role-based rules

6 replies
Last updated: Jul 26, 2022
Hallo guys, is there any way to get the current user inside validation? Thinking about apply different validation rules for different users with different roles.
AI Update

Unfortunately, you cannot directly access the current user context inside validation functions in Sanity. The validation function's context parameter only provides access to document data (like parent and document), not user information.

From the validation documentation, the context object in validation functions includes things like the parent object and document values, but not the current user.

However, there are several alternative approaches you can use to achieve role-based validation:

Alternative Approaches

1. Use Custom Input Components with User Context

Custom input components have access to the current user via the useCurrentUser() hook. You can apply different validation logic or UI based on roles:

import { useCurrentUser, StringInputProps } from 'sanity'

export function RoleBasedInput(props: StringInputProps) {
  const currentUser = useCurrentUser()
  const isAdmin = currentUser?.roles.some(role => role.name === 'administrator')
  
  // Apply different validation or UI based on role
  // ...
}

2. Use readOnly or hidden Callbacks

The readOnly and hidden field options do receive user context through their callback functions. This won't provide validation messages, but can prevent unauthorized editing:

defineField({
  name: 'sensitiveField',
  type: 'string',
  readOnly: (context) => {
    const userRoles = context.currentUser?.roles.map(r => r.name) || []
    return !userRoles.includes('administrator')
  }
})

3. Store User Info in the Document

As shown in the Studio customizations course, you can store user information in the document itself, then reference it in validation:

defineField({
  name: 'createdBy',
  type: 'string',
  initialValue: (params, context) => context.currentUser?.id,
  hidden: true
}),
defineField({
  name: 'someField',
  type: 'string',
  validation: (rule) => rule.custom((value, context) => {
    const createdBy = context.document?.createdBy
    // Apply different validation rules based on createdBy
    if (createdBy === 'specific-user' && !value) {
      return 'This field is required for you'
    }
    return true
  })
})

4. Backend Validation with Document Actions or Functions

For critical validation that must be enforced server-side, use custom document actions or Sanity Functions to validate on publish. Functions can access the authenticated user making the request and enforce rules accordingly.

Remember that Studio-level validation is primarily for user experience - backend validation ensures data integrity regardless of who's editing.

The combination of readOnly/hidden callbacks with custom input components is usually the most practical approach for role-based field control in the Studio!

Show original thread
6 replies
I think this is possible. Validation has the ability to allow custom validation rules and async operations, and the current user store is part of the JS client and should expose the default roles, e.g: https://www.sanity.io/schemas/initial-value-template-with-current-user-4449e575

N.B.: There's a warning at the end of the validation doc that validation happens on every field change but I think it applies more to truly external APIs (like not getting rate limited trying to get a stock price because you typed a sentence and rang up fifty requests in a few seconds =)
Thanks for the reply and I had a try but getting some error, you can see the screenshot.
That's an interesting error; I am not sure what the type is for validation; I'd assume an object, but I thought I'd give it a try myself. Outside the schema at the top of the file I wrote

const getUser = async () => {
    const userObject = await userStore.getUser('me')
    if (userObject) {
        return userObject
    }
}
and then under my array field, I wrote


validation: Rule => Rule.required().custom(value => {
                return getUser().then(user => {
                    if (user.displayName === 'Vincent Florio') {
                        return true
                    } else {
                        return 'It isn't Vincent'
                    }
                })
            }),
and it worked as you'd expect. No errors if I was me (I am) but as a test I inverted the check to be
!== and got my message as a validation warning.
I make no claim that it's performant or a good way to do it, but it functions as a proof of concept.
Cool many thanks for the help πŸ‘
I'm asking that because in sanity if we set a max rule for the array, the "add item" button would be disabled if there's already 2 items and that could prevent user clicking that button

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?