Migration from LDAP to OIDC: Nextcloud
enIn my hackerspace we operate various services for our members. Up until this month, most of these services used to do user authentication against a LDAP server. For a multitude of reasons, we replaced the LDAP server with an OpenID Connect SSO using a Keycloak server as the OIDC Identity Provider.
In this series of articles I summarize the efforts required to migrate each of the services from LDAP to OIDC authentication. This article covers the setup and migration in Nextcloud.
OIDC Setup in Nextcloud
While LDAP integration is part of the Nextcloud core,
OIDC integration must be installed as an additional app,
user_oidc
. Once this app is installed and enabled
(occ app:enable user_oidc
), you'll find a new tab in Nextcloud's
admin settings called "OpenID Connect", where you can add your
Identity Provider.
When adding a new IdP, you need to pay attention to choose an user ID
claim that matches the user IDs from LDAP: If you used the uid
or
sAMAccountName
attribute in the LDAP backend, chances are you can
simply use the preferred_username
claim in the OIDC backend and call
it a day. (Please test whether these indeed match for all users!)
However, if you used e.g. the objectGUID
LDAP attribute, you need to
make sure that the same attribute is available as an OIDC token claim.
Finally, remove the Use unique user id
check mark, otherwise the OIDC
backend generates its own user IDs anyway (by using a hash over the
OIDC username and the name of the IdP).
Migration of Users From LDAP to OIDC
Users in Nextcloud are not managed in one central database table. Instead, each user backend maintains its own set of users and mapping to Nextcloud user IDs, usually each using its own database table, such as:
oc_users
: Users created directly in Nextcloud.oc_ldap_user_mapping
: Users maintained by theuser_ldap
backend and the mapping from LDAP DNs to Nextcloud usernames.- Groups and group memberships synced from LDAP are stored in
oc_ldap_group_mapping
andoc_ldap_group_members
.
- Groups and group memberships synced from LDAP are stored in
oc_user_oidc
: Users maintained by theuser_oidc
backend.
This is why, as soon as the user_ldap
app is disabled, all users and
groups that were synced from LDAP disappear immediately. Don't
worry - the users aren't gone. If you re-enable the user_ldap
app,
they automagically reappear. But the LDAP user mappings need to be
migrated to the oc_user_oidc
table so that the users are still
around when the LDAP backend is disabled. For this we first need to
understand the table schemas:
MariaDB [nextcloud]> select * from oc_ldap_user_mapping;
+--------------------------------------+---------------+----------------+------------------------------------------------------------------+
| ldap_dn | owncloud_name | directory_uuid | ldap_dn_hash |
+--------------------------------------+---------------+----------------+------------------------------------------------------------------+
| uid=s3lph,cn=users,dc=example,dc=org | s3lph | s3lph | faea045b8b1be34c7e02fa0771f3a2d6b7cbad4297a68c3c3fc1c39ff3cd2988 |
| uid=s4lph,cn=users,dc=example,dc=org | s4lph | s4lph | 14df2aecf103280c8fd693b77959f7fdab4c530fce9ebc35575d9b763fd1c5e6 |
| uid=s5lph,cn=users,dc=example,dc=org | s5lph | s5lph | a5938499eb735d4cdb2aebe641a155aa930bb0962003e595ce960eb2a956d10d |
+--------------------------------------+---------------+----------------+------------------------------------------------------------------+
MariaDB [nextcloud]> select * from oc_user_oidc;
+----+-----------+--------------+
| id | user_id | display_name |
+----+-----------+--------------+
| 2 | s3lph | s3lph |
+----+-----------+--------------+
In the oc_ldap_user_mapping
table, we have a mapping of DNs
(ldap_dn
) to usernames in Nextcloud (owncloud_name
). The key
component here is the owncloud_name
column. This is the
Nextcloud internal name, and also the name that must be provided by
the OIDC backend (i.e. it must be present as a claim in the
id_token
, and that claim must be chosen as the "User ID mapping"
attribute in the OIDC provider settings in Nextcloud. The
oc_user_oidc
table, on the other hand, does not store any mappings
between OIDC and Nextcloud IDs. Instead it only contains the user ID
and display name as provided by the OIDC provider.
The user migration from LDAP to OIDC can then be performed with a single SQL statement:
INSERT IGNORE INTO oc_user_oidc (user_id, display_name) SELECT owncloud_name, owncloud_name FROM oc_ldap_user_mapping;
Though you may want to keep the display name in mind. With our Nextcloud instance, we used the username as display name as well, so we simply used the same value for both columns. I don't think the LDAP display names are stored in the database, so if you have different display names, this simple insert statement may not work as well for you.
Limitations
Synchronization of user groups from your OIDC IdP to Nextcloud is not
possible yet, but there's an open pull request that
implements this feature. This means that until this is merged and
released, group memberships that have previously been synced from LDAP
won't be updated automatically anymore. Furthermore, if/when you
disable the user_ldap
app, any group that was synced from LDAP will
disappear, and has to be recreated.
Another limitation is that OIDC users are only created in Nextcloud once they sign in for the first time. This means that you e.g. can't share files with users that have never signed in before. If your IdP has an automatable impersonation feature, you could work around this issue with a workflow such as:
- Make sure the option "Check Bearer token on API and WebDav requests" is enabled in Nextcloud's OIDC settings.
- Use the IdP's impersonation API to obtain an
id_token
for the new user. - Use this
id_token
to perform a simple request to Nextcloud, e.g. a WebDAVPROPFIND
. - Nextcloud automatically creates the new user.
Tips and Tricks
Nextcloud does not automatically redirect to the IdP for login - users always have to click the "Login with SSO" button. If you want your users to be redirected to the SSO automatically when they try to log in, you have to configure this in your web server. For example, in Apache 2:
RewriteEngine on
RewriteCond %{QUERY_STRING} !local=1
RewriteCond %{REQUEST_METHOD} GET
RewriteRule ^/login$ /apps/user_oidc/login/1 [L,R,QSD]
Replace the number at the end of the URL (/1
) with the ID of the
OIDC provider in Nextcloud. You can obtain this ID with occ
user_oidc:provider
. The RewriteCond
s in this config allow you to
append ?local=1
to disable the redirect, e.g. if you need to sign in
as a local admin user.