goauthentik / goauthentik/authentik

LDAP Sync removes group memberships after every sync (group.users.set() clears members)

Open
#21,040 5 comments 1 reaction 0 assignees View on GitHub
bug bug/confirmed
Dominant language
Python
Stars
25.6k
Forks
2k
Avg merge
1d 1h
Merged PRs (30d)
644

Description

### Describe the bug

After every LDAP sync, all group memberships are cleared for LDAP-synced users.
The sync correctly imports users and groups (with ldap_uniq), but the membership
relationship between users and groups is lost after each sync run.

### How to reproduce

1. Configure LDAP Source with Active Directory
2. Run LDAP sync → Users and groups imported correctly ✅
3. Check group memberships → Members are assigned ✅
4. Run LDAP sync again
5. Check group memberships → **All memberships are gone** ❌

### Expected behavior

Group memberships should persist after LDAP sync, or be correctly
re-created based on LDAP group membership attributes (e.g. `member` attribute).

Actual behavior
After every sync, `group.users.set()` is called which **clears all existing
memberships** and only re-adds members if they are explicitly listed in the
LDAP group's `member` attribute during that sync run.

### Screenshots

_No response_

### Additional context

Groups have correct ldap_uniq values set in authentik_core_group
Users have correct ldap_uniq values set in authentik_core_user
The issue occurs 100% reproducibly on every sync run
Possibly related to: #18726

## Database verification
```sql
-- After sync: only akadmin remains
SELECT u.username, STRING_AGG(g.name, ', ') AS groups
FROM authentik_core_user u
JOIN authentik_core_user_groups ug ON u.id = ug.user_id
JOIN authentik_core_group g ON g.group_uuid = ug.group_id
GROUP BY u.username ORDER BY u.username;

-- Result: only akadmin | authentik Admins
-- All LDAP users lose their group memberships

Workaround
We implemented a cronjob that re-inserts memberships directly via SQL every 5 minutes using the ldap_uniq attribute to match users to groups. This is not a sustainable solution.

Workaround used
Re-inserting memberships every 5 minutes via cronjob directly in PostgreSQL:

INSERT INTO authentik_core_user_groups (user_id, group_id)
SELECT u.id, g.group_uuid
FROM authentik_core_user u
JOIN authentik_core_group g
ON g.attributes->>'ldap_uniq' = ANY(
ARRAY(SELECT jsonb_array_elements_text(u.attributes->'memberOf'))
)
ON CONFLICT DO NOTHING;

Logs
No errors visible in authentik-worker-1 logs during or after sync. The sync completes with "event": "Task finished", "exc": null – making this bug hard to detect without manually checking the database.

## LDAP Property Mappings

### Custom LDAP Group Mapping
```python
return {
"name": ldap.get("cn"),
"attributes": {
"ldap_dn": ldap.get("distinguishedName"),
"ldap_uniq": ldap.get("distinguishedName"),
"distinguishedName": ldap.get("distinguishedName"),
"member": ldap.get("member", []),
}
}

### Custom LDAP User Mapping
```python
return {
"name": ldap.get("displayName"),
"username": ldap.get("sAMAccountName"),
"email": ldap.get("mail"),
"attributes": {
"ldap_dn": ldap.get("distinguishedName"),
"ldap_uniq": ldap.get("distinguishedName"),
"distinguishedName": ldap.get("distinguishedName"),
"memberOf": ldap.get("memberOf", []),
"upn": ldap.get("userPrincipalName"),
"sn": ldap.get("sn"),
"givenName": ldap.get("givenName"),
}
}

### Deployment Method

Docker

### Version

2026.2.1

### Relevant log output

```shell

```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.