Extending OpenLDAP: Implementing Custom TOTP Secret Schemas

A Deep Dive into OIDs, Auxiliary Classes, and UUID-based Identification

In modern infrastructure, the Directory Service (LDAP) often serves as the “Source of Truth” for authentication. However, when implementing Multi-Factor Authentication (MFA/TOTP), we frequently encounter a limitation: the standard LDAP schema does not have a dedicated field for TOTP secrets.

This article documents the process of safely extending the OpenLDAP schema using a globally unique approach, ensuring scalability and professional-grade data organization.


1. The Foundation: Understanding OIDs

Every element in an LDAP directory must be uniquely identified by an Object Identifier (OID). Think of an OID as a global, immutable DNA sequence for data structures.

Why not just use a name?

LDAP is a distributed protocol. If two different organizations created an attribute named totpSecret with different technical specifications, integrating their systems would cause a collision. An OID (e.g., 2.25.x...) prevents this.

The Strategy: IANA vs. UUID

While many organizations apply for a Private Enterprise Number (PEN) at IANA (under the 1.3.6.1.4.1 arc), this process can take weeks. For immediate, technically sound implementation, we use the UUID-to-OID mapping (2.25 arc).

https://www.iana.org/assignments/enterprise-numbers

Generating a collision-free OID via Python:

Python

python3 -c "import uuid; print('2.25.' + str(uuid.uuid4().int))"

2.25.268946279408571954743200031670960925569


2. Architecting the Schema: Attributes and Classes

An LDAP extension requires two components: an Attribute Type (the field) and an Object Class (the container).

The Attribute: totpSecret

We define the secret as a SINGLE-VALUE string. Using the caseIgnoreMatch equality ensures robust searching, while the syntax 1.3.6.1.4.1.1466.115.121.1.15 (Directory String) allows for Base64 encoded secrets.


[ OID: 2.25.268946279408571954743200031670960925569 ] <-- Your unique "Company ID" in the world. |
+--> [ .1 ] Attribute: devopsdbSecrets
| (Where do you save the Base64 code?)
|
+--> [ .2 ] ObjectClass: totpUser (AUXILIARY)
(The "folder" authorizing the use of totpSecret)

The Class: devopsdbSecrets

Instead of creating a “Fixed” user type, we define an AUXILIARY class.

  • Why Auxiliary? It acts as a “plug-in.” You can attach it to any existing user (whether they are a person, posixAccount, or inetOrgPerson) without altering their primary identity.
DN: cn=Fausto Branco,ou=UserGroups,dc=ldap,dc=devops-db,dc=info
|
+-- ObjectClass: top
+-- ObjectClass: person
+-- ObjectClass: organizationalPerson
+-- ObjectClass: inetOrgPerson <-- (Main Class: Name, Email, Password)
|
+-- ObjectClass: devopsdbSecrets <-- (Your Auxiliary Class added)
|
+-- Atributo: totpSecret: "R0VUVEhJU0NPREU=" (O valor em Base64)

ldapsearch -x -H ldap://ldap.devops-db.info:389 \
  -b "cn=Fausto Branco,ou=UserGroups,dc=ldap,dc=devops-db,dc=info" \
  -s base objectClass
# extended LDIF
#
# LDAPv3
# base <cn=Fausto Branco,ou=UserGroups,dc=ldap,dc=devops-db,dc=info> with scope baseObject
# filter: (objectclass=*)
# requesting: objectClass
#

# Fausto Branco, UserGroups, ldap.devops-db.info
dn: cn=Fausto Branco,ou=UserGroups,dc=ldap,dc=devops-db,dc=info
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: devopsdbSecrets

3. Implementation: The LDIF Workflow

Using the vi editor—the standard for Unix systems—we create the Schema definition. Crucial Tip: In LDIF files, line continuations must start with a leading space.

Step 1: Injecting the Schema

We use the SASL/EXTERNAL mechanism over a Unix socket (ldapi:///). This allows the system root to modify the configuration (cn=config) without needing a separate LDAP password.

Bash

add_totp_schema.ldif

# Schema definition using arc 2.25 (UUID)
dn: cn=devops-schema,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: devops-schema
olcAttributeTypes: ( 2.25.268946279408571954743200031670960925569.1
  NAME 'totpSecret'
  DESC 'TOTP Secret encoded in Base64'
  EQUALITY caseIgnoreMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE )
olcObjectClasses: ( 2.25.268946279408571954743200031670960925569.2
  NAME 'devopsdbSecrets'
  DESC 'Auxiliary class for DevOps-DB infrastructure secrets'
  SUP top
  AUXILIARY
  MAY totpSecret )

Apply:

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

Step 2: Modifying the User

Targeting a user requires their Full Distinguished Name (DN). Even if you know their uid, LDAP modification operations require the exact path in the tree.

The Update LDIF (update_user_totp.ldif):

LDIF

dn: cn=Fausto Branco,ou=UserGroups,dc=ldap,dc=devops-db,dc=info
changetype: modify
add: objectClass
objectClass: devopsdbSecrets
-
add: totpSecret
totpSecret: VFJLUVdXWlVDQjJaSzZXTUFKUTdHVkpOVlZOR0hXRjQ=

4. Verification: Ensuring Data Integrity

To validate the work, we perform a scoped search. If the schema was applied correctly, the server will return both the auxiliary class and the custom attribute.

Bash

ldapsearch -x -H ldap://ldap.devops-db.info:389 \
  -b "cn=Fausto Branco,ou=UserGroups,dc=ldap,dc=devops-db,dc=info" \
  -s base "totpSecret" "objectClass"
  
  
# extended LDIF
#
# LDAPv3
# base <cn=Fausto Branco,ou=UserGroups,dc=ldap,dc=devops-db,dc=info> with scope baseObject
# filter: (objectclass=*)
# requesting: totpSecret objectClass
#

# Fausto Branco, UserGroups, ldap.devops-db.info
dn: cn=Fausto Branco,ou=UserGroups,dc=ldap,dc=devops-db,dc=info
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: devopsdbSecrets
totpSecret: VFJLUVdXWlVDQjJaSzZXTUFKUTdHVkpOVlZOR0hXRjQ=

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

Conclusion

Extending OpenLDAP is a powerful way to centralize security infrastructure. By using OIDs under the 2.25 arc and designing auxiliary classes, we create a flexible, standards-compliant environment ready for modern MFA requirements.

Next Steps: Implement Access Control Lists (ACLs) to ensure that totpSecret is only readable by the owner and the administrative service, preventing unauthorized exposure of MFA seeds.

Encoding: TOTP secrets should be stored in Base64 to handle non-standard character sets safely across different LDAP clients.