Connect Neo4j to a LDAP


In this tutorial you will see how to connect Neo4j to a LDAP server for authentication and authorization (only available with the Enterprise Edition).

Setup an Openldap server

We need to have a LDAP server with some data. For this purpose, I will use the following docker image :

By default the domain is dc=example,dc=org and the admin is cn=admin,dc=example,dc=org with the password admin.

We can pass an argument to docker to add a ldif folder for initiate the ldap server with some data at the startup.

This is the ldif file that I use :

dn: ou=users,dc=example,dc=org
objectClass: organizationalUnit
objectClass: top
ou: users

dn: ou=groups,dc=example,dc=org
objectClass: organizationalUnit
objectClass: top
ou: groups

dn: cn=reader,ou=groups,dc=example,dc=org
objectClass: groupOfNames
objectClass: top
cn: reader

dn: cn=publisher,ou=groups,dc=example,dc=org
objectClass: groupOfNames
objectClass: top
cn: publisher

dn: cn=architect,ou=groups,dc=example,dc=org
objectClass: groupOfNames
objectClass: top
cn: architect

dn: cn=admin,ou=groups,dc=example,dc=org
objectClass: groupOfNames
objectClass: top
cn: admin

dn: uid=reader,ou=users,dc=example,dc=org
objectClass: organizationalPerson
objectClass: person
objectClass: extensibleObject
objectClass: uidObject
objectClass: inetOrgPerson
objectClass: top
cn: Reader User
givenName: Reader
sn: reader
uid: reader
ou: users
userpassword: test
memberOf: cn=reader,ou=groups,dc=example,dc=org

dn: uid=publisher,ou=users,dc=example,dc=org
objectClass: organizationalPerson
objectClass: person
objectClass: extensibleObject
objectClass: uidObject
objectClass: inetOrgPerson
objectClass: top
cn: Publisher User
givenName: Publisher
sn: publisher
uid: publisher
ou: users
userpassword: test
memberOf: cn=publisher,ou=groups,dc=example,dc=org

dn: uid=architect,ou=users,dc=example,dc=org
objectClass: organizationalPerson
objectClass: person
objectClass: extensibleObject
objectClass: uidObject
objectClass: inetOrgPerson
objectClass: top
cn: Architect User
givenName: Architect
sn: architect
uid: architect
ou: users
userpassword: test
memberOf: cn=architect,ou=groups,dc=example,dc=org

dn: uid=admin,ou=users,dc=example,dc=org
objectClass: organizationalPerson
objectClass: person
objectClass: extensibleObject
objectClass: uidObject
objectClass: inetOrgPerson
objectClass: top
cn: Admin User
givenName: Architect
sn: admin
uid: admin
ou: users
userpassword: test
memberOf: cn=admin,ou=groups,dc=example,dc=org

As you can see, it only defines :

  • an organization unit for groups : ou=groups,dc=example,dc=org, with the following groups

    • group admin cn=admin,ou=groupss,dc=example,dc=org with the admin

    • group architect cn=architect,ou=groups,dc=example,dc=org with the architect

    • group publisher cn=publisher,ou=groups,dc=example,dc=org with the publisher

    • group reader cn=reader,ou=groups,dc=example,dc=org with the reader

  • an organization unit for users : ou=users,dc=example,dc=org, with the following users

    • user admin uid=admin,ou=users,dc=example,dc=org

    • user architect uid=architect,ou=users,dc=example,dc=org

    • user publisher uid=publisher,ou=users,dc=example,dc=org

    • user reader uid=reader,ou=users,dc=example,dc=org

INFO: users are must be linked to groups via the memberOf attribut

In my case, I have stored this ldif file in this folder /home/bsimard/ldif, so the command to start the docker image is the following :

docker run \
   --volume /home/bsimard/ldif/:/container/service/slapd/assets/config/bootstrap/ldif/custom \
   osixia/openldap:1.1.10 \
   --copy-service \
   --loglevel debug

Neo4j configuration

Now we have to configure Neo4j to use the LDAP. For this, edit the NEO4J_HOME/conf/neo4j.conf and put those properties :

# Security Configuration

# The authentication and authorization provider that contains both users and roles.
# This can be one of the built-in `native` or `ldap` auth providers,
# or it can be an externally provided plugin, with a custom name prefixed by `plugin`,
# i.e. `plugin-<AUTH_PROVIDER_NAME>`.

# The time to live (TTL) for cached authentication and authorization info when using
# external auth providers (LDAP or plugin). Setting the TTL to 0 will
# disable auth caching.

# The maximum capacity for authentication and authorization caches (respectively).

# Set to log successful authentication events to the security log.
# If this is set to `false` only failed authentication events will be logged, which
# could be useful if you find that the successful events spam the logs too much,
# and you do not require full auditing capability.

# LDAP Auth Provider Configuration

# URL of LDAP server to use for authentication and authorization.
# The format of the setting is `<protocol>://<hostname>:<port>`, where hostname is the only required field.
# The supported values for protocol are `ldap` (default) and `ldaps`.
# The default port for `ldap` is 389 and for `ldaps` 636.
# For example: `ldaps://`.
# NOTE: You may want to consider using STARTTLS (``) instead of LDAPS
# for secure connections, in which case the correct protocol is `ldap`.

# Use secure communication with the LDAP server using opportunistic TLS.
# First an initial insecure connection will be made with the LDAP server, and then a STARTTLS command
# will be issued to negotiate an upgrade of the connection to TLS before initiating authentication.

# The LDAP referral behavior when creating a connection. This is one of `follow`, `ignore` or `throw`.
# `follow` automatically follows any referrals
# `ignore` ignores any referrals
# `throw` throws an exception, which will lead to authentication failure

# The timeout for establishing an LDAP connection. If a connection with the LDAP server cannot be
# established within the given time the attempt is aborted.
# A value of 0 means to use the network protocol's (i.e., TCP's) timeout value.

# The timeout for an LDAP read request (i.e. search). If the LDAP server does not respond within
# the given time the request will be aborted. A value of 0 means wait for a response indefinitely.

# LDAP Authentication Configuration

# LDAP authentication mechanism. This is one of `simple` or a SASL mechanism supported by JNDI,
# for example `DIGEST-MD5`. `simple` is basic username
# and password authentication and SASL is used for more advanced mechanisms. See RFC 2251 LDAPv3
# documentation for more details.

# LDAP user DN template. An LDAP object is referenced by its distinguished name (DN), and a user DN is
# an LDAP fully-qualified unique user identifier. This setting is used to generate an LDAP DN that
# conforms with the LDAP directory's schema from the user principal that is submitted with the
# authentication token when logging in.
# The special token {0} is a placeholder where the user principal will be substituted into the DN string.{0},ou=users,dc=example,dc=org

# Determines if the result of authentication via the LDAP server should be cached or not.
# Caching is used to limit the number of LDAP requests that have to be made over the network
# for users that have already been authenticated successfully. A user can be authenticated against
# an existing cache entry (instead of via an LDAP server) as long as it is alive
# (see ``).
# An important consequence of setting this to `true` is that
# Neo4j then needs to cache a hashed version of the credentials in order to perform credentials
# matching. This hashing is done using a cryptographic hash function together with a random salt.
# Preferably a conscious decision should be made if this method is considered acceptable by
# the security standards of the organization in which this Neo4j instance is deployed.

# LDAP Authorization Configuration
# Authorization is performed by searching the directory for the groups that
# the user is a member of, and then map those groups to Neo4j roles.

# Perform LDAP search for authorization info using a system account instead of the user's own account.
# If this is set to `false` (default), the search for group membership will be performed
# directly after authentication using the LDAP context bound with the user's own account.
# The mapped roles will be cached for the duration of ``,
# and then expire, requiring re-authentication. To avoid frequently having to re-authenticate
# sessions you may want to set a relatively long auth cache expiration time together with this option.
# NOTE: This option will only work if the users are permitted to search for their
# own group membership attributes in the directory.
# If this is set to `true`, the search will be performed using a special system account user
# with read access to all the users in the directory.
# You need to specify the username and password using the settings
# `` and
# `` with this option.
# Note that this account only needs read access to the relevant parts of the LDAP directory
# and does not need to have access rights to Neo4j, or any other systems.

# An LDAP system account username to use for authorization searches when
# `` is `true`.
# Note that the `` will not be applied to this username,
# so you may have to specify a full DN.,dc=example,dc=org

# An LDAP system account password to use for authorization searches when
# `` is `true`.

# The name of the base object or named context to search for user objects when LDAP authorization is enabled.
# A common case is that this matches the last part of ``.,dc=example,dc=org

# The LDAP search filter to search for a user principal when LDAP authorization is
# enabled. The filter should contain the placeholder token {0} which will be substituted for the
# user principal.*)(uid={0}))

# A list of attribute names on a user object that contains groups to be used for mapping to roles
# when LDAP authorization is enabled.

# An authorization mapping from LDAP group names to Neo4j role names.
# The map should be formatted as a semicolon separated list of key-value pairs, where the
# key is the LDAP group name and the value is a comma separated list of corresponding role names.
# For example: group1=role1;group2=role2;group3=role3,role4,role5
# You could also use whitespaces and quotes around group names to make this mapping more readable,
# for example:\
#          "cn=Neo4j Read Only,cn=users,dc=example,dc=com"      = reader;    \
#          "cn=Neo4j Read-Write,cn=users,dc=example,dc=com"     = publisher; \
#          "cn=Neo4j Schema Manager,cn=users,dc=example,dc=com" = architect; \
#          "cn=Neo4j Administrator,cn=users,dc=example,dc=com"  = admin\
          "cn=reader,ou=groups,dc=example,dc=org"  = reader; \
          "cn=publisher,ou=groups,dc=example,dc=org"  = publisher; \
          "cn=architect,ou=groups,dc=example,dc=org"  = architect; \
          "cn=admin,ou=groups,dc=example,dc=org"  = admin

Now you can restart Neo4j, and open the Neo4j browser (http://localhost:7474) to test the connection.

How it works ?

There is two steps : authentication and authorization.


The authentication is made by a direct bind of the user with the DN defined like this : Neo4j replace the user login (ie .{0}) in the configuration key

In the example, if you try to connect with the user admin, Neo4j will do a bind with uid=admin,ou=Users,dc=example,dc=org


Now Neo4j knows that the user is valid, so it looks up for the user’s groups.

For this, it needs a system account to searh the authorizations. To do it, it performs : * the ldap query configure by by replacing the {0} with the user’s login : (&(objectClass=*)(uid={0})) * inside the DN configure by : ou=users,dc=example,dc=org

Once it has found the correspondig user, it will take the attribute configure by (so memberOf in our example) to have the list of all the user’s group. Finally, it can makes the role mapping as defined in

Good to know

  • The LDAP connector is only available into the Enterprise Edition

  • Configuration is simple

  • LDAP groups must be linked into users DN (via an attribut like memberOf in our case)

  • In this example we have used a system account but it’s not necessary if users have the right to BIND & SEARCH themself