This directory contains dynamically loaded backend providers. Backends placed here are loaded at runtime without requiring a rebuild of the application.
server/backends/
├── README.md # This file
├── template.js # Template for creating new backends
├── custom-auth.example.js # Example authentication backend
└── custom-directory.example.js # Example directory backend
- Copy the template: Start with
template.jsor an example file - Rename the file: Use a descriptive name (e.g.,
redis-auth.js) - Implement the interface: Add your backend logic
- Configure: Set
AUTH_BACKENDSorDIRECTORY_BACKENDto your backend name - Restart: The backend loads automatically on server start
Every backend must export an object with three properties:
module.exports = {
name: 'my-backend', // Unique identifier (used in .env)
type: 'auth', // 'auth' or 'directory'
provider: class MyBackend { // Provider class implementation
// Your methods here
}
};Authentication backends must extend AuthProvider and implement:
const { AuthProvider } = require('@ldap-gateway/core');
class MyAuthBackend extends AuthProvider {
constructor(options) {
super();
// Initialize with options passed from .env
}
/**
* Authenticate a user
* @param {string} username - Username to authenticate
* @param {string} password - Password to verify
* @param {Object} req - LDAP request object (optional, for context)
* @returns {Promise<boolean>} True if authenticated
*/
async authenticate(username, password, req) {
// Your authentication logic here
return true; // or false
}
}Directory backends must extend DirectoryProvider and implement:
const { DirectoryProvider } = require('@ldap-gateway/core');
class MyDirectoryBackend extends DirectoryProvider {
constructor(options) {
super();
// Initialize with options
}
/**
* Find a specific user
* @param {string} username - Username to find
* @returns {Promise<Object|null>} User object or null
*/
async findUser(username) {
// Return user object with LDAP attributes
return {
uid: username,
cn: 'Full Name',
uidNumber: 1000,
gidNumber: 1000,
homeDirectory: `/home/${username}`,
loginShell: '/bin/bash',
mail: `${username}@example.com`
};
}
/**
* Get all users
* @returns {Promise<Array>} Array of user objects
*/
async getAllUsers() {
return [];
}
/**
* Find groups (filtered)
* @param {Object|string} filter - LDAP filter object or parsed filter
* @returns {Promise<Array>} Array of group objects
*/
async findGroups(filter) {
return [];
}
/**
* Get all groups
* @returns {Promise<Array>} Array of group objects
*/
async getAllGroups() {
return [];
}
}Users should return objects with these standard LDAP attributes:
{
uid: 'username', // Login name (required)
cn: 'Full Name', // Common name (required)
sn: 'Last', // Surname (optional)
givenName: 'First', // First name (optional)
mail: 'user@example.com', // Email (optional)
uidNumber: 1000, // POSIX UID (required for SSH)
gidNumber: 1000, // POSIX GID (required for SSH)
homeDirectory: '/home/user', // Home directory (required for SSH)
loginShell: '/bin/bash', // Login shell (optional)
userPassword: '{SSHA}...' // Password hash (optional)
}Groups should return objects with:
{
cn: 'groupname', // Group name (required)
gidNumber: 1000, // POSIX GID (required)
memberUid: ['user1', 'user2'], // Array of member usernames (required)
description: 'Group desc' // Description (optional)
}Set your backend in .env:
# Use a dynamic authentication backend
AUTH_BACKENDS=my-custom-auth
# Use a dynamic directory backend
DIRECTORY_BACKEND=my-custom-directory
# Optional: Custom backend directory
BACKEND_DIR=/path/to/backendsOptions are passed to your backend constructor. They come from:
- Database service: If using database, pass
databaseService - LDAP pool: If using LDAP, pass
ldapServerPool - Custom paths: Pass any required file paths or URLs
- Environment variables: Access via
process.envin constructor
Example in your backend:
constructor(options) {
super();
this.apiUrl = process.env.MY_API_URL;
this.apiKey = process.env.MY_API_KEY;
this.db = options.databaseService; // If passed from factory
}See the example files:
custom-auth.example.js- Simple API-based authenticationcustom-directory.example.js- JSON file-based directory
Enable debug logging:
LOG_LEVEL=debugWatch for backend loading messages:
[BackendLoader] Scanning for backends in: /path/to/backends
[BackendLoader] Registered auth backend: my-backend from my-backend.js
[BackendLoader] Loaded 1 auth backends
- Use
.jsextension - Avoid
.example.jssuffix (those are skipped) - Don't name your file
template.js(skipped)
- Only load backends from trusted sources
- Validate all user input in your backend
- Use environment variables for secrets (never hardcode)
- Consider implementing rate limiting
- Backends are loaded once at startup
- Cache data when possible to reduce external calls
- Use connection pooling for databases
- Implement timeouts for external APIs
- Always use try/catch for async operations
- Return
falsefor failed authentication (don't throw) - Return empty arrays for not-found queries
- Log errors with the logger utility
Before deploying:
-
Test authentication:
ldapwhoami -x -H ldaps://localhost:636 -D "uid=test,dc=example,dc=com" -W -
Test user search:
ldapsearch -x -H ldaps://localhost:636 -b "dc=example,dc=com" "(uid=test)"
-
Test group search:
ldapsearch -x -H ldaps://localhost:636 -b "dc=example,dc=com" "(objectClass=posixGroup)"
-
Test SSH:
ssh test@localhost -p 2222
- LDAP Gateway Core Documentation
- AuthProvider Interface
- DirectoryProvider Interface
- Existing Backend Examples
If your backend isn't loading:
- Check logs for validation errors
- Ensure all required methods are implemented
- Verify the module exports structure matches the template
- Test the backend file with
node -c your-backend.js(syntax check) - Review example files for reference patterns