How to setup an LDAP directory for Connect2id server use

The Connect2id server requires a database to persist various data, such as the details of registered OAuth and OpenID Connect clients. LDAP was the only supported database backend until v6.0 which can also handle PostgreSQL, MySQL and H2.

1. Supported LDAP directory servers

The Connect2id server works with any directory that implements LDAP v3, as specified in RFC 4510. The server has been successfully tested with the following open source directory software:

  • OpenLDAP 2.4+
  • OpenDJ 2.6+

These two directory servers have been found to perform very well, even under extreme load and with active users in the tens of millions.

Tests with the 389 Directory Server (maintained by RedHat) revealed that it cannot handle large loads well, and we don't recommend it.

Other LDAP directories, such as MS Active Directory and Oracle Internet Directory, will probably work, but the Connect2id server hasn't been tested with them yet.

2. LDAP directory setup

These are the general steps for setting up an LDAP directory to store Connect2id server data. Check the bottom of this guide for directory specific instructions.

  1. Add required schemas

    Supply the LDAP directory with the required schemas:

    • Client registration schema: oidc-client-schema-[server].ldif
    • Long-lived authorisation schema: oidc-authz-schema-[server].ldif
    • Subject (end-user) session schema: oidc-session-schema-[server].ldif

    The [server] denotes the particular LDAP server implementation that you have, e.g. openldap.

  2. Add required indices

    The following attributes should be indexed to speed up LDAP searches:

    • authzSubject -- for retrieving long-lived authorisations by subject
    • authzActor -- for retrieving long-lived authorisations by actor
    • authzClientID -- for retrieving long-lived authorisations by client identifier
  3. Create directory branches for the data

    Create a directory branch for each type of persisted data:

    • A branch for the client registrations, e.g. ou=clients, o=c2id
    • For the authorisations, e.g. ou=authorizations, o=c2id
    • For the identifier based access tokens, e.g. ou=idAccessTokens, o=c2id
    • For the revocation journal, e.g. ou=revocations,o=c2id
    • For the subject sessions, e.g. ou=sessions, o=c2id
    • For the subject index, e.g. ou=subjects, o=c2id
  4. Create an LDAP user

    Create an LDAP user for the Connect2id server with permissions to read, search, and write entries in the above mentioned branches.

    For example: cn=c2id-server, o=c2id

    Do not give the LDAP user more permissions than necessary!

3. Recommendations

  • Dedicated LDAP directory: While it's possible to share an LDAP directory with other applications, we recommend that you provision the Connect2id server with its own LDAP directory. This is good for security and ensures performance will not suffer when another application loads the directory with its own requests.

  • Clustering: For high-availability we recommend that you set up an LDAP cluster configured for multi-master or fail-over (hot-standby) operation. Note that the Connect2id server caches extensively LDAP data, and may sustain basic service (single sign-on and token issue) with the backend LDAP directory being unavailable.

  • Backup: Make regular backups of your LDAP data. The client registrations are the most critical, as they hold the credentials of each client application that relies on the Connect2id server. If the registration data is lost each client application will have to be registered again. Loss of persisted authorisations (consent) has less of an impact -- all previously issued refresh tokens will become invalid and end-users will have to re-authorise their apps.

  • Security: Provision the LDAP directory with an SSL / TLS certificate to protect the communication with the Connect2id server. CA-issued as well as self-signed certificates are accepted (the latter require an extra setting).

4. Estimating LDAP storage requirements

4.1 Client registrations storage

The size of a typical OAuth 2.0 / OpenID Connect client registration is about 2 KBytes. The storage requirements for the registrations are therefore minuscule unless you intend to provide open registration with potentially thousands or millions of client applications. In such case consider using a separate LDAP directory or cluster to store the client registrations.

4.2 Long-lived authorisations storage

The size of a typical persisted authorisation begins at 1 KByte and may be take up to several KB with multiple or longer scope and claim values.

The maximum storage requirement can be calculated with the following formula:

Total client count X total end-user count X average authorisation size

For example:

  • 100 client applications
  • 1 million end-users
  • 1 KB average authorisation size
  • Required storage: 100 GB

4.3 Session storage

If end-user sessions are going to be persisted or passivated to LDAP, you have to plan storage for that as well.

To estimate the storage requirement for sessions:

Total user count X configured session quota per user X average session size

For example:

  • 1 million end-users
  • 5 sessions quota per user
  • 1 KB average session size
  • Required storage: 5 GB

5. LDAP configuration settings

When the LDAP directory is set up put down its details in the Infinispan XML configuration:

  1. ldapServer.url -- The LDAP directory URL(s) (host and port)

  2. ldapServer.selectionAlgorithm -- Specify a round-robin or fail-over mode if you have an LDAP directory cluster (two or more LDAP URLs).

  3. ldapServer.connectTimeout -- Provide a sensible LDAP connect timeout, in milliseconds.

  4. ldapServer.security -- Specify if SSL or StartTLS LDAP connection security is required.

  5. ldapServer.trustSelfSignedCerts -- Set to true if the LDAP directory has a self-signed certificate for SSL and StartTLS.

  6. ldapServer.connectionPoolSize -- Set an LDAP connection pool size.

  7. ldapUser.dn and ldapUser.password -- The credentials for the LDAP user.

  8. ldapDirectory.baseDN -- The directory branches for the client registrations, authorisations, etc.

6. Browsing the LDAP directory

The directory content can be easily examined with a standard LDAP browser. Apache Directory Studio is probably the best and most versatile one.

Warning: Client registrations, authorisations and other persisted entries should not be edited directly via LDAP, unless the Connect2id server is shut down. The server caches data extensively and your changes may not be seen or may be overwritten. You also run the risk of corrupting the data. Use the dedicated Connect2id server web APIs instead.

7. Directory specific setup

Here you can find detailed instructions for setting up an OpenDJ or OpenLDAP directory server.

7.1 OpenDJ

These setup instructions apply to OpenDJ 2.6 and 3.0.

7.1.1 Add required schemas

Simple offline method to add the required schemas to a freshly installed OpenDJ directory:

  1. Make sure the OpenDJ server is stopped. This can be done with the bin/status utility. To stop it run the bin/stop-ds script in the OpenDJ installation directory.

  2. Save the schema LDIF files to the config/schema directory. Give the schema files a numeric prefix that will have them loaded after all standard schema definitions. For example:

    • 90-oidc-client-schema-opendj.ldif

    • 91-oidc-authz-schema-opendj.ldif

    • 92-oidc-session-schema-opendj.ldif

  3. Start the OpenDJ server with bin/start-ds.

7.1.2 Add indices

Use the OpenDJ dsconfig utility to create indices for the authzSubject, authzActor and the authzClientID attributes (assuming the admin connector is running on localhost at port 4444, and has the following admin credentials):

For OpenDJ 2.6:

opendj/bin/dsconfig  create-local-db-index \
    --hostname localhost \
    --port 4444 \
    --trustAll \
    --bindDN "cn=Directory Manager" \
    --bindPassword secret \
    --backend-name userRoot \
    --index-name authzActor \
    --set index-type:equality \
    --set index-entry-limit:1000000
opendj/bin/dsconfig  create-local-db-index \
    --hostname localhost \
    --port 4444 \
    --trustAll \
    --bindDN "cn=Directory Manager" \
    --bindPassword secret \
    --backend-name userRoot \
    --index-name authzSubject \
    --set index-type:equality \
    --set index-entry-limit:1000000
opendj/bin/dsconfig  create-local-db-index \
    --hostname localhost \
    --port 4444 \
    --trustAll \
    --bindDN "cn=Directory Manager" \
    --bindPassword secret \
    --backend-name userRoot \
    --index-name authzClientID \
    --set index-type:equality \
    --set index-entry-limit:1000000

For OpenDJ 3.0:

opendj/bin/dsconfig  create-backend-index \
    --hostname localhost \
    --port 4444 \
    --trustAll \
    --bindDN "cn=Directory Manager" \
    --bindPassword secret \
    --backend-name userRoot \
    --index-name authzSubject \
    --set index-type:equality \
    --set index-entry-limit:1000000
opendj/bin/dsconfig  create-backend-index \
    --hostname localhost \
    --port 4444 \
    --trustAll \
    --bindDN "cn=Directory Manager" \
    --bindPassword secret \
    --backend-name userRoot \
    --index-name authzActor \
    --set index-type:equality \
    --set index-entry-limit:1000000
opendj/bin/dsconfig  create-backend-index \
    --hostname localhost \
    --port 4444 \
    --trustAll \
    --bindDN "cn=Directory Manager" \
    --bindPassword secret \
    --backend-name userRoot \
    --index-name authzClientID \
    --set index-type:equality \
    --set index-entry-limit:1000000

The dsconfig utility will present you with a prompt. Press f to confirm and finish:

        Property                        Value(s)
        -----------------------------------------------------------------------
    1)  attribute                       authzclientid
    2)  index-entry-limit               1000000
    3)  index-extensible-matching-rule  No extensible matching rules will be
                                        indexed.
    4)  index-type                      equality

    ?)  help
    f)  finish - create the new Local DB Index
    q)  quit

OpenDJ often requires the indices to be rebuild after a new one is added. Do that with the following command where -b specifies the root DN of the directory:

opendj/bin/rebuild-index \
    -b o=c2id \
    --rebuildAll \
    --hostname localhost \
    --port 4444 \
    --trustAll \
    --bindDN "cn=Directory Manager" \
    --bindPassword secret

7.1.3 Create directory branches

To create the necessary directory branches for the various persisted data types you can use an LDIF file like this one:

dn: ou=clients,o=c2id
objectClass: organizationalUnit
objectClass: top
ou: clients

dn: ou=authorizations,o=c2id
objectClass: organizationalUnit
objectClass: top
ou: authorizations

dn: ou=idAccessTokens,o=c2id
objectClass: organizationalUnit
objectClass: top
ou: idAccessTokens

dn: ou=revocations,o=c2id
objectClass: organizationalUnit
objectClass: top
ou: revocations

dn: ou=sessions,o=c2id
objectClass: organizationalUnit
objectClass: top
ou: sessions

dn: ou=subjects,o=c2id
objectClass: organizationalUnit
objectClass: top
ou: subjects

Use the ldapmodify command line LDAP client (or your favourite LDAP browser) to add the branches specified in the LDIF file:

opendj/bin/ldapmodify -p 1389 -D 'cn=Directory Manager' -a -f branches.ldif

7.1.4 Create user

LDAP connections from the Connect2id server should be made under a dedicated user account, to avoid using the privileged admin account. To do that create an LDIF file specifying the account DN and password.

Example LDIF file:

dn: uid=c2id-server,o=c2id
objectClass: account
objectClass: top
objectClass: simpleSecurityObject
uid: c2id-server
userPassword: secret
description: Connect2id server account

Add the account entry with ldapmodify:

opendj/bin/ldapmodify -p 1389 -D 'cn=Directory Manager' -a -f user.ldif

The password may be set or updated separately with the ldappasswordmodify command, or again with your favourite LDAP browser.

7.1.5 Give the user appropriate permissions

Create an access control instruction to give uid=c2id-server,o=c2id access to the directory branches where Connect2id server data is to be persisted.

LDIF file:

dn: o=c2id
changetype: modify
add: aci
aci: (target="ldap:///*=*,*,o=c2id")
 (version 3.0;
 acl "Connect2id server permissions";
 allow (all)
 (userdn = "ldap:///uid=c2id-server,o=c2id");)

Apply it with ldapmodify:

opendj/bin/ldapmodify -p 1389 -D 'cn=Directory Manager' -f aci.ldif

7.2 OpenLDAP

These setup instructions apply to OpenLDAP 2.4.0 and later. Its on-line configuration permits changes during runtime and uses standard LDAP client commands to make them.

7.2.1 Add required schemas

To add the required schema files from a command line on the OpenLDAP server host:

sudo ldapadd -Y EXTERNAL -H ldapi:/// -f oidc-client-schema-openldap.ldif
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f oidc-authz-schema-openldap.ldif
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f oidc-session-schema-openldap.ldif

Note that the OpenLDAP server must be online (running).

The ldapadd client utility can also access a remote OpenLDAP server. For that you need to specify the host name / IP and a bind DN / password for the configuration super user (e.g. cn=admin,cn=config).

7.2.2 Add indices

OpenLDAP can serve data from multiple backends. To create an index for a particular backend you need to know its name (DN) in the cn=config tree. OpenLDAP is typically created with a single backend using an LMDB database named {1}mdb.

You can list the available backends with ldapsearch:

sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config '(olcDatabase=*)' dn

Create an LDIF text file add-index.ldif containing instructions to add equality (eq) indices for the specified attributes (assuming Connect2id data is stored in the {1}mdb backend):

dn: olcDatabase={1}mdb,cn=config
add: olcDbIndex
olcDbIndex: authzSubject eq
olcDbIndex: authzActor eq
olcDbIndex: authzClientID eq

Apply the config changes to the OpenLDAP directory using the standard ldapmodify command:

sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f add-index.ldif

To verify that the indices have been created:

sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config '(olcDatabase={1}mdb)' olcDbIndex

The resulting listing of the indices for the backend should like this:

dn: olcDatabase={1}mdb,cn=config
olcDbIndex: objectClass eq
olcDbIndex: cn,uid eq
olcDbIndex: uidNumber,gidNumber eq
olcDbIndex: member,memberUid eq
olcDbIndex: authzSubject eq
olcDbIndex: authzActor eq
olcDbIndex: authzClientID eq

7.2.3 Create directory branches

To create the necessary directory branches for the various persisted data types you can use an LDIF file like this one:

dn: ou=clients,o=c2id
objectClass: organizationalUnit
objectClass: top
ou: clients

dn: ou=authorizations,o=c2id
objectClass: organizationalUnit
objectClass: top
ou: authorizations

dn: ou=idAccessTokens,o=c2id
objectClass: organizationalUnit
objectClass: top
ou: idAccessTokens

dn: ou=revocations,o=c2id
objectClass: organizationalUnit
objectClass: top
ou: revocations

dn: ou=sessions,o=c2id
objectClass: organizationalUnit
objectClass: top
ou: sessions

dn: ou=subjects,o=c2id
objectClass: organizationalUnit
objectClass: top
ou: subjects

Use the ldapadd command line LDAP client (or your favourite LDAP browser) to add the branches specified in the LDIF file:

sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// -f branches.ldif

7.2.4 Create user

LDAP connections from the Connect2id server should be made under a dedicated user account, to avoid using the privileged admin account. To do that create an LDIF file specifying the account DN and password.

Example LDIF file:

dn: uid=c2id-server,o=c2id
objectClass: account
objectClass: top
objectClass: simpleSecurityObject
uid: c2id-server
userPassword: secret
description: Connect2id server account

Add the account entry with ldapadd:

sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// -f user.ldif

The password may be set or updated separately with the ldappasswd command, or again with your favourite LDAP browser.

7.2.5 Give the user appropriate permissions

Connect to the configuration tree (with root cn=config) of the LDAP directory, and add new access control list (ACL) attributes to the backend configuration (e.g. olcDatabase={1}mdb,cn=config, see above), in order to give tne newly created user manage access to the directory branches where the Connect2id server data will be persisted.

Example:

olcAccess: {0}to dn.subtree="ou=clients,o=c2id"        by dn.base="uid=c2id,o=c2id" manage by * none
olcAccess: {1}to dn.subtree="ou=authorizations,o=c2id" by dn.base="uid=c2id,o=c2id" manage by * none
olcAccess: {2}to dn.subtree="ou=idAccessTokens,o=c2id" by dn.base="uid=c2id,o=c2id" manage by * none
olcAccess: {3}to dn.subtree="ou=revocations,o=c2id"    by dn.base="uid=c2id,o=c2id" manage by * none
olcAccess: {4}to dn.subtree="ou=sessions,o=c2id"       by dn.base="uid=c2id,o=c2id" manage by * none
olcAccess: {5}to dn.subtree="ou=subjects,o=c2id"       by dn.base="uid=c2id,o=c2id" manage by * none

Make sure the new ACL attributes are prefixed with a number that orders them before any more general ACL rules. To do that you may have to increment the prefixes of existing LDAP ACL rules.

8. Questions?

Get in touch with Connect2id support. We'll be delighted to help!