Skip to content

Permission Conditions

Permission conditions restrict access beyond the action itself. They are evaluated at authorization time and allow attribute-based rules within the RBAC model — for example, "only if the user owns the resource" or "only if the user's department matches."

Overview

When a permission has a condition, Grant evaluates it after matching the resource and action. The condition receives an execution context (user, roles, groups, and optionally the resolved resource) and returns whether access is granted.

  • Permissions without a condition are granted immediately when the resource and action match.
  • Permissions with a condition must pass the condition evaluation before access is granted.

Syntax Structure

Conditions are JSON objects. The structure is:

json
{ "Operator": { "field.path": value } }

For logical operators, the structure differs:

  • And and Or: { "And": [ condition1, condition2, ... ] } — array of condition expressions
  • Not: { "Not": condition } — single condition expression

Logical Operators

OperatorStructureDescription
And{ "And": [ ... ] }All nested conditions must be true
Or{ "Or": [ ... ] }At least one nested condition must be true
Not{ "Not": { ... } }Negates the nested condition

Examples:

json
{
  "And": [
    { "StringEquals": { "user.metadata.department": "sales" } },
    { "StringEquals": { "user.metadata.region": "us-east" } }
  ]
}
json
{
  "Or": [
    { "Equals": { "user.id": "{{resource.ownerId}}" } },
    { "In": { "user.metadata.adminIds": ["{{resource.organizationId}}"] } }
  ]
}
json
{
  "Not": {
    "StringEquals": { "user.metadata.status": "suspended" }
  }
}

Comparison Operators

OperatorLeft operandRight operandDescription
EqualsanyanyEquality (both sides coerced to string)
StringEqualsanyanyString equality
NotEqualsanyanyInequality
StringNotEqualsanyanyString inequality
InanyarrayLeft value is in the right array
StringInanyarrayLeft value (as string) is in the right array
NotInanyarrayLeft value is not in the right array
StringNotInanyarrayLeft value (as string) is not in the right array
ContainsarrayanyRight value is contained in the left array
StartsWithstringstringLeft string starts with right string
EndsWithstringstringLeft string ends with right string
NumericEqualsnumbernumberNumeric equality
NumericGreaterThannumbernumberLeft > right
NumericLessThannumbernumberLeft < right
NumericGreaterThanEqualsnumbernumberLeft >= right
NumericLessThanEqualsnumbernumberLeft <= right

Examples:

json
{ "StringEquals": { "user.metadata.department": "engineering" } }
json
{ "In": { "user.metadata.policies": ["POLICY-1", "POLICY-2"] } }
json
{ "NumericGreaterThan": { "resource.amount": 500 } }

Field Paths

Field paths reference values in the execution context. Dot notation is used for nested properties.

Path prefixSourceDescription
user.idExecution contextThe requesting user's ID
user.metadata.*User entityUser metadata (e.g. user.metadata.department)
role.idRole (when evaluating)Role ID
role.nameRoleRole name
role.metadata.*Role entityRole metadata
group.idGroup (when evaluating)Group ID
group.nameGroupGroup name
group.metadata.*Group entityGroup metadata
resource.*Resource resolverResolved resource data from your application (e.g. resource.ownerId, resource.amount)

TIP

The resource prefix refers to resolved resource data. When using @grantjs/server with a resourceResolver, the returned object is available under resource.*. If no resource is resolved, these paths evaluate to undefined.

Field References

Values can be static or dynamic. Dynamic values reference other fields in the context:

Template syntax: {{path}} — References a field path (e.g. {{user.id}}, {{resource.ownerId}})

Object syntax: { "$ref": "path" } — Same as template, useful when the value is not a string

Examples:

json
{
  "StringEquals": {
    "resource.ownerId": "&#123;&#123;user.id&#125;&#125;"
  }
}
json
{
  "Equals": {
    "resource.department": { "$ref": "user.metadata.department" }
  }
}

Arrays can contain references; they are resolved and flattened:

json
{
  "In": {
    "resource.id": ["&#123;&#123;user.metadata.policies&#125;&#125;"]
  }
}

Use Cases

Ownership (user owns the resource)

Only allow access when the resource's owner matches the current user:

json
{
  "StringEquals": {
    "resource.ownerId": "&#123;&#123;user.id&#125;&#125;"
  }
}

Requires a resource resolver that returns { ownerId: ... } for the resource.

Department or region checks

Restrict to users in a specific department:

json
{
  "StringEquals": {
    "user.metadata.department": "sales"
  }
}

Or allow multiple regions:

json
{
  "StringIn": {
    "user.metadata.region": ["us-east", "us-west"]
  }
}

Own sessions / authentication methods

Grant's built-in "own sessions only" and "own auth methods only" patterns use conditions comparing user.id with the session/method owner. The resource resolver provides the ownership data.

API key: delete own keys only

Allow Dev role to delete/revoke only API keys they created:

json
{
  "StringEquals": {
    "resource.createdBy": "&#123;&#123;user.id&#125;&#125;"
  }
}

Complex logic (And + Or)

User must be in sales and (in us-east or us-west):

json
{
  "And": [
    { "StringEquals": { "user.metadata.department": "sales" } },
    {
      "Or": [
        { "StringEquals": { "user.metadata.region": "us-east" } },
        { "StringEquals": { "user.metadata.region": "us-west" } }
      ]
    }
  ]
}

Validation

Invalid conditions are rejected when creating or updating permissions. Common errors:

  • And and Or must have an array of condition expressions
  • Not must have a single condition expression (object), not an array
  • Comparison operators expect an object of field paths to values
  • Field paths must reference valid context properties

Released under the MIT License.