Access Control
Control who can do what with your GraphQL API.
Note: This is the API documentation for Access Control. For getting started, see the Access Control Guide or the Authentication Guide.
Table of Contents
GraphQL Access Control
There are two ways of specifying Access Control:
- List level
- Field level
Defaults
To set defaults for all lists & fields, use the defaultAccess config when
creating a Keystone instance:
const keystone = new Keystone('My App', {
  // Initial values shown here:
  defaultAccess: {
    list: true,
    field: true,
  },
  // ...
});
List level access control
List level access control can have varying degrees of specificity depending on how much control you need.
Access API
A key on the list config, access can be specified either as a single control,
covering all CRUD operations, or as an object keyed by CRUD operation names.
There are 3 ways to define the values of access, in order of flexibility:
- Static
- Imperative
- Declarative
Described as a Flow type, it looks like this:
type GraphQLWhere = {}; // fake/placeholder
type AccessInput = {
  authentication: {
    item?: {},
    listKey?: string,
  },
  listKey?: string,
  operation?: string,
  originalInput?: {},
  gqlName?: string,
  itemId?: string,
  itemIds?: [string],
};
type StaticAccess = boolean;
type ImperativeAccess = AccessInput => boolean;
type DeclarativeAccess = GraphQLWhere | (AccessInput => GraphQLWhere);
type ListConfig = {
  access:
    | StaticAccess
    | ImperativeAccess
    | {
        create?: StaticAccess | ImperativeAccess,
        read?: StaticAccess | ImperativeAccess | DeclarativeAccess,
        update?: StaticAccess | ImperativeAccess | DeclarativeAccess,
        delete?: StaticAccess | ImperativeAccess | DeclarativeAccess,
      },
  // ...
};
GraphQLWhere matches the where clause on the GraphQl type.
ie; for a list User, it would match the input type UserWhereInput.
AccessInput function parameter
- authenticationdescribes the currently authenticated user.- .itemis the details of the current user. Will be- undefinedfor anonymous users.
- .listKeyis the list key of the currently authenticated user. Will be- undefinedfor anonymous users.
 
- listKeyis the key of the list being operated on.
- operationis the CRUDA operation being peformed (- 'create',- 'read',- 'update',- 'delete',- 'auth').
- originalInputfor- create&- updatemutations, this is the data as passed in the mutation.
- gqlNameis the name of the query or mutation which triggered the access check
- itemIdis the- idof the item being updated/deleted in singular- updateand- deleteoperations.
- itemIdsare the- idsof the items being updated/deleted in multiple- updateand- deleteoperations.
When resolving StaticAccess;
- true: Allow access
- false: Do not allow access
Definition of access operations:
- create: Ability to create new items in the list
- read: Ability to view / fetch data on any items in the list
- update: Ability to alter data on any items in the list
- delete: Ability to remove an item from the list
When access is denied, the GraphQL response will contain an error with
type: 'AccessDeniedError', and null for the data.
Note: The create operation cannot be given DeclarativeAccess - it does not
make sense to do so and will throw an error if attempted.
Let's break it down into concrete examples:
Booleans
Shorthand static Boolean
keystone.createList('User', {
  access: true,
  fields: {
    // ...
  },
});
Great for blanket access control for lists you want everyone/no one to see.
NOTE: When set to false, the list queries/mutations/types will not be included in the GraphQL schema.
Granular static Booleans
keystone.createList('User', {
  access: {
    create: true,
    read: true,
    update: true,
    delete: true,
  },
  fields: {
    // ...
  },
});
Use when you need some more fine grained control over what actions users can perform.
NOTE: When set to false, the list queries/mutations/types exclusive to that
operation will not be included in the GraphQL schema. For example, setting
create: false will cause the createXXXX mutation to be excluded from the
schema, update: false will cause the updateXXXX mutation to be excluded, and
so on.
Shorthand Imperative Boolean
keystone.createList('User', {
  access: ({ authentication: { item, listKey } }) => {
    return true;
  },
  fields: {
    // ...
  },
});
Enables turning access on/off based on the currently authenticated user.
NOTE: Even when returning false, the queries/mutations/types will be
included in the GraphQL Schema.
Granular functions returning Boolean
keystone.createList('User', {
  access: {
    create: ({ authentication: { item, listKey } }) => true,
    read: ({ authentication: { item, listKey } }) => true,
    update: ({ authentication: { item, listKey } }) => true,
    delete: ({ authentication: { item, listKey } }) => true,
  },
  fields: {
    // ...
  },
});
Use when you need some more fine grained control over what actions some or all anonymous/authenticated users can perform.
NOTE: Even when returning false,
the queries/mutations/types for that operation will be included in the GraphQL Schema.
For example, create: () => false will still include the createXXXX mutation in the GraphQL Schema, and so on.
GraphQLWhere
In the examples below, the name_contains: 'k' syntax matches the UserWhereInput GraphQL type for the list.
NOTES:
- For singular read/update/deleteoperations, when theGraphQLWhereclause results in 0 items, anAccessDeniedErroris returned.
- For batch readoperations (eg;query { allUsers }), when theGraphQLWhereclause results in 0 items returned, no error is returned.
- For createoperations, anAccessDeniedErroris returned if the operation is set to / returnsfalse
Granular static GraphQLWheres
keystone.createList('User', {
  access: {
    create: true,
    read: { name_contains: 'k' },
    update: { name_contains: 'k' },
    delete: { name_contains: 'k' },
  },
  fields: {
    name: { type: Text },
    // ...
  },
});
Use when you need some more fine grained control over what items a user can perform actions on.
Granular functions returning GraphQLWhere
keystone.createList('User', {
  access: {
    create: ({ authentication: { item, listKey } }) => true,
    read: ({ authentication: { item, listKey } }) => ({
      state_not: 'deactivated',
    }),
    update: ({ authentication: { item, listKey } }) => ({
      state_not: 'deactivated',
    }),
    delete: ({ authentication: { item, listKey } }) => ({
      state_not: 'deactivated',
    }),
  },
  fields: {
    state: {
      type: Select,
      options: ['active', 'deactivated'],
      defaultValue: 'active',
    },
    // ...
  },
});
Use when you need some more fine grained control over which items and actions anonymous/authenticated users can perform.
Field level access control
access API
A key on the field config, access can be specified either as a single control,
covering all CRU operations, or as an object keyed by CRU operation names.
There are 2 ways to define the values of access, in order of flexibility:
- Static
- Imperative
Described as a Flow type, it looks like this:
type AccessInput = {
  authentication: {
    item?: {},
    listKey?: string,
  },
  listKey?: string,
  fieldKey?: string,
  originalInput?: {},
  existingItem?: {},
  operation?: string,
  gqlName?: string,
  itemId?: string,
  itemIds?: [string],
};
type StaticAccess = boolean;
type ImperativeAccess = AccessInput => boolean;
type FieldConfig = {
  access:
    | StaticAccess
    | ImperativeAccess
    | {
        create?: StaticAccess | ImperativeAccess,
        read?: StaticAccess | ImperativeAccess,
        update?: StaticAccess | ImperativeAccess,
      },
  // ...
};
NOTE: Unlike List level access, it is not possible to specify a Declarative where clause for Field level access.
NOTE: Fields do not have a delete access controls - this control exists on
the list level only (it's not possible to 'delete' an existing field value -
only to modify it).
AccessInput function parameter
- authenticationdescribes the currently authenticated user.- .itemis the details of the current user. Will be- undefinedfor anonymous users.
- .listKeyis the list key of the currently authenticated user. Will be- undefinedfor anonymous users.
 
- listKeyis the key of the list being operated on.
- fieldKeyis the key of the field being operated on.
- originalInputis the data as passed in the mutation for- create&- updatemutations (- undefinedfor- read).
- existingItemis the existing item this field belongs to for- updatemutations &- readqueries (- undefinedfor- create).
- operationis the CRUDA operation being performed (- 'create',- 'read',- 'update',- 'delete',- 'auth').
- gqlNameis the name of the query or mutation which triggered the access check
- itemIdis the- idof the item being updated/deleted in singular- updateand- deleteoperations.
- itemIdsare the- idsof the items being updated/deleted in multiple- updateand- deleteoperations.
When defining StaticAccess;
- true: Allow access
- false: Do not allow access
Definition of access operations:
- create: Ability to set the value of the field when creating a new item
- read: Ability to view / fetch the value of this field on an item
- update: Ability to alter the value of this field on an item
When access is denied, the GraphQL response will contain an error with type: 'AccessDeniedError',
and null for the field.
Let's break it down into concrete examples:
Shorthand static Boolean
keystone.createList('User', {
  fields: {
    name: {
      type: Text,
      access: true,
    },
  },
});
Great for blanket access control for fields you want everyone/no one to see.
NOTE: When set to false, the list queries/mutations/types will not include
this field in the GraphQL schema.
Granular static Booleans
keystone.createList('User', {
  fields: {
    name: {
      type: Text,
      access: {
        create: true,
        read: true,
        update: true,
      },
    },
  },
});
Use when you need some more fine grained control over what actions users can perform with this field.
NOTE: When set to false, this field will not be included in GraphQL
queries/mutations/types exclusively used by that operation.
Eg, setting update: false in the example above will remove the name field from the
UserUpdateInput type but may still include the field in UserCreateInput for example.
Shorthand Imperative Boolean
keystone.createList('User', {
  fields: {
    name: {
      type: Text,
      access: ({ authentication: { item, listKey }, existingItem }) => {
        return true;
      },
    },
  },
});
Enables turning access on/off based on the currently authenticated user.
NOTE: Even when returning false, the queries/mutations/types will
include the field in the GraphQL Schema.
Granular functions returning Boolean
keystone.createList('User', {
  access: {
    create: ({ authentication: { item, listKey }, existingItem }) => true,
    read: ({ authentication: { item, listKey }, existingItem }) => true,
    update: ({ authentication: { item, listKey }, existingItem }) => true,
  },
  fields: {
    // ...
  },
});
Use when you need some more fine grained control over what actions some or all anonymous/authenticated users can perform.
NOTE: Even when returning false, this field will be included in GraphQL
queries/mutations/types exclusively used by that operation.
Eg, setting update: () => false in the example above will still include the
name field in the UserUpdateInput type.