Skip to content

Conditions and fields

Every constraint on a rule must match for the rule to match.

Facts are rooted at subject, resource, or context. Values can be literals or references to other facts.

{
effect: "allow",
permission: "project:update",
where: {
fact: "resource.ownerId",
operator: "equals",
value: { fact: "subject.id" },
},
}

Compose with all, any, none, and not:

where: {
all: [
{ fact: "resource.status", operator: "oneOf", value: ["draft", "review"] },
{ not: { fact: "subject.attributes.suspended", operator: "equals", value: true } },
],
}
Group Operators
Equality equals, notEquals
Membership oneOf, noneOf, contains, containsAll
Shape exists, sizeEquals
Strings startsWith, endsWith, contains
Comparison lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual

Comparisons require finite values of the same type. Dynamic oneOf, noneOf, and containsAll operands must resolve to non-empty arrays. An empty needle never matches string contains, startsWith, or endsWith.

  • A missing or malformed fact never grants access.
  • An unknown condition never grants an allow, and conservatively matches a deny. A conditional deny fires when its fact is absent.
  • exists is the one operator that tests presence itself, so exists: false is a known match when the fact is absent.

A rule may be limited to field paths on the resource.

{ effect: "allow", permission: "project:update", fields: ["title", "metadata.*"] }

Check a specific field by naming it on the request:

authorization.check({ permission: "project:update", resource: project, field: "metadata.visibility" });

Two asymmetric rules keep this safe:

  • A field-restricted allow cannot authorize a resource-wide request. Granting title does not grant the whole project.
  • A field-restricted deny does match a resource-wide request, because that request may expose the denied field.