v3-docs/docs/packages/roles.md
Authorization package for Meteor - compatible with built-in accounts package.
Available since Meteor 3.1.0 (previously alanning:roles)
To add roles to your application, run this command in your terminal:
meteor add roles
The roles package lets you attach roles to users and then check against those roles when deciding whether to grant access to Meteor methods or publish data. The core concept is simple - you create role assignments for users and then verify those roles later. This package provides helper methods to make the process of adding, removing, and verifying roles easier.
Although named "roles", you can define your roles, scopes or permissions however you like. They are essentially tags assigned to users that you can check later.
You can have traditional roles like admin or webmaster, or more granular permissions like view-secrets, users.view, or users.manage. Often, more granular permissions are better as they handle edge cases without creating many higher-level roles.
Roles can be organized in a hierarchy:
Example hierarchy setup:
import { Roles } from "meteor/roles";
// Create base roles
await Roles.createRoleAsync("user");
await Roles.createRoleAsync("admin");
// Create permission roles
await Roles.createRoleAsync("USERS_VIEW");
await Roles.createRoleAsync("POST_EDIT");
// Set up hierarchy
await Roles.addRolesToParentAsync("USERS_VIEW", "admin");
await Roles.addRolesToParentAsync("POST_EDIT", "admin");
await Roles.addRolesToParentAsync("POST_EDIT", "user");
Scopes allow users to have independent sets of roles. Use cases include:
Users can have both scoped roles and global roles:
Example using scopes:
// Assign scoped roles
await Roles.addUsersToRolesAsync(userId, ["manage-team"], "team-a");
await Roles.addUsersToRolesAsync(userId, ["player"], "team-b");
// Check scoped roles
await Roles.userIsInRoleAsync(userId, "manage-team", "team-a"); // true
await Roles.userIsInRoleAsync(userId, "manage-team", "team-b"); // false
// Assign global role
await Roles.addUsersToRolesAsync(userId, "super-admin", null);
// Global roles work in all scopes
await Roles.userIsInRoleAsync(userId, ["manage-team", "super-admin"], "team-b"); // true
Example:
import { Roles } from "meteor/roles";
// Create a new role
await Roles.createRoleAsync("admin");
// Create if doesn't exist
await Roles.createRoleAsync("editor", { unlessExists: true });
Example:
// Make 'editor' a child role of 'admin'
await Roles.addRolesToParentAsync("editor", "admin");
// Add multiple child roles
await Roles.addRolesToParentAsync(["editor", "moderator"], "admin");
Example:
// Remove 'editor' as child role of 'admin'
await Roles.removeRolesFromParentAsync("editor", "admin");
Example:
// Delete role and all its assignments
await Roles.deleteRoleAsync("temp-role");
Example:
// Rename an existing role
await Roles.renameRoleAsync("editor", "content-editor");
Example:
// Add global roles
await Roles.addUsersToRolesAsync(userId, ["admin", "editor"]);
// Add scoped roles
await Roles.addUsersToRolesAsync(userId, ["manager"], "department-a");
// Add roles to multiple users
await Roles.addUsersToRolesAsync([user1Id, user2Id], ["user"]);
Example:
// Replace user's global roles
await Roles.setUserRolesAsync(userId, ["editor"]);
// Replace scoped roles
await Roles.setUserRolesAsync(userId, ["viewer"], "project-x");
// Clear all roles in scope
await Roles.setUserRolesAsync(userId, [], "project-x");
Example:
// Remove global roles
await Roles.removeUsersFromRolesAsync(userId, ["admin"]);
// Remove scoped roles
await Roles.removeUsersFromRolesAsync(userId, ["manager"], "department-a");
// Remove roles from multiple users
await Roles.removeUsersFromRolesAsync([user1Id, user2Id], ["temp-role"]);
Example:
// Rename a scope
await Roles.renameScopeAsync("department-1", "marketing");
Example:
// Remove a scope and all its role assignments
await Roles.removeScopeAsync("old-department");
Example:
// Get all roles sorted by name
const roles = Roles.getAllRoles({ sort: { _id: 1 } });
// Get roles with custom query
const customRoles = Roles.getAllRoles({
fields: { _id: 1, children: 1 },
sort: { _id: -1 },
});
Example:
// returns a cursor of all admin users
const adminUsersCursor = await Roles.getUsersInRoleAsync("admin");
// Find all admin users
const adminUsers = await (await Roles.getUsersInRoleAsync("admin")).fetchAsync();
// Find users with specific roles in a scope
const scopedUsers = await (await Roles.getUsersInRoleAsync(
["editor", "writer"],
"blog"
)).fetchAsync();
// Find users with custom options
const users = await (await Roles.getUsersInRoleAsync("manager", {
scope: "department-a",
queryOptions: {
sort: { createdAt: -1 },
limit: 10,
},
})).fetchAsync();
Example:
// Check global role
const isAdmin = await Roles.userIsInRoleAsync(userId, "admin");
// Check any of multiple roles
const canEdit = await Roles.userIsInRoleAsync(userId, ["editor", "admin"]);
// Check scoped role
const isManager = await Roles.userIsInRoleAsync(
userId,
"manager",
"department-a"
);
// Check role in any scope
const hasRole = await Roles.userIsInRoleAsync(userId, "viewer", {
anyScope: true,
});
Example:
// Get user's global roles
const globalRoles = await Roles.getRolesForUserAsync(userId);
// Get scoped roles
const deptRoles = await Roles.getRolesForUserAsync(userId, "department-a");
// Get all roles including inherited
const allRoles = await Roles.getRolesForUserAsync(userId, {
anyScope: true,
fullObjects: true,
});
Example:
// Check if admin is a parent of editor
const isParent = await Roles.isParentOfAsync("admin", "editor");
// Can be used to check inheritance chains
const hasPermission = await Roles.isParentOfAsync("super-admin", "post-edit");
Example:
// Get all scopes for user
const allScopes = await Roles.getScopesForUserAsync(userId);
// Get scopes where user has specific roles
const editorScopes = await Roles.getScopesForUserAsync(userId, ["editor"]);
Role assignments need to be published to be available on the client. Example publication:
// Publish user's own roles
Meteor.publish(null, function () {
if (this.userId) {
return Meteor.roleAssignment.find({ "user._id": this.userId });
}
this.ready();
});
// Publish roles for specific scope
Meteor.publish("scopeRoles", function (scope) {
if (this.userId) {
return Meteor.roleAssignment.find({ scope: scope });
}
this.ready();
});
On the client alongside the async methods, you can use the sync versions of the functions:
Roles.userIsInRole(userId, roles, scope)Roles.getRolesForUser(userId, scope)Roles.getScopesForUser(userId)Roles.isParentOf(parent, child)Roles.getUsersInRole(role, scope)Roles.getAllRoles(options)Roles.createRole(role, options)Roles.addUsersToRoles(userId, roles, scope)Roles.setUserRoles(userId, roles, scope)Roles.removeUsersFromRoles(userId, roles, scope)Roles.addRolesToParent(child, parent)Roles.removeRolesFromParent(child, parent)Roles.deleteRole(role)Roles.renameRole(oldRole, newRole)Roles.renameScope(oldScope, newScope)Roles.removeScope(scope)The roles package automatically provides an isInRole helper for templates:
{{#if isInRole "admin"}}
<div class="admin-panel">
<!-- Admin only content -->
</div>
{{/if}}
{{#if isInRole "editor,writer" "blog"}}
<div class="editor-tools">
<!-- Blog editor tools -->
</div>
{{/if}}
If you are currently using the alanning:roles package, follow these steps to migrate to the core version:
alanning:roles firstalanning:roles package:
meteor remove alanning:roles
meteor add roles
import { Roles } from "meteor/roles";
The sync versions of these functions are still available on the client.
// server/methods.js
Meteor.methods({
deletePost: async function (postId) {
check(postId, String);
const canDelete = await Roles.userIsInRoleAsync(
this.userId,
["admin", "moderator"],
"posts"
);
if (!canDelete) {
throw new Meteor.Error("unauthorized", "Not authorized to delete posts");
}
Posts.remove(postId);
},
});
// server/publications.js
Meteor.publish("secretDocuments", async function (scope) {
check(scope, String);
const canView = await Roles.userIsInRoleAsync(
this.userId,
["view-secrets", "admin"],
scope
);
if (canView) {
return SecretDocs.find({ scope: scope });
}
this.ready();
});
// server/users.js
Meteor.methods({
promoteToEditor: async function (userId, scope) {
check(userId, String);
check(scope, String);
const canPromote = await Roles.userIsInRoleAsync(
this.userId,
"admin",
scope
);
if (!canPromote) {
throw new Meteor.Error("unauthorized");
}
await Roles.addUsersToRolesAsync(userId, ["editor"], scope);
},
});