Skip to content

Rule Types

Strobel Pierre edited this page Mar 20, 2026 · 1 revision

Rule Types Reference

This page documents all 5 rule types in detail, including their configuration, behavior with different claim value types, and concrete examples.

Common rule structure

Every rule has the same base structure:

{
  "id": "my-rule",
  "type": "direct",
  "enabled": true,
  "claimPath": "department",
  "config": {}
}
Field Type Description
id string Unique identifier for the rule
type string One of: direct, prefix, map, conditional, template
enabled boolean Whether the rule is evaluated during login
claimPath string Dot-notation path to the claim in the token (see Claim path resolution)
config object Type-specific configuration (see below)

Behavior when the claim is missing: If the claim path resolves to null (claim not present in the token), the rule produces no groups and is marked as not matched. This is safe — a missing claim never causes an error.


direct

Uses the claim value directly as a group name.

Config

No configuration needed — pass an empty object {}.

Behavior

Claim value Result
"Engineering" (string) ["Engineering"]
["admin", "editor"] (array of strings) ["admin", "editor"]
"" (empty string) [] (no groups)
["admin", 42, "editor"] (mixed array) ["admin", "editor"] (non-strings filtered out)
null / missing claim [] (no groups)

Example

Token:

{ "department": "Engineering" }

Rule:

{
  "id": "departments",
  "type": "direct",
  "enabled": true,
  "claimPath": "department",
  "config": {}
}

Output: ["Engineering"]

Use case

Best for claims whose values are already valid group names — like department, team, or a groups claim from your IdP.


prefix

Prepends a prefix string to each claim value.

Config

Key Type Description
prefix string The prefix to prepend to each value

Behavior

Claim value Prefix Result
"admin" (string) role_ ["role_admin"]
["admin", "editor"] (array) role_ ["role_admin", "role_editor"]
"" (empty string) role_ [] (empty strings are skipped)
["admin", 42] (mixed array) role_ ["role_admin"] (non-strings skipped)
null / missing claim role_ [] (no groups)

Example

Token:

{ "roles": ["admin", "editor"] }

Rule:

{
  "id": "user-roles",
  "type": "prefix",
  "enabled": true,
  "claimPath": "roles",
  "config": { "prefix": "role_" }
}

Output: ["role_admin", "role_editor"]

Use case

Useful when you want to namespace groups from a specific claim to avoid collisions. For example, IdP roles like admin might conflict with existing Nextcloud groups, so prefixing with role_ keeps them distinct.


map

Maps claim values through a lookup table. Each value is replaced by its mapped counterpart.

Config

Key Type Description
values object Lookup table: { "claimValue": "groupName" } or { "claimValue": ["group1", "group2"] }
unmappedPolicy string What to do when a value isn't in the table: "ignore" (default) or "passthrough"

Behavior

Claim value Map Policy Result
"corp.example.com" { "corp.example.com": "Staff" } ignore ["Staff"]
"unknown.com" { "corp.example.com": "Staff" } ignore [] (value not in map → skipped)
"unknown.com" { "corp.example.com": "Staff" } passthrough ["unknown.com"] (original value kept)
["corp.example.com", "partner.example.com"] { "corp.example.com": "Staff", "partner.example.com": "Partners" } ignore ["Staff", "Partners"]
"corp.example.com" { "corp.example.com": ["Staff", "FullTime"] } ignore ["Staff", "FullTime"] (one-to-many mapping)
null / missing claim any any [] (no groups)

Example

Token:

{ "organization": "corp.example.com" }

Rule:

{
  "id": "org-mapping",
  "type": "map",
  "enabled": true,
  "claimPath": "organization",
  "config": {
    "values": {
      "corp.example.com": "Staff",
      "partner.example.com": "Partners"
    },
    "unmappedPolicy": "ignore"
  }
}

Output: ["Staff"]

Use case

Ideal when IdP claim values don't match your desired Nextcloud group names — for example, mapping domain names to human-readable groups, or mapping opaque role IDs to meaningful names. Also supports one-to-many mapping (one claim value → multiple groups).


conditional

Evaluates a condition against the claim value. If the condition matches, assigns a fixed list of groups.

Config

Key Type Description
operator string One of: equals, contains, regex
value string The value to compare against
groups array Groups to assign if the condition matches

Operators

Operator What it does Claim type
equals Exact string match: claimValue === value string
contains Array contains value: in_array(value, claimValue) array
regex Regex match: preg_match(value, claimValue) string

Behavior

Operator Claim value Config value Matches?
equals "INTERNAL" "INTERNAL" Yes
equals "EXTERNAL" "INTERNAL" No
equals ["INTERNAL"] (array) "INTERNAL" No (equals only works on strings)
contains ["admin", "editor"] "admin" Yes
contains "admin" (string) "admin" No (contains only works on arrays)
regex "jdoe@example.com" "/@example\\.com$/" Yes
regex ["jdoe@example.com"] (array) "/@example\\.com$/" No (regex only works on strings)
any null / missing claim any No

Important: For the regex operator, the pattern must include delimiters (e.g., /.../). Invalid regex patterns are silently treated as non-matching.

Example

Token:

{ "userType": "INTERNAL" }

Rule:

{
  "id": "internal-flag",
  "type": "conditional",
  "enabled": true,
  "claimPath": "userType",
  "config": {
    "operator": "equals",
    "value": "INTERNAL",
    "groups": ["Internal-Users"]
  }
}

Output: ["Internal-Users"]

Use case

Perfect for flag-based logic: "if the user is internal, add them to these groups". Also useful for regex-based matching on email domains, organization patterns, etc.


template

Applies a string template to each claim value. The placeholder {value} is replaced with the actual claim value.

Config

Key Type Description
template string Template string containing {value} placeholder

Behavior

Claim value Template Result
"Engineering" dept_{value} ["dept_Engineering"]
["admin", "editor"] role-{value} ["role-admin", "role-editor"]
"" (empty string) dept_{value} [] (empty strings skipped)
null / missing claim any [] (no groups)

Example

Token:

{ "department": "Engineering" }

Rule:

{
  "id": "dept-template",
  "type": "template",
  "enabled": true,
  "claimPath": "department",
  "config": { "template": "dept_{value}" }
}

Output: ["dept_Engineering"]

Use case

More flexible than prefix — you can place the value anywhere in the string (e.g., team-{value}-members). Use when you need a consistent naming pattern but the prefix alone isn't enough.


Claim path resolution

The claimPath field uses dot-notation to navigate nested token structures.

Simple paths

Path Resolves to
department token.department
roles token.roles

Nested paths

Path Resolves to
extended_attributes.domain token.extended_attributes.domain
extended_attributes.auth.permissions token.extended_attributes.auth.permissions

URL-style claim keys

Some identity providers use URL-style claim names (common with Azure AD and custom OIDC providers):

Path Resolves to
https://idp.example.com/claims/domain token["https://idp.example.com/claims/domain"]

Resolution strategy

The resolver handles ambiguous paths (where dots could be part of a key name or a separator) using a progressive split strategy:

  1. Try the full path as a direct property name (handles URL-style keys with dots)
  2. If not found, try splitting on each dot from left to right, looking for the deepest valid path

This means https://idp.example.com/claims/extended_attributes.auth.permissions correctly resolves the URL-part first, then navigates into the nested object.

Examples with a complex token

Given this token:

{
  "department": "Engineering",
  "https://idp.example.com/claims/domain": "corp.example.com",
  "extended_attributes": {
    "auth": {
      "permissions": ["read", "write"]
    }
  }
}
Claim path Result
department "Engineering"
https://idp.example.com/claims/domain "corp.example.com"
extended_attributes.auth.permissions ["read", "write"]
nonexistent.path null