Skip to Page NavigationSkip to Page NavigationSkip to Content
Keystone 6 is in Community Preview! For Keystone 5 docs visit v5.keystonejs.com

Access Control API

We recently improved this API so it’s easier to program, and makes it harder to introduce security gaps in your system. If you were using it prior to September 6th 2021, read this guide for info on how to upgrade.

The access property of the list configuration object configures who can query, create, update, and delete items in your Keystone system. The access property of the field configuration object configures who can read, create, and update specific field values of an item.

import { config, list } from '@keystone-next/keystone';
import { text } from '@keystone-next/keystone/fields';
export default config({
lists: {
ListKey: list({
fields: {
fieldName: text({ access: { /* ... */ }, }),
},
access: { /* ... */ },
}),
},
});

List Access Control

Keystone lets you apply access control on a per-list basis. By default it allows all operations for all users. Access control is applied to the generated CRUD operations and mutations in the GraphQL API, and is applied before any hooks are executed.

List-level access control can be specified in three ways.

  • operation access control lets you check the information in the context and session objects to decide if the user is allowed to access the list.
  • filter access control lets you provide a GraphQL filter to restrict the items the user is allowed to access.
  • item access control lets you write a function which inspects the provided input data and the existing object (if it exists) and make a decision based on this extra data.

Access denied: Mutations return null data with an access denied error. Queries never return access denied errors, and instead treat items as if they didn't exist.

If access is denied due to any of the access control methods, the following responses will be returned from the GraphQL API:

  • Mutations
    • Single operations return null and an access denied error.
    • Multi operations return a data array with null values for the items which have access denied. Error responses are returned for each null item.
  • Queries
    • Single item queries return null with no errors.
    • Many item queries filter out items which have access denied. No error responses are returned.
    • Count queries will only count those items for which access is not denied. No error responses are returned.

Operation

Operation-level access control lets you control which operations can be accessed by a user based on the session and context. Individual functions can be provided for each of the operations.

These functions must return true if the operation is allowed, or false if it is not allowed.

import { config, list } from '@keystone-next/keystone';
export default config({
lists: {
ListKey: list({
access: {
operation: {
query: ({ session, context, listKey, operation }) => true,
create: ({ session, context, listKey, operation }) => true,
update: ({ session, context, listKey, operation }) => true,
delete: ({ session, context, listKey, operation }) => true,
}
},
}),
},
});

The query access control rule is applied when running GraphQL query operations. It does not prevent a user reading the data returned by the mutation operations. You should ensure that your create, update, and delete are also configured to prevent access to protected data.

Filter

Filter-level access control lets you restrict which items can be operated on by providing a function which returns a GraphQL filter.

  • For mutations, the access control filter will be combined with the unique identifier provided to the operation, and access will be denied if no item is found with this combined filter.
  • For queries, the access control filter will be combined with the query filter and only items which match both filters will be returned.

In general, the filter access control functions will return GraphQL filters. They can also return boolean values true or false to match or exclude all items.

import { config, list } from '@keystone-next/keystone';
import { checkbox } from '@keystone-next/keystone/fields';
export default config({
lists: {
ListKey: list({
fields: {
isReadable: checkbox(),
isUpdatable: checkbox(),
isDeletable: checkbox(),
}
access: {
filter: {
query: ({ session, context, listKey, operation }) => {
return { isReadable: { equals: true } };
},
update: ({ session, context, listKey, operation }) => {
return { isUpdatable: { equals: true } };
},
delete: ({ session, context, listKey, operation }) => {
return { isDeletable: { equals: true } };
},
}
},
}),
},
});

Filter access control cannot be used on create operations, as there is no pre-existing item to filter against. To restrict create operations configure either access.operation.create or access.item.create.

Filter based access control can impact the performance when querying related items on a list. Keystone optimises queries for to-one relationships, however these optimisations cannot be used in conjunction with access control filters.

Item (mutations only)

item is the final and most powerful level of access control. It’s available to create, update, and delete mutations, and lets you write functions which have access to:

  • the input data of the mutation (for create and update operations), and
  • the existing item in the database (for update and delete operations).

These functions must return true if the operation is allowed, or false if it is not allowed.

import { config, list } from '@keystone-next/keystone';
import { checkbox } from '@keystone-next/keystone/fields';
export default config({
lists: {
ListKey: list({
access: {
item: {
create: ({ session, context, listKey, operation, inputData }) => true,
update: ({ session, context, listKey, operation, inputData, item }) => true,
delete: ({ session, context, listKey, operation, item }) => true,
}
},
}),
},
});

Item-level access control is not available for query operations. Applying access control after fetching items would lead to inconsistent pagination behaviour and incorrect count results.

Function Arguments

List-level access control functions are passed a collection of arguments which can be used to determine whether the operation is allowed.

ArgumentDescription
sessionThe current session object. See the Sessions API for details.
contextThe KeystoneContext object of the originating GraphQL operation.
listKeyThe key of the list being operated on.
operationThe operation being performed ('query', 'create', 'update', 'delete').
inputDataFor create and update operations, this is the value of data passed into the mutation. (Item level only)
itemThe existing item being updated/deleted in update and delete operations. (Item level only)

Field Access Control

Keystone also allows you to set up access control on a per-field basis. Rules can be set for read, create and update operations.

Each operation is defined by a function which returns true if access is allowed and false if access is not allowed.

Mutations

Field-level access control rules are applied after the list level access rules have been applied. Access control rules are only applied to the fields that have an input value provided to the mutation. If any of the provided fields fail their access control check, the whole operation is aborted. The GraphQL API then returns null along with an access denied error.

Read

Field-level access control rules are applied when trying to resolve a field on the output type. If access is denied then the query will still return an item object, but the specific field will return null. No errors will be returned for read access denied.

The read access control is applied to fields returned from both queries and mutations.

import { config, list } from '@keystone-next/keystone';
import { text } from '@keystone-next/keystone/fields';
export default config({
lists: {
ListKey: list({
fields: {
fieldName: text({
access: {
read: ({ session, context, listKey, fieldKey, operation, item }) => true,
create: ({ session, context, listKey, fieldKey, operation, inputData }) => true,
update: ({ session, context, listKey, fieldKey, operation, inputData, item }) => true,
},
}),
},
}),
},
});

Field-level access control is not available for delete operations. Restrict delete operations at the List-level instead with access.operation.delete, access.filter.delete or access.item.delete.

Function Arguments

Field-level access control functions are passed a collection of arguments which can be used to determine whether the operation is allowed.

ArgumentDescription
sessionThe current session object. See the Sessions API for details.
contextThe KeystoneContext object of the originating GraphQL operation.
listKeyThe key of the list being operated on.
fieldKeyThe key of the field being operated on.
operationThe operation being performed ('read', 'create', 'update').
inputDataFor create and update operations, this is the value of data passed into the mutation.
itemThe existing item being read/updated in read and update operations.