A Nextcloud app that maps multiple OIDC token claims to Nextcloud groups via configurable rules. Works with any identity provider through the user_oidc app.
- Vue admin UI — visual rule editor with 3 tabs (Visual Editor, JSON, Simulator)
- Drag-and-drop reorder — reorder rules visually in the admin panel
- 5 rule types — direct, prefix, map, conditional, template
- Dot-notation claim paths — access any nested token field
- Additive or replace mode — merge with or override existing groups
- REST API — manage rules programmatically via OCS endpoints
- OCC commands — list, set, and test rules from the CLI
- Dark mode — full support for Nextcloud dark and light themes
- Zero config on user_oidc — works with any provider setup
- The problem
- Quick start
- Rule types
- Configuration
- Configuring user_oidc
- Admin settings
- REST API
- OCC commands
- How it works
- Installation
- Development
- Troubleshooting
- Contributing
Your identity provider sends a JWT token like this:
{
"sub": "jdoe",
"email": "jdoe@example.com",
"department": "Engineering",
"roles": ["admin", "editor"],
"organization": "corp.example.com",
"userType": "INTERNAL"
}With user_oidc alone, you can map one claim to groups (mappingGroups). But what if you need groups from department, roles, organization, and userType all at once?
This app solves that. Configure rules to map any number of claims to Nextcloud groups:
| Without this app | With this app |
|---|---|
| 1 claim → groups | N claims → groups via configurable rules |
roles → ["admin", "editor"] |
department → Engineering |
roles → role_admin, role_editor |
|
organization → Staff (via lookup table) |
|
userType == INTERNAL → Internal-Users |
- Nextcloud 29 -- 32
- PHP 8.1+
- user_oidc app installed and enabled
# Install from the App Store
php occ app:install oidc_groups_mapping
# Configure rules
php occ oidc-groups:set '{
"version": 1,
"mode": "additive",
"rules": [
{"id": "departments", "type": "direct", "enabled": true, "claimPath": "department", "config": {}},
{"id": "user-roles", "type": "prefix", "enabled": true, "claimPath": "roles", "config": {"prefix": "role_"}}
]
}'
# Test with a sample token
php occ oidc-groups:test --token '{"department":"Engineering","roles":["admin","editor"]}'Using the JWT token example above:
| Type | What it does | Example | Result |
|---|---|---|---|
direct |
Claim value becomes group name | department |
Engineering |
prefix |
Prefix each value | roles with prefix role_ |
role_admin, role_editor |
map |
Lookup table | organization: corp.example.com → Staff |
Staff |
conditional |
If claim matches condition → assign groups | userType equals INTERNAL |
Internal-Users |
template |
String template with {value} placeholder |
department with dept_{value} |
dept_Engineering |
| Operator | Description | Example |
|---|---|---|
equals |
Exact string match | userType equals "EXTERNAL" |
contains |
Array contains value | roles contains "admin" |
regex |
Regex match (with delimiters) | email matches /@example\.com$/ |
Rules are stored as JSON in IAppConfig. You can configure them via Admin Settings or OCC commands.
{
"version": 1,
"mode": "additive",
"rules": [
{
"id": "departments",
"type": "template",
"enabled": true,
"claimPath": "department",
"config": { "template": "dept_{value}" }
},
{
"id": "user-roles",
"type": "prefix",
"enabled": true,
"claimPath": "roles",
"config": { "prefix": "role_" }
},
{
"id": "org-mapping",
"type": "map",
"enabled": true,
"claimPath": "organization",
"config": {
"values": {
"corp.example.com": "Staff",
"partner.example.com": "Partners"
},
"unmappedPolicy": "ignore"
}
},
{
"id": "internal-flag",
"type": "conditional",
"enabled": true,
"claimPath": "userType",
"config": {
"operator": "equals",
"value": "INTERNAL",
"groups": ["Internal-Users"]
}
}
]
}| Mode | Behavior |
|---|---|
additive (default) |
Rule-produced groups are merged with existing groups from mappingGroups |
replace |
Only rule-produced groups are kept. If rules produce nothing, falls back to existing groups (safety net) |
Dot-notation paths resolve nested token claims:
department→token.departmentextended_attributes.auth.permissions→token.extended_attributes.auth.permissions
URL-style claim keys are also supported (e.g., https://idp.example.com/claims/domain).
When a map rule encounters a value not in the lookup table:
| Policy | Behavior |
|---|---|
ignore |
Value is silently skipped |
passthrough |
Original claim value is used as group name |
This app works alongside user_oidc's built-in group mapping. Here's how they interact:
When a user logs in via OIDC, user_oidc dispatches an event for group mapping. This app intercepts that event, applies your rules against the full JWT token, and produces groups.
The mappingGroups setting in your user_oidc provider configuration controls what user_oidc extracts before this app runs:
mappingGroups setting |
What happens |
|---|---|
Set to a claim (e.g., groups) |
user_oidc extracts groups from that claim first. This app then merges (additive) or replaces them. |
| Empty or claim doesn't exist | No groups extracted by user_oidc. This app produces all groups from rules. |
If your IdP token has a groups claim and you want to combine it with advanced rules:
- In user_oidc provider settings, set
mappingGroupstogroups(or your claim name) - Configure this app in
additivemode — rule-produced groups merge with the native ones
If you want full control via rules only:
- Leave
mappingGroupsat its default — it doesn't matter what it's set to - Configure all your mapping rules in this app
- Use
replacemode if you want only rule-produced groups
- No special user_oidc settings are required — the app works with any provider configuration
- The app accesses the full JWT token, not just the claim pointed to by
mappingGroups - Other attribute mappings (
mappingDisplayName,mappingEmail, etc.) are unaffected
Configure rules through the Nextcloud admin panel under Administration → OIDC Groups Mapping.
The admin UI has 3 tabs:
- Visual Editor — add, edit, reorder (drag-and-drop), enable/disable, and delete rules
- JSON — raw JSON editor for bulk editing or copy-pasting configurations
- Simulator — paste a sample JWT token and preview which groups each rule produces
Rules can be managed programmatically via OCS REST endpoints:
GET /ocs/v2.php/apps/oidc_groups_mapping/api/v1/rules— list all rulesPUT /ocs/v2.php/apps/oidc_groups_mapping/api/v1/rules— replace all rulesPOST /ocs/v2.php/apps/oidc_groups_mapping/api/v1/rules/simulate— simulate rules against a token
# List configured rules
php occ oidc-groups:list
# Set rules from JSON
php occ oidc-groups:set '{"version":1,"mode":"additive","rules":[...]}'
# Test rules against a sample token
php occ oidc-groups:test --token '{"department":"IT","roles":["admin","editor"]}'
# Test with existing groups (to see merge behavior)
php occ oidc-groups:test --token '{"department":"IT"}' --existing '["users"]'This app listens to the AttributeMappedEvent dispatched by user_oidc during login. When the mappingGroups attribute is being processed, it:
- Loads mapping rules from
IAppConfig - Resolves claim values from the token using dot-notation paths
- Applies each enabled rule to produce groups
- Merges or replaces the group list depending on the mode
- Calls
setValue()andstopPropagation()on the event
Install directly from the Nextcloud App Store:
- Via admin UI: Administration → Apps → search "OIDC Groups Mapping" → Install
- Via OCC:
php occ app:install oidc_groups_mapping
cd /var/www/html/custom_apps/
wget https://github.com/strobelpierre/nextcloud_oidc_groups_mapping/releases/latest/download/oidc_groups_mapping.tar.gz
tar xzf oidc_groups_mapping.tar.gz
php occ app:enable oidc_groups_mappingcd /var/www/html/custom_apps/
git clone https://github.com/strobelpierre/nextcloud_oidc_groups_mapping.git oidc_groups_mapping
cd oidc_groups_mapping
composer install --no-dev
npm ci && npm run build
php occ app:enable oidc_groups_mapping# PHP dependencies
composer install
# Frontend (Vue admin UI)
npm ci
npm run build # Production build
npm run dev # Development build
npm run watch # Watch mode with auto-rebuild
# Quality checks
composer test:unit # PHPUnit
composer psalm # Static analysis
composer cs:check # Code style check (requires vendor-bin setup)
composer cs:fix # Fix code style
composer lint # PHP syntax checkmkdir -p vendor-bin/cs-fixer
cd vendor-bin/cs-fixer
composer require nextcloud/coding-standard
cd ../..A Docker Compose setup with Keycloak is available in dev/:
cd dev && docker compose up -dmake appstore
# Output: build/oidc_groups_mapping.tar.gz- Ensure
user_oidcis installed and enabled - Verify claim paths match your IdP token structure using
php occ oidc-groups:test - Check Nextcloud logs for
oidc_groups_mappingmessages
- Verify rules are enabled (
"enabled": true) - Ensure the JSON is valid via the admin settings UI or
php occ oidc-groups:list - For conditional rules with
regexoperator, ensure the regex pattern is valid (including delimiters)
- Migrate frontend from Vue 2 (
@nextcloud/vuev8) to Vue 3 (@nextcloud/vuev9) when targeting Nextcloud 33+
Contributions are welcome! See CONTRIBUTING.md for guidelines.
AGPL-3.0-or-later -- see LICENSE for details.