LDAP and Active Directory sign-in
This page is for administrators who want the people of their organisation to sign in to Libris with the account they already have in an LDAP directory (OpenLDAP, 389 Directory Server…) or in Active Directory. Every setting is described in configuration.md; what people see is in the user guide.
How it works
- The person picks the directory on the sign-in screen and types their directory login and password.
- Libris connects to the directory over TLS (
ldaps://, orldap://with StartTLS) and checks the server certificate and host name. - Libris binds with a service account and searches the search base with the user filter, the login being escaped before it goes into the filter. Exactly one entry must match.
- Libris binds again as that entry, with the password typed. An empty password is refused before this step: many directories treat a bind with an empty password as an anonymous bind and answer “success”.
- If groups are configured, the entry’s group attribute (
memberOf) must contain an allowed group. The administrator group, if any, decides the Libris administrator role. - The Libris account is found by the entry’s stable identifier (
entryUUIDorobjectGUID), created if the installation allows it, and its name and address are refreshed from the directory. If the account has a Libris second step (code or security key), it is asked for now.
The password is never stored, never logged and never sent anywhere but to the directory. Referrals are not followed. An unknown login, two matching entries and a wrong password all get the same answer, so the sign-in screen cannot be used to list the people in the directory.
Before you start
- A service account that can search the people (and read
memberOf) under the search base. It needs no other right. On Active Directory any ordinary domain user can do this; give it a long random password. - TLS on the directory. Use
ldaps://host:636, orldap://host:389with StartTLS. The host name in the address must be in the server certificate (an IP address is refused). If that certificate is signed by an internal CA, paste the CA certificate (PEM) into Authority certificate; otherwise the system trust store of the Libris image is used. - The Libris container must reach the directory on that port. With Docker, check from the API container:
docker compose exec api python -c "import socket; socket.create_connection(('ldap.example.com', 636), 5)". - A licence seat per person who will get an account. Restrict who may sign in with an allowed group before turning on account creation.
OpenLDAP
The defaults assume the inetOrgPerson schema and the memberof overlay, which fills memberOf on
people entries.
| Setting | Example |
|---|---|
| Directory type | OpenLDAP |
| Directory address | ldaps://ldap.example.com |
| Service account DN | cn=libris,ou=services,dc=example,dc=com |
| Search base | ou=people,dc=example,dc=com |
| User filter | (empty: (&(objectClass=inetOrgPerson)(uid={username}))) |
| Attributes | (empty: uid, mail, entryUUID, memberOf) |
| Allowed groups | cn=libris-users,ou=groups,dc=example,dc=com |
| Administrator group | cn=libris-admins,ou=groups,dc=example,dc=com |
A read-only service account, in LDIF (hash the password with slappasswd):
dn: cn=libris,ou=services,dc=example,dc=com
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: libris
userPassword: {SSHA}...
and an ACL that lets it read people, for example:
access to dn.subtree="ou=people,dc=example,dc=com"
by dn.exact="cn=libris,ou=services,dc=example,dc=com" read
by * break
Without the memberof overlay there is no memberOf attribute: leave the groups empty and restrict access in
the user filter instead, e.g. (&(objectClass=inetOrgPerson)(uid={username})(employeeType=translator)).
To check the settings from a shell before entering them in Libris:
ldapsearch -H ldaps://ldap.example.com -D 'cn=libris,ou=services,dc=example,dc=com' -W \
-b 'ou=people,dc=example,dc=com' '(&(objectClass=inetOrgPerson)(uid=jdupont))' uid mail entryUUID memberOf
Active Directory
| Setting | Example |
|---|---|
| Directory type | Active Directory |
| Directory address | ldaps://dc01.corp.example.com (port 636; a certificate must be installed on the domain controllers) |
| Service account DN | CN=svc-libris,OU=Service Accounts,DC=corp,DC=example,DC=com |
| Search base | OU=Staff,DC=corp,DC=example,DC=com |
| User filter | (empty: sAMAccountName or userPrincipalName, people only, disabled accounts excluded) |
| Attributes | (empty: sAMAccountName, mail, objectGUID, memberOf) |
| Allowed groups | CN=Libris Users,OU=Groups,DC=corp,DC=example,DC=com |
| Administrator group | CN=Libris Admins,OU=Groups,DC=corp,DC=example,DC=com |
People sign in with jdupont or jdupont@corp.example.com. The DOMAIN\jdupont form is not supported.
memberOf lists direct memberships only. To admit the members of nested groups, leave Allowed groups
empty and put the transitive-membership rule in the user filter:
(&(objectCategory=person)(objectClass=user)(sAMAccountName={username})(!(userAccountControl:1.2.840.113556.1.4.803:=2))(memberOf:1.2.840.113556.1.4.1941:=CN=Libris Users,OU=Groups,DC=corp,DC=example,DC=com))
Failed sign-ins count against the domain’s account lockout policy like any other bind. Libris’ own throttle stops a client after 20 failures for one name in five minutes, but set the lockout threshold with that in mind.
To check the settings from a Windows shell: Get-ADUser jdupont -Properties mail,memberOf,objectGUID.
Turn it on
- In Paramètres › Authentification unique, card Annuaire LDAP / Active Directory, fill in the settings above, tick Activer la connexion par l’annuaire, and save. Saving asks for a recent proof of your identity.
- Press Tester l’annuaire, first alone (connection, certificate and service account), then with a login in Identifiant à rechercher (the filter finds exactly that person). No password is tried by the test.
- Choose how accounts appear:
- existing local accounts: open each one in Paramètres › Utilisateurs and press Rattacher à l’annuaire. Its books, second step and API tokens stay; its Libris password is forgotten;
- new people: tick Créer les comptes à la première connexion depuis l’annuaire, with an allowed group.
- Sign in from a private window with a directory account, then with a local account, to check both doors.
Keep at least one local administrator account: if the directory is unreachable, it is the way in.
Troubleshooting
The API log (docker compose logs api) records every refusal with a reason code and never a password:
| Log line | Meaning |
|---|---|
ldap=unreachable reason=... | No connection: address, port, firewall, or the TLS handshake failed (unknown CA, host name not in the certificate). |
ldap=service_bind_refused | The service account DN or password is wrong, or the account is locked. |
ldap=lookup_refused entries=0 / entries=many | The filter found nobody, or more than one entry: check the search base and the filter. |
ldap=refused code=not_allowed | The person is not in an allowed group (check memberOf, and nested groups on AD). |
ldap=missing_identifier | The entry has no stable identifier attribute: check Attribut d’identifiant stable. |
ldap=refused code=unknown_account | Account creation is off and no Libris account is linked to this person. |
ldap=last_admin_kept | The administrator group no longer contains the last active administrator; the role was kept. |
Account creations, links, role changes made by the administrator group, and changes to these settings are also written in the audit log.
What happens when…
- someone leaves the organisation: once removed from the directory, disabled, or taken out of the allowed
groups, they can no longer sign in. Sessions already open last until they expire
(
SESSION_DURATION_HOURS); deactivate the account in Libris to close them at once. - someone is renamed or moved in the directory: the account is found by its stable identifier, so the new name and address are picked up at the next sign-in. If the new name is taken by another Libris account, a suffix is kept.
- the directory is replaced: identifiers change, so the new directory’s people are new to Libris, and an account already linked to the old directory cannot be moved to the new one from the interface. Plan such a migration before switching.