SyntaxStudy
Sign Up
GraphQL Role-Based Access Control in GraphQL
GraphQL Beginner 1 min read

Role-Based Access Control in GraphQL

Role-based access control (RBAC) restricts resolver execution based on the authenticated user's role. The most maintainable approach is to define roles as an enum in the schema, store them in the user record, and check them in the context object that is already available to every resolver. For fine-grained permissions beyond simple roles, attribute-based access control (ABAC) evaluates policies against user attributes, resource attributes, and environment conditions. Libraries like CASL integrate well with GraphQL and let you define permissions as can(action, subject) rules that are checked in resolvers or middleware.
Example
// Role enum matches schema enum Role { ADMIN EDITOR VIEWER }

// Utility: assert role
function requireRole(currentUser, ...roles) {
  if (!currentUser) {
    throw new GraphQLError('Unauthenticated', {
      extensions: { code: 'UNAUTHENTICATED' },
    });
  }
  if (!roles.includes(currentUser.role)) {
    throw new GraphQLError('Forbidden', {
      extensions: { code: 'FORBIDDEN' },
    });
  }
}

// CASL integration
import { AbilityBuilder, createMongoAbility } from '@casl/ability';

function defineAbilityFor(user) {
  const { can, cannot, build } = new AbilityBuilder(createMongoAbility);

  if (user.role === 'ADMIN') {
    can('manage', 'all');
  } else if (user.role === 'EDITOR') {
    can('read',   'Post');
    can('create', 'Post');
    can('update', 'Post', { authorId: user.id }); // own posts only
    cannot('delete', 'Post');
  } else {
    can('read', 'Post', { published: true });
  }

  return build();
}

// In resolver
const resolvers = {
  Mutation: {
    deletePost: async (_, { id }, { currentUser, db }) => {
      const ability = defineAbilityFor(currentUser);
      const post = await db.posts.findById(id);
      if (ability.cannot('delete', post)) throw new GraphQLError('Forbidden', { extensions: { code: 'FORBIDDEN' } });
      return db.posts.delete(id);
    },
  },
};