Skip to content

RBAC Model ​

Grant uses multi-tenant RBAC: roles, groups, and permissions can be combined flexibly. The classic path is User → Role → Group → Permission → Resource, but Grant also supports direct attachments — groups and permissions on users, and permissions on roles — and unions all sources when checking access.

Overview ​

Account-level: Personal Account Owner, Organization Account Owner (full control over that account).

Organization-level: Organization Owner, Admin, Dev, Viewer — full control down to read-only within the organization.

Scope of this doc

This page covers authorization (which actions need which permissions). These need only authentication (logged in), not authorization: login/register, session management, password reset, email verification, profile picture upload, session revocation, auth method management (create/delete/set-primary/change-password).

Resources and Actions ​

Actions that require authorization: read (single resource by ID), query (list/collection). Some resources expose only query (e.g. Project, Role, Tag, API Key); others support both read and query.

Which resources have read vs query only
  • Both read + query: User, Account, Organization, Organization Member, Organization Invitation, Project User, User Session, User Authentication Method
  • Query only: Project, Project App, Resource, Role, Group, Permission, Tag, API Key (list with optional ids filter)

Core Resources ​

ResourceActionsDescription
Usercreate read update delete query export-dataPlatform users
Accountread delete queryPersonal or organization workspace
Organizationcreate read update delete query upload-pictureContainer for projects and members
Projectcreate update delete queryIsolated environment (query only)
Resourcecreate update delete queryDomain entities (query only)
Rolecreate update delete queryNamed permission sets (query only)
Groupcreate update delete queryRole–resource permission bundles
Permissioncreate update delete queryAction on a resource
Tagcreate update delete queryLabels (query only)
API Keycreate delete query revoke exchangeProgrammatic credentials
Appcreate update query deleteConsent-flow applications

Relationship & Session Resources ​

ResourceActionsDescription
Organization Memberread update remove queryUsers in an organization
Organization Invitationcreate read query revoke resend-email renewJoin-org invitations
Project Userread query set-email set-password export-credentialsUsers in a project
User Sessionread queryActive sessions (own only)
User Authentication Methodread queryAuth methods (own only)

Roles ​

Account-Level ​

Two account types: personal (one per user) and organization (owned by users). Each has a single role: Personal Account Owner or Organization Account Owner — full control over that account. Account ownership is independent of organization membership (you can be account owner and also Org Admin/Dev/Viewer elsewhere).

Organization-Level ​

Within an organization, four roles: Owner (full control), Admin (teams and permissions, no account ownership), Dev (create/manage resources, roles, permissions), Viewer (read-only).

Project-Level: Two Kinds of Users ​

  • Platform users — People with a Grant account (personal or org member). They use the dashboard to design auth. Their project access is inherited: account owner → Owner on account projects; org role (Owner/Admin/Dev/Viewer) applies to org projects. The platform does not define project-level groups for them.
  • Project users — Third-party identities in a project (Project User resource). They use the project's APIs/apps (API keys, OAuth), not the dashboard. Platform users define custom roles, groups, and permissions inside the project for these users; the platform supplies the primitives but not a fixed structure.

Groups ​

Groups bundle permissions (often named {Resource} {Role}, e.g. "Organization Owner", "User Dev"). Roles typically receive groups via role_groups, but that is not the only path: users may be assigned groups directly via user_groups (and project_user_groups in project scope), and both users and roles may receive permissions directly via user_permissions / role_permissions (plus the project-scoped variants).

Permission sources (unioned at check time):

User → Role → Group → Permission → Resource
User → Group → Permission → Resource
User → Role → Permission → Resource
User → Permission → Resource

For CDM import/export field mapping, see CDM Import & Export → Permission assignment paths.

Standard account- and organization-level groups (reference)

Account-level: Only Owner groups — Personal Account Owner, Organization Account Owner (Role: Owner, Resource: Account). Organization-level: All four — Organization Owner, Organization Admin, Organization Dev, Organization Viewer. Project-level: No standard groups; platform users' project access is inherited from org role; custom project groups are user-defined.

Permission Mapping to Groups ​

Common groups — assigned to all roles (basic read/query, some create). Role-specific groups — extra permissions for Owner, Admin, Dev, or Viewer. Summary: Tags are Owner-only; API Key delete/revoke for Dev is own-keys only (condition). Expand below for full reference.

Full group → permission reference

Common Groups (all roles) ​

Group NameResourcePermissionsDescription
Account CommonAccountread queryBasic account access for all authenticated users
User CommonUserread queryBasic user viewing for all authenticated users
Organization CommonOrganizationcreate queryBasic organization access (can create and query organizations)
Project CommonProjectqueryBasic project viewing for all authenticated users
Account Project OwnerProjectcreate update delete set-exportable-api-key-secretsProject management for account owners
Resource CommonResourcequeryBasic resource viewing for all authenticated users
Role CommonRolequeryBasic role viewing for all authenticated users
Group CommonGroupqueryBasic group viewing for all authenticated users
Permission CommonPermissionqueryBasic permission viewing for all authenticated users
Tag CommonTagqueryBasic tag viewing for all authenticated users
API Key CommonAPI Keyquery exchangeBasic API key access for all authenticated users
Organization Member CommonOrganization Memberread queryBasic organization member viewing for all authenticated users
Organization Invitation CommonOrganization InvitationqueryBasic organization invitation access for all authenticated users
Project User CommonProject Userread queryBasic project user viewing for all authenticated users
User Session CommonUser Sessionread queryBasic user session viewing (own sessions only, enforced by condition)
User Authentication Method CommonUser Authentication Methodread queryBasic authentication method viewing (own methods only, enforced by condition)

Role-Specific Groups ​

These groups contain permissions specific to certain roles:

Account Groups ​

Group NameResourcePermissionsAssigned ToDescription
Personal Account OwnerAccountdeletePersonal Account OwnerFull personal account control
Organization Account OwnerAccountdeleteOrganization Account OwnerFull organization account control

User Groups ​

Group NameResourcePermissionsAssigned ToDescription
User OwnerUsercreate update delete export-dataOrganization OwnerFull user management
User AdminUsercreate update delete export-dataOrganization AdminFull user management
User DevUserupdate (own user only)Organization DevLimited user management (own profile only)
User ViewerUser(no additional permissions)—Viewer has same user access as common groups

Organization Groups ​

Group NameResourcePermissionsAssigned ToDescription
Organization OwnerOrganizationupdate deleteOrganization OwnerFull organization management
Organization AdminOrganizationupdate deleteOrganization AdminFull organization management
Organization DevOrganization(no additional permissions)—Dev has same organization access as common groups
Organization ViewerOrganization(no additional permissions)—Viewer has same organization access as common groups

Project Resource Groups ​

These groups control what organization-level roles can do with the Project resource (create, update, delete projects). They are not project-scope groups — see Project-Level: Two Kinds of Users.

Group NameResourcePermissionsAssigned ToDescription
Project OwnerProjectcreate update delete set-exportable-api-key-secretsOrganization OwnerFull project management
Project AdminProjectcreate update delete set-exportable-api-key-secretsOrganization AdminFull project management
Project DevProjectcreate update deleteOrganization DevFull project management
Project ViewerProject(no additional permissions)—Viewer has same project access as common groups

Resource Groups ​

Group NameResourcePermissionsAssigned ToDescription
Resource OwnerResourcecreate update deleteOrganization OwnerFull resource management
Resource AdminResourcecreate update deleteOrganization AdminFull resource management
Resource DevResourcecreate update deleteOrganization DevFull resource management
Resource ViewerResource(no additional permissions)—Viewer has same resource access as common groups

Role Groups ​

Group NameResourcePermissionsAssigned ToDescription
Role OwnerRolecreate update deleteOrganization OwnerFull role management
Role AdminRolecreate update deleteOrganization AdminFull role management
Role DevRolecreate update deleteOrganization DevFull role management
Role ViewerRole(no additional permissions)—Viewer has same role access as common groups

Group Groups ​

Group NameResourcePermissionsAssigned ToDescription
Group OwnerGroupcreate update deleteOrganization OwnerFull group management
Group AdminGroupcreate update deleteOrganization AdminFull group management
Group DevGroupcreate update deleteOrganization DevFull group management
Group ViewerGroup(no additional permissions)—Viewer has same group access as common groups

Permission Groups ​

Group NameResourcePermissionsAssigned ToDescription
Permission OwnerPermissioncreate update deleteOrganization OwnerFull permission management
Permission AdminPermissioncreate update deleteOrganization AdminFull permission management
Permission DevPermissioncreate update deleteOrganization DevFull permission management
Permission ViewerPermission(no additional permissions)—Viewer has same permission access as common groups

Tag Groups ​

Group NameResourcePermissionsAssigned ToDescription
Tag OwnerTagcreate update deleteOrganization OwnerFull tag management (Owner only)
Tag AdminTag(no additional permissions)—Admin cannot manage tags
Tag DevTag(no additional permissions)—Dev has same tag access as common groups
Tag ViewerTag(no additional permissions)—Viewer has same tag access as common groups

Note: Only Organization Owner can manage tags. Admin, Dev, and Viewer roles have read-only access via common groups.

API Key Groups ​

Group NameResourcePermissionsAssigned ToDescription
API Key OwnerAPI Keycreate update delete revokeOrganization OwnerFull API key management
API Key AdminAPI Keycreate update delete revokeOrganization AdminFull API key management
API Key DevAPI Keycreate update delete* revoke*Organization DevAPI key creation; delete/revoke own keys only
API Key ViewerAPI Key(no additional permissions)—Viewer has same API key access as common groups

INFO

Dev can only delete/revoke their own API keys (enforced by condition) *

ApiKey:Update is a permission of the ApiKey resource and of the three groups above (the account-project group already listed it). It gates setApiKeyPermissions and PUT /api/api-keys/{id}/permissions, which replace the permissions a project key carries (see API keys).

API keys are attenuated ​

A project API key (an accountProject or organizationProject scope) never carries more than its creator can do in the key's project scope. When a key is created, everything its role resolves to there (the role's groups and the role's direct permissions for that project) must be among the permissions its creator holds in the same scope. Equality is allowed: an Owner may create a key with the Owner role. The creator's permissions are collected the way a request is authorized (GrantService.collectPermissionIds) with the creator's own token type, so a key that creates a key is bound by its own permissions.

The same check decides who may rotate, revoke or delete a key: the caller must cover the key's permissions. A key with neither a role nor permissions is not manageable by anyone. The rule is the same for account and organization projects. A role key carries only a standard role (a standard organization role for an organization project, a standard account role for an account project); any other role is refused. Permissions are compared by row, so a row that carries a condition is covered only by the same row.

A machine-only permission may be carried by a key whose creator does not hold it, when the creator holds the permission its template names as the gate (attachableByHoldersOf). ProjectUser:ExportCredentials is the first: its gate is ProjectUser:SetPassword, held by the Owner, the Admin and the account owners and not by the Dev or the Viewer. The flag describes who may attach the permission to a key; it does not change who is authorized to use it.

A project key carries either a standard role or a set of permissions of its own (CreateApiKeyInput.permissionIds). A permission key is authorized, in its own project scope, by exactly those permissions: GrantService.collectPermissionIds adds the key's rows (read through the key's link to the project, so they resolve nowhere else) for an API-key token in an accountProject or organizationProject scope, and nothing from a role. Editing a key's permissions applies the same rule to the change: the editor must cover the key as it is, every permission the edit adds must be among the editor's own (a machine-only one through its gate), and removing is always allowed. Converting a role key to permissions is bounded by the editor's own permissions, not by the old role's. apiKeyAttachableGrants returns what the caller may attach. An edit drops the key's cached authorization results, so it applies to the key's next request, including through a token issued before the edit.

Member role changes and invitations are not API keys and keep the role-level comparison.

Project App Groups ​

Project App (Apps) are OAuth/consent applications scoped to a project. Account owners get full app management via Account Project App Owner; organization roles get app management via Project App Owner/Admin/Dev. There is no Project App Common group — only these role-specific groups grant access.

Group NameResourcePermissionsAssigned ToDescription
Account Project App OwnerProject Appcreate update delete queryPersonal/Organization Account OwnerFull app management for account projects
Project App OwnerProject Appcreate update delete queryOrganization OwnerFull project app management
Project App AdminProject Appcreate update delete queryOrganization AdminFull project app management
Project App DevProject Appcreate update delete queryOrganization DevFull project app management
Project App ViewerProject App(no additional permissions)—Viewer has no project app access by default

Organization Member Groups ​

Group NameResourcePermissionsAssigned ToDescription
Organization Member OwnerOrganization Memberupdate removeOrganization OwnerFull organization member management
Organization Member AdminOrganization Memberupdate removeOrganization AdminFull organization member management
Organization Member DevOrganization Member(no additional permissions)—Dev has same organization member access as common groups
Organization Member ViewerOrganization Member(no additional permissions)—Viewer has same organization member access as common groups

Organization Invitation Groups ​

Group NameResourcePermissionsAssigned ToDescription
Organization Invitation OwnerOrganization Invitationcreate revoke resend-email renewOrganization OwnerFull organization invitation management
Organization Invitation AdminOrganization Invitationcreate revoke resend-email renewOrganization AdminFull organization invitation management
Organization Invitation DevOrganization Invitation(no additional permissions)—Dev has same organization invitation access as common groups
Organization Invitation ViewerOrganization Invitation(no additional permissions)—Viewer has same organization invitation access as common groups

Project User Groups ​

Group NameResourcePermissionsAssigned ToDescription
Project User OwnerProject Userset-email set-passwordOrganization Owner, Personal Account Owner, Organization Account OwnerSet a project user's Email authentication method and password
Project User AdminProject Userset-email set-passwordOrganization AdminSet a project user's Email authentication method and password
Project User DevProject User(no additional permissions)—Dev has same project user access as common groups
Project User ViewerProject User(no additional permissions)—Viewer has same project user access as common groups

ProjectUser:SetEmail stores the address unverified (isVerified: false), whether it adds the Email method or replaces the current one: an operator can type an address, not prove it belongs to the user. Only the platform (dashboard) sign-in enforces the flag. There the user can sign in with the address, but is held to the verification window and blocked from organization-context mutations until the address is verified. Project app sign-in and the project password-reset flow do not read it, so an operator-set address works there at once, and completing a project password reset (or an e-mail magic link) marks the method verified. The user clears the state with the dashboard's resend-verification action, which mails a code to the address; whoever cannot read that mailbox cannot clear it. The operation is recorded on the authentication-method audit log with metadata.operation set to operator-set-email (or operator-create-user when createUser supplied the address), and is rate-limited per operator and scope (SECURITY_RATE_LIMIT_SET_EMAIL_*, see Security).

ProjectUser:SetPassword is an ordinary extensible action: it is held by Owner and Admin by default, and an organization can add it to any other role's group exactly like SetEmail, with no code change.

Project:SetExportableApiKeySecrets follows the same precedent. It is the per-project opt-in for exportable API-key secrets (setProjectExportableApiKeySecrets, PUT /api/projects/{id}/exportable-api-key-secrets), and it is held by Owner and Admin only: the Project Owner and Project Admin groups (Organization Owner, Organization Admin) and the Account Project Owner group (Personal Account Owner, Organization Account Owner). The Project Dev group does not hold it, although it holds Project:Update, because the flag decides whether Grant keeps a credential in a reversible form. Like Project:Update, it applies to the project in the authorized project scope. An organization can add it to any other role's group, with no code change.

ProjectUser:ExportCredentials has no default holder. It reads project users' password hashes, so it belongs to no standard group and is attached deliberately to a mirror's API key as a machine-only permission (by a holder of ProjectUser:SetPassword, see API keys are attenuated), or granted to a group. It is seeded as a standalone permission (a permission row linked to no group), so the groups UI and API can grant it.

It gates two things. The exportProjectUserCredentials GraphQL query and POST /api/users/credentials/export return, for the requested project members, { algorithm: 'bcrypt', hash } (kind PASSWORD; the one place a project password hash leaves Grant) and the plaintext secrets of their API keys (kind API_KEY_SECRET; the one place an API-key secret can be read back, and only for keys of a project that opted in to exportable secrets, see ADR 0013). Both kinds sit behind this one permission, because a later credential type is a new kind and not a second export. Every successful call writes one export row on the project credential audit log (the caller, the number of users and credentials, the kinds; never a hash or a secret) and one export row per exported API-key secret on the API-key audit log (the key; never the secret), and a request naming a user who is not a member of the project fails as a whole. The User.passwordCredential field, resolved in the request's project scope (source, updatedAt and a SHA-256 fingerprint of the stored hash, a change detector rather than a secret), resolves to null, not an error, for any caller without the permission. ProjectUser:ExportCredentials also gates User.apiKeyCredentials, resolved the same way in the request's project scope: per project-user API key, its fingerprint (the SHA-256 of the secret string as issued, null for a key created before it existed) and whether an encrypted exportable copy exists, never a secret or a hash.

New standard permissions such as ProjectUser:SetEmail, ProjectUser:SetPassword and Project:SetExportableApiKeySecrets reach organizations bootstrapped earlier through seedAll (instance-wide, idempotent). bootstrapDatabase() runs that seed when DB_BOOTSTRAP_ON_BOOT is true (ADR 0001, the default); serverless deployments must run the migrate/seed step together with the flag.

User Session Groups ​

All roles have only common group permissions for User Session (read, query own sessions):

Group NameResourcePermissionsAssigned ToDescription
User Session OwnerUser Session(no additional permissions)—Owner has same user session access as common groups (own sessions)
User Session AdminUser Session(no additional permissions)—Admin has same user session access as common groups (own sessions)
User Session DevUser Session(no additional permissions)—Dev has same user session access as common groups (own sessions)
User Session ViewerUser Session(no additional permissions)—Viewer has same user session access as common groups (own sessions)

User Authentication Method Groups ​

All roles have only common group permissions for User Authentication Method (read, query own methods):

Group NameResourcePermissionsAssigned ToDescription
User Authentication Method OwnerUser Authentication Method(no additional permissions)—Owner has same auth method access as common groups (own methods)
User Authentication Method AdminUser Authentication Method(no additional permissions)—Admin has same auth method access as common groups (own methods)
User Authentication Method DevUser Authentication Method(no additional permissions)—Dev has same auth method access as common groups (own methods)
User Authentication Method ViewerUser Authentication Method(no additional permissions)—Viewer has same auth method access as common groups (own methods)

The dedicated User Authentication Method resource is own-methods-only. Nested User.authenticationMethods on GraphQL users and REST GET /users is different: those lists authorize User:Query only, with no extra UserAuthenticationMethod:Read check. This is User:Query, not ProjectUserCommon. An OrganizationProject or AccountProject API key whose role includes User:Query (Owner and Viewer both have it via User Common) can therefore request authenticationMethods { provider providerId isVerified } for another member and receive the Email mailbox, including an operator-set unverified address. A ProjectUser-tenant key sees that nested field only when the bound user holds User:Query and the list is requested on a users-list tenant (projectUser itself is not one).

:::

Permission Mapping to Roles ​

Action × role matrices: Owner/Admin/Dev can do most writes; Viewer is read-only. Exceptions: Tags — Owner only; User update and API Key delete/revoke — Dev only for self (condition).

Full permission matrix (all resources)

Core Resource Permissions ​

User Permissions ​

ActionResourceOrganization OwnerOrganization AdminOrganization DevOrganization Viewer
createuser
readuser
updateuser*
deleteuser
queryuser
export-datauser

INFO

Dev can only update their own user record *

Account Permissions ​

ActionResourcePersonal Account OwnerOrganization Account Owner
readaccount
deleteaccount
queryaccount

Organization Permissions ​

ActionResourceOrganization OwnerOrganization AdminOrganization DevOrganization Viewer
createorganization
updateorganization
deleteorganization
queryorganization

INFO

Organization read is enforced by scope (users can only read organizations they belong to).

Project Permissions ​

ActionResourceOrganization OwnerOrganization AdminOrganization DevOrganization Viewer
createproject
updateproject
deleteproject
queryproject

Resource Permissions ​

ActionResourceOrganization OwnerOrganization AdminOrganization DevOrganization Viewer
createresource
updateresource
deleteresource
queryresource

Role Permissions ​

ActionResourceOrganization OwnerOrganization AdminOrganization DevOrganization Viewer
createrole
updaterole
deleterole
queryrole

Group Permissions ​

ActionResourceOrganization OwnerOrganization AdminOrganization DevOrganization Viewer
creategroup
updategroup
deletegroup
querygroup

Permission Permissions ​

ActionResourceOrganization OwnerOrganization AdminOrganization DevOrganization Viewer
createpermission
updatepermission
deletepermission
querypermission

Tag Permissions ​

ActionResourceOrganization OwnerOrganization AdminOrganization DevOrganization Viewer
createtag
updatetag
deletetag
querytag

Note: Only Organization Owner can manage tags.

API Key Permissions ​

ActionResourceOrganization OwnerOrganization AdminOrganization DevOrganization Viewer
createapi-key
deleteapi-key*
queryapi-key
revokeapi-key*
exchangeapi-key

* Dev can only delete/revoke their own API keys

Relationship Permissions ​

Organization Member Permissions ​

ActionResourceOrganization OwnerOrganization AdminOrganization DevOrganization Viewer
readorganization-member
updateorganization-member
removeorganization-member
queryorganization-member

Organization Invitation Permissions ​

ActionResourceOrganization OwnerOrganization AdminOrganization DevOrganization Viewer
createorganization-invitation
readorganization-invitation
queryorganization-invitation
revokeorganization-invitation
resend-emailorganization-invitation
reneworganization-invitation

Project User Permissions ​

ActionResourceOrganization OwnerOrganization AdminOrganization DevOrganization Viewer
readproject-user
queryproject-user
set-emailproject-user
set-passwordproject-user
export-credentialsproject-user

export-credentials is denied to every standard role by default; an organization grants it explicitly (see Project User Groups above).

Session & Authentication Permissions (Authorization-Required Only) ​

User Session Permissions ​

ActionResourceAll Roles
readuser-session*
queryuser-session*

* Users can only read/query their own sessions (enforced by condition)

User Authentication Method Permissions ​

ActionResourceAll Roles
readuser-authentication-method*
queryuser-authentication-method*

* Users can only read/query their own authentication methods (enforced by condition)

:::

Naming and Scope ​

Permissions are stored as action (kebab-case: create, read, update, delete, query, export-data, remove, revoke, resend-email, renew, exchange) plus resourceId (references the resources table). Scopes: Account (account-level actions), Organization (org and members), Project (project resources). Access is limited to resources in the user's assigned scopes.

Implementation Notes ​

Role hierarchy (conceptual): Owner ⊃ Admin ⊃ Dev ⊃ Viewer; only Tags are Owner-only (Admin has no tag write). Self-management (conditions): Dev can update own user; all users can read own sessions and auth methods; Dev can delete/revoke own API keys. API keys are not bound by this hierarchy but by permission containment. See Permission Conditions for syntax. Auth-only (no RBAC): Login, register, session refresh, password reset, profile picture, session revocation, auth method CRUD — require only authentication.

Permission evaluation ​

Implemented by GrantService.getUserPermissions (API) and evaluated by PermissionChecker (@grantjs/core):

  1. Load the user’s roles in the scope (account / organization / project). Roles are optional — a user with only direct groups or permissions can still be authorized.
  2. Collect groups from those roles (role_groups) and direct user groups (user_groups / project_user_groups).
  3. Resolve permissions from those groups (group_permissions).
  4. Add direct role permissions (role_permissions / project_role_permissions) and direct user permissions (user_permissions / project_user_permissions).
  5. Union all permission IDs, then keep those matching the requested action + resource.
  6. If a matching permission has a condition, evaluate it with the execution context (role/group combinations when present).
  7. Enforce tenant isolation (resource in the user’s scope).

See also the diagram on Architecture Overview → Permission Evaluation.


Related: Data Model · Multi-Tenancy · Security

Released under the MIT License.