@o3co/auth.policy-verifier.builtins
v0.3.1
Published
Built-in attribute collectors, rule collectors, and resource parser for auth.policy-verifier.
Readme
@o3co/auth.policy-verifier.builtins
Built-in attribute collectors, rule collectors, and resource parser for auth.policy-verifier.
Runtime: Node.js 22+. This package uses node:crypto for stable rule-type hashing; browser / edge runtime support is tracked as future work.
Install
npm install @o3co/auth.policy-verifier.builtinsAttribute Collectors
All collectors implement AttributeCollector.
| Name | Reads from | Emits | Constructor args |
| --- | --- | --- | --- |
| PayloadScopeCollector | payload.scope (space-separated string) | ATTR_SCOPES: string[] | none |
| PayloadSubjectIdCollector | payload.sub, payload.azp | ATTR_USER_ID, ATTR_CLIENT_ID | none |
| StaticPermissionCollector | — | ATTR_PERMISSIONS: string[] | { permissions: string[] } |
| StaticRoleCollector | — | ATTR_ROLES: Role[] | { roles: Role[] } |
StaticPermissionCollector and StaticRoleCollector always emit the values supplied at construction time, regardless of request context.
No built-in collector for requestContext
The engine does not ship a collector that expands CollectorContext.requestContext into attributes. The shape of requestContext is defined by each consuming project's transport/interceptor, so interpreting it is a project concern. Write a focused AttributeCollector for each field you need to promote — validate its shape there, and store it under a project-specific constant key. See AGENTS.md — Core Vocabulary Scope for the rationale and a worked example.
Rules
HasPermission
new HasPermission(permission: string)ruleType:"permission",code:"no_permission"- Checks
ATTR_PERMISSIONS(direct) andATTR_ROLES[].permissions(via roles). - Wildcard matching (case-insensitive):
"*"matches any permission."foo*"matches any permission with prefixfoo."*bar"matches any permission with suffixbar."foo*bar"matches any permission starting withfooand ending withbar.
HasScope
new HasScope(scope: string)ruleType:"scope",code:"invalid_scope"- Checks
ATTR_SCOPES. - A bare scope string
"resource"inATTR_SCOPESis normalized to"read:resource"before comparison. - Matching is case-insensitive exact match after normalization.
AttrMatchRule
Deprecated. Use AttrPairEqual instead. AttrMatchRule is kept as a thin wrapper class that extends AttrPairEqual and preserves the legacy ruleType (attr_match:${a}:${b}) and legacy message wording for backward compatibility. The type AttrMatchRuleConfig is a type alias of AttrPairEqualConfig. It will be removed in a future major version.
new AttrMatchRule({ a: string, b: string, group?: string })code:"attr_mismatch".- Passes when
attrs.get(a)andattrs.get(b)are both non-empty strings and equal. Any other case returnsfalse(fail closed). - Pure predicate — does not read
CollectorContext. Consuming projects provide the two values to compare through upstreamAttributeCollectors and wire the rule through their ownRuleCollector. ruleTypedefaults to"attr_match:${a}:${b}". The evaluator ORs rules within aruleTypeand ANDs across differentruleTypes, so the default ensures two independent comparisons are AND-combined (required together). Passgroupexplicitly when you want two comparisons to be OR-combined (for example, "identify by DID or by email") — both rules then share the providedgroupas theirruleType.
Attribute Comparison Rules
The attribute comparison rules form a 2 × 5 matrix over two axes: family (Literal vs. Pair) and operator (Equal, NotEqual, In, NotIn, Compare).
- Literal rules compare a single named attribute against a static value (or set of values) supplied at construction time.
- Pair rules compare two named attributes resolved from the
Attributesmap at evaluation time. In/NotInvariants exist for the Literal family only. A pair-over-set operation does not generalize cleanly to a finite list, soAttrPairIn/AttrPairNotInare intentionally absent.
| Family | Equal | NotEqual | In | NotIn | Compare |
| ------- | ------------------ | --------------------- | --------------- | ------------------ | -------------------- |
| Literal | AttrLiteralEqual | AttrLiteralNotEqual | AttrLiteralIn | AttrLiteralNotIn | AttrLiteralCompare |
| Pair | AttrPairEqual | AttrPairNotEqual | — | — | AttrPairCompare |
AttrLiteralEqual
new AttrLiteralEqual({ a: string, v: string | number | boolean, group?: string })code:"attr_not_equal".- Default
ruleType:`attr_literal_equal:${a}:${typeof v}:${String(v)}`. Thetypeof vsegment prevents silent collisions between distinct-type literals that stringify the same way (e.g.truevs"true"). - Passes when
attrs.get(a)is the same type and strictly equal tov. No type coercion.
AttrLiteralNotEqual
new AttrLiteralNotEqual({ a: string, v: string | number | boolean, group?: string })code:"attr_equal".- Default
ruleType:`attr_literal_not_equal:${a}:${typeof v}:${String(v)}`. Thetypeof vsegment prevents silent collisions between distinct-type literals (same rationale asAttrLiteralEqual). - Passes when
attrs.get(a)is the same type asvand strictly not equal to it. Missing or wrong-type attributes returnfalse(safe-deny).
AttrLiteralIn
new AttrLiteralIn({ a: string, values: (string | number | boolean)[], group?: string })code:"attr_not_in_set".- Default
ruleType:`attr_literal_in:${a}:${type}:${count}:${hashPrefix}`— wherecountis the post-deduplication element count andhashPrefixis the first 8 hex characters of a SHA-256 over the deduplicated, sorted, stringified values. Two instances with the sameaand logically equivalentvalues(duplicates and order do not matter) share the sameruleTypeand are OR-combined by the evaluator. valuesmust be a non-empty, homogeneous array (string[],number[], orboolean[]). Passes whenattrs.get(a)is in the set. Duplicate elements invaluesare ignored (the rule usesSetsemantics internally).
AttrLiteralNotIn
new AttrLiteralNotIn({ a: string, values: (string | number | boolean)[], group?: string })code:"attr_in_set".- Default
ruleType:`attr_literal_not_in:${a}:${type}:${count}:${hashPrefix}`— same stable, deduplication-aware hash scheme asAttrLiteralIn. valuesmust be a non-empty, homogeneous array. Passes whenattrs.get(a)is NOT in the set. Duplicate elements invaluesare ignored.
AttrLiteralCompare
new AttrLiteralCompare({ a: string, op: "lt" | "le" | "gt" | "ge", v: number, group?: string })code:"attr_compare_violated".- Default
ruleType:`attr_literal_compare:${a}:${op}:${String(v)}`. - Passes when
attrs.get(a)is a number satisfyinga op v. NaN asvis rejected at construction time. NaN attributes always returnfalse.
AttrPairEqual
new AttrPairEqual({ a: string, b: string, group?: string })code:"attr_mismatch".- Default
ruleType:`attr_pair_equal:${a}:${b}`. - Passes when both
attrs.get(a)andattrs.get(b)are non-empty strings and strictly equal. This is the successor to the deprecatedAttrMatchRule.
AttrPairNotEqual
new AttrPairNotEqual({ a: string, b: string, group?: string })code:"attr_match".- Default
ruleType:`attr_pair_not_equal:${a}:${b}`. - Passes when both
attrs.get(a)andattrs.get(b)are non-empty strings and strictly not equal. Missing, empty, or non-string attributes returnfalse(safe-deny).
AttrPairCompare
new AttrPairCompare({ a: string, op: "lt" | "le" | "gt" | "ge", b: string, group?: string })code:"attr_compare_violated".- Default
ruleType:`attr_pair_compare:${a}:${op}:${b}`. - Passes when both
attrs.get(a)andattrs.get(b)are numbers satisfyinga op b. NaN on either side returnsfalse(JS comparison semantics).
Grouping: AND by default, group for OR
All attribute comparison rules follow the same grouping semantics described for AttrMatchRule above. By default, each rule's ruleType is derived from its distinguishing parameters so that distinct requirements are AND-combined by the evaluator. Pass the same group string to two rules to give them the same ruleType — the evaluator then OR-combines them (either condition satisfies the requirement).
Rule Collectors
| Name | Derives permission/scope | Returns |
| --- | --- | --- |
| ResourceActionPermissionRuleCollector | "<resource.raw>.perm:<action>" | [HasPermission(...)] |
| ResourceActionScopeRuleCollector | "<action>:<resource.resourceType>" | [HasScope(...)] |
Both collectors take no constructor arguments.
Resource Parser
DotNotationResourceParser
Parses a dot-notation string into a Resource.
new DotNotationResourceParser()Input format: "<type>[:<id>].<type>[:<id>]..."
Example: "foo.bar:123" → { raw: "foo.bar:123", resourceType: "foo_bar", resourceId: "123" }
- Segments are split by
.. Each segment may include:id. resourceTypeis the segment types joined with_.resourceIdis the id of the last segment, if present.
builtinCollectorsModule
builtinCollectorsModule is a Module (name: "builtin-collectors") that registers all built-in implementations into their respective registries.
import { builtinCollectorsModule } from "@o3co/auth.policy-verifier.builtins";| Registry | Name | Factory |
| --- | --- | --- |
| attributeCollector | "PayloadScopeCollector" | () => new PayloadScopeCollector() |
| attributeCollector | "PayloadSubjectIdCollector" | () => new PayloadSubjectIdCollector() |
| attributeCollector | "StaticPermissionCollector" | (config) => new StaticPermissionCollector(config) |
| attributeCollector | "StaticRoleCollector" | (config) => new StaticRoleCollector(config) |
| ruleCollector | "ResourceActionScopeRuleCollector" | () => new ResourceActionScopeRuleCollector() |
| ruleCollector | "ResourceActionPermissionRuleCollector" | () => new ResourceActionPermissionRuleCollector() |
| resourceParser | "DotNotationResourceParser" | () => new DotNotationResourceParser() |
See Also
- Extension guide (
docs/extending.md) — how to write customRuleandAttributeCollectorimplementations; positioning ofbuiltinsas a basic set @o3co/auth.policy-verifier.core— core interfaces and attribute constants- auth.policy-verifier root README — full setup and configuration reference
