goauthentik / goauthentik/authentik
SCIM: Normalize single-valued attributes from LDAP (e.g. department) by default
- Dominant language
- Python
- Stars
- 25.6k
- Forks
- 2k
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 651
Description
**Is your feature request related to a problem? Please describe.**
When using an LDAP source for attributes, values such as department don’t normalize cleanly into SCIM mappings.
* LDAP may return ["IT"] as a list, but SCIM expects a string. This can cause updates to append values rather than replace them.
* Using list_flatten() can help, but if LDAP returns a string ("IT"), it gets treated as an iterable of characters, resulting in "I".
This requires custom mapping logic in LDAP Source Property Mappings and SCIM Provider Mappings to stay SCIM-compliant.
**Describe the solution you’d like**
It would be helpful if Authentik normalized these values automatically with safe defaults, especially for attributes that SCIM defines as single-valued (like department, title, and manager).
For example:
* If the attribute is a single-element list, unwrap it.
* If it’s already a string, preserve it.
* If it’s multi-valued, allow admins to handle that explicitly.
This way, SCIM payloads would be compliant, and administrators wouldn’t need to reimplement the same normalization logic in every deployment.
**Describe alternatives you’ve considered**
1. Flattening in LDAP mappings. Works for lists, but breaks on strings:
```python
return {
"attributes": {
"department": list_flatten(ldap.get("departmentNumber")),
},
}
```
2. Custom normalization in SCIM Provider Mappings
```python
raw = request.user.attributes.get("department")
if isinstance(raw, list):
department = raw[0] if raw else None
elif isinstance(raw, str):
department = raw.strip() or None
else:
department = None
dept_value = department or "TBD"
result["urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"] = {
"department": dept_value
}
```
This works, but it’s verbose and requires every admin to maintain their own version.
Both approaches are workarounds — they solve the issue but add complexity.
**Additional context**
📉 Before (broken payloads):
```json
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
"department": ["IT"]
}
```
or:
```json
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
"department": "I"
}
```
✅ After (with workaround):
```json
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
"department": "IT"
}
```
Since the SCIM RFC (7643) defines department as a single string, it feels like Authentik could handle this more gracefully by default. That would make life easier for folks syncing attributes from LDAP into SCIM without needing extra mapping code.
Thanks for considering, and happy to test or help refine if this gets picked up! If the team feels this belongs in docs rather than code, I’d be glad to help draft examples” to show you’re open either way?
Contributor guide
Research direction
Start by tracing LDAP Source Property Mappings and SCIM Provider Mappings, then compare their value handling with the SCIM RFC 7643 definitions for single-valued attributes. Done means single-element lists become strings, existing strings remain intact, and multi-valued values remain available for explicit administrator handling.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100