Issues with Kerberos SSO and User ID

I recently setup a Seafile server version 11.0.12 (current pkg repo version) on FreeBSD 14, paired with Apache 2.4 as reverse proxy and MySQL 8.4 database. Users only in Active Directory via LDAP integration, with Kerberos single sign-on support also. Kerberos was already setup and the host domain-joined.

I configured LDAP to AD in seahub_settings.py as follows:

ENABLE_LDAP = True
LDAP_SERVER_URL = ‘ldaps://dc.acme.local’
LDAP_BASE_DN = ‘ou=People,dc=ad,dc=acme,dc=local’
LDAP_ADMIN_DN = ‘cn=ldapbind,ou=Services,dc=acme,dc=local’
LDAP_ADMIN_PASSWORD = ‘LdapPasswd’
LDAP_PROVIDER = ‘ACME’
LDAP_LOGIN_ATTR = ‘userPrincipalName’
LDAP_CONTACT_EMAIL_ATTR = ‘mail’
LDAP_USER_ROLE_ATTR = ‘’
LDAP_USER_FIRST_NAME_ATTR = ‘givenName’
LDAP_USER_LAST_NAME_ATTR = ‘sn’
LDAP_USER_NAME_REVERSE = False
LDAP_FILTER = ‘memberOf=CN=Cloud,OU=Groups,DC=ad,DC=acme,DC=local’

Using email as a login is not a good idea with Active Directory. AD provides userPrincipalName and sAMAccountName as unique account identifiers. I used the UPN because Seafile prefers an email form (even though it’s not an email address) and Kerberos will supply a UPN. I manually added the sAMAccountName as the login_id (no config setting for this) so users can login with their normal Windows username.

LDAP worked fine, but the subsequent issues I experienced arise from the new behaviour of creating randomly-generated internal user IDs. For example, when a AD user with sAMAccountName=bob, userPrincipalName=bob@ad.acme.local, mail=bob.smith@acme.c0m makes his initial login, Seafile adds to the databases:

ccnet.emailuser: email=randomID@auth.local

seahub.profile_profile: user=randomID@auth.local, contact_email=bob.smith@acme.c0m

seahub.social_auth_usersocialauth: username=randomID@auth.local, uid=bob@ad.acme.local

I then set about enabling Kerberos/SSO in seahub_settings.py as follows:

ENABLE_REMOTE_USER_AUTHENTICATION = True
REMOTE_USER_HEADER = ‘HTTP_X_REMOTE_USER’
REMOTE_USER_CREATE_UNKNOWN_USER = False
REMOTE_USER_PROTECTED_PATH = [‘/sso/’, ‘/seafile/sso/’]

And this added to the Apache virtual host:

<Location /seafile/sso>
AuthType Kerberos
AuthName “Acme”
KrbMethodNegotiate On
KrbMethodK5Passwd Off
KrbAuthRealms AD.ACME.LOCAL
KrbServiceName HTTP/www.acme.com@AD.ACME.LOCAL
Krb5Keytab /etc/krb5.keytab
Require valid-user
RequestHeader set X-Remote-User %{REMOTE_USER}s
</Location>

Although I could see Kerberos working in Apache, it was rejected by Seafile until I discovered the undocumented setting remote_user_protected_path, which had to be adjusted because my Seafile is at www.acme.c0m/seafile. After fixing that, SSO produced an error about failing to add the user. Obviously Seafile would not associate the supplied Kerberos UPN with the existing LDAP account uid because it only looks at the random ID. Searching for help, it seems the recommended ‘solution’ is to allow SSO to make a new account and then link the two in the social_auth_usersocialauth table. So I set remote_user_create_unknown_user=true to test. If the same user clicks single sign-on it now works, but adds this to the database:

ccnet.emailuser: email=bob@ad.acme.local

seahub.profile_profile: user=bob@ad.acme.local

So it did not generate a random user ID, and nothing was added to the social_auth_usersocialauth table. This meant there was no way of linking the SSO account to the LDAP account. I tried manually inserting an entry to the social_auth_usersocialauth table, but that did not work.

The only way I could make LDAP and Kerberos work together (and I spent some time on this) was to get rid of the auto-generated user IDs. Create the LDAP accounts, dump the databases, search and replace each randomID@auth.local with the UPN, then import the databases. I could then disable remote_user_create_unknown_user, and SSO correctly locates the LDAP user account.

I am sure the developers have their reasons for introducing this random user ID concept, but it strikes me as flawed in a few ways. Creating duplicate accounts for the same user, and patching the database to link them, is a poor design concept. Allowing unknown user account creation is undesirable from a security and management point. And the biggest issue is that the functionality is broken. Integration with Active Directory using LDAP and Kerberos is a very common scenario for web applications, and it should just work.

Having predicable, controllable user IDs is paramount to a secure, reliable system. I think the software should at least provide a config option for the administrator to be able to manage how users are identified.

This issue occurs because Seafile 11 introduced a virtual user ID system where internal identifiers (like randomID@auth.local) are used to decouple the login attribute from the internal account. When using both LDAP and SSO (including Kerberos via REMOTE_USER), Seafile may create separate accounts if it doesn’t know they belong to the same person.

To resolve this and ensure that SSO logins match your existing LDAP accounts, you should add the following setting to your seahub_settings.py:

SSO_LDAP_USE_SAME_UID = True

How it works:

When this option is enabled, Seafile will check if the UID provided by the SSO provider (in your case, the Kerberos UPN from REMOTE_USER) matches the LDAP_LOGIN_ATTR of an existing LDAP user. If a match is found, Seafile will bind the SSO login to that existing LDAP account instead of creating a new one.

Implementation steps:

  1. Check Attributes: Ensure that the value provided by Apache in the X-Remote-User header exactly matches the userPrincipalName stored in your LDAP (as you have LDAP_LOGIN_ATTR = 'userPrincipalName').
  2. Existing Accounts: For users who already have duplicate accounts:
    • The user can log in via LDAP and then link their SSO account in the Settings > Social Login section of their profile.
    • Alternatively, as an administrator, you can manually link them by ensuring the social_auth_usersocialauth table has entries for both the ldap and remoteuser (or your SSO provider name) providers pointing to the same Seafile username (the random ID).
  3. Path Configuration: You correctly identified that REMOTE_USER_PROTECTED_PATH is necessary when Seafile is served from a non-root path (like /seafile).

For more details on this logic, you can refer to the Seafile Admin Manual on LDAP Integration.

Note: In the recently released Seafile 13.0, further options like SAML_CREATE_UNKNOWN_USER have been added to provide even stricter control over account creation during SSO flows.

Thank you for the feedback. I did not mention it in my post, but I did come across the “sso_ldap_use_same_uid” setting when searching the web. I did try adding

SSO_LDAP_USE_SAME_UID = True

to seahub_settings.py, but it did not fix the SSO/LDAP mismatch issue for me. I did a recursive text search for “sso_ldap_use_same_uid” on the Seafile application root and it returned nothing, which may explain why it had no effect. Maybe this setting was introduced in a later version of Seafile or is a pro-version feature. My Seafile version is 11.0.12, which is current for the FreeBSD software repository. Maybe someone could confirm this.

I can also report that my Seahub UI does not have a “Settings → Social Login” menu item as described. Again, this may have been added in a later software version than mine.

I know from Apache debug logs it is definitely creating and passing the X-Remote-User header with the Windows User Principal Name. The problem is how Seafile is handling it.

I think the key to where it falls apart is that Seafile DOES create a randomID@auth.local on initial login with LDAP credentials, but does NOT create a randomID@auth.local for SSO login - it uses the UPN from Apache. If SSO behaved the same way as LDAP and created an internal ID, it would have added this to the “social_auth_usersocialauth” table, and I maybe could have edited this to join them.

Unfortunately, nothing I tried worked, so I ended up taking the sledgehammer approach of removing the internal IDs. It’s not what I wanted, but it was the last resort, and it fixed the issue.

If a combination of:

SSO_LDAP_USE_SAME_UID = True
REMOTE_USER_CREATE_UNKNOWN_USER = False

allows SSO login to find and use existing LDAP account (without duplicating accounts), I agree this would be the optimal setup.