# LDAP to OAuth2 Bridge

A lightweight LDAP server that authenticates users against an OAuth2/OIDC provider (such as Keycloak). This bridge allows legacy applications that only support LDAP authentication to work with modern OAuth2 identity providers.

## Overview

This bridge presents an LDAP interface to applications while performing OAuth2 Resource Owner Password Credentials (ROPC) flow authentication against your identity provider in the background.

**Use Case**: Integrate applications like TheLounge IRC client, which only support LDAP authentication, with Keycloak or other OAuth2/OIDC providers.

## Features

- Lightweight Node.js implementation
- Simple LDAP bind operation support
- OAuth2 Resource Owner Password Credentials flow
- Configurable base DN
- Easy integration with Keycloak and other OIDC providers
- FreeBSD service support (includes rc.d script)

## Requirements

- Node.js 14+ and npm
- An OAuth2/OIDC provider (Keycloak, Auth0, etc.)
- A client configured with Direct Access Grants enabled

## Installation

### 1. Clone the repository

```bash
git clone https://your-gitea-instance.com/ldap-to-oauth2.git
cd ldap-to-oauth2
```

### 2. Install dependencies

```bash
npm install
```

### 3. Configure OAuth2 Provider

#### For Keycloak:

1. Create a new client in your realm
2. Set **Access Type** to `confidential`
3. Enable **Direct Access Grants Enabled**
4. Save and retrieve the client secret from the Credentials tab

#### For other providers:

Ensure your OAuth2 provider supports the Resource Owner Password Credentials grant type.

### 4. Configure the bridge

Edit `server.js` and update the configuration section:

```javascript
const LDAP_PORT = 3893;
const KEYCLOAK_URL = 'https://your-keycloak-domain.com';
const KEYCLOAK_REALM = 'your-realm';
const KEYCLOAK_CLIENT_ID = 'your-client-id';
const KEYCLOAK_CLIENT_SECRET = 'your-client-secret';
const BASE_DN = 'dc=example,dc=com';
```

### 5. Run the server

```bash
node server.js
```

## Testing

Test authentication using `ldapsearch`:

```bash
ldapsearch -H ldap://localhost:3893 \
  -D "cn=username,dc=example,dc=com" \
  -W \
  -b "dc=example,dc=com"
```

Replace `username` with a valid username from your OAuth2 provider.

## FreeBSD Service Installation

### 1. Copy files to system directories

```bash
# Copy application files
cp -r ldap-to-oauth2 /usr/local/ldap-oauth-bridge

# Copy RC script
cp ldap_oauth_bridge.rc /usr/local/etc/rc.d/ldap_oauth_bridge
chmod +x /usr/local/etc/rc.d/ldap_oauth_bridge
```

### 2. Enable and start service

```bash
echo 'ldap_oauth_bridge_enable="YES"' >> /etc/rc.conf
service ldap_oauth_bridge start
```

### 3. Check service status

```bash
service ldap_oauth_bridge status
sockstat -l | grep 3893
```

## Configuration Examples

### TheLounge IRC Client

In `config.js`:

```javascript
ldap: {
    enable: true,
    url: "ldap://your-server:3893",
    baseDN: "dc=example,dc=com",
    searchDN: {
        rootDN: "cn=admin,dc=example,dc=com",
        rootPassword: "dummy",
        filter: "(|(uid=%s)(cn=%s))"
    }
}
```

### Generic LDAP Application

- **LDAP Server**: `ldap://your-server:3893`
- **Base DN**: `dc=example,dc=com`
- **Bind DN Format**: `cn=USERNAME,dc=example,dc=com`

Users authenticate with their OAuth2 provider username and password.

## How It Works

1. Application sends LDAP BIND request with username and password
2. Bridge extracts username from the LDAP DN (e.g., `cn=alice,dc=example,dc=com` → `alice`)
3. Bridge performs OAuth2 password grant flow with the identity provider
4. If OAuth2 authentication succeeds, LDAP BIND succeeds
5. If OAuth2 authentication fails, LDAP BIND fails

## Security Considerations

- **Transport Security**: Use LDAPS (LDAP over TLS) in production or ensure the bridge runs on a trusted network
- **Port < 1024**: Requires root privileges on Unix systems. Consider using port 3893 or higher to avoid running as root
- **Password Grant Flow**: The Resource Owner Password Credentials flow passes passwords to the bridge. Ensure secure communication channels
- **Network Isolation**: Run the bridge on the same network as your applications or use VPN/tunneling

## Limitations

- Only supports LDAP BIND operations (authentication only)
- Does not provide a full LDAP directory (no user enumeration, group queries, etc.)
- Search operations return empty results
- Requires OAuth2 provider to support Resource Owner Password Credentials grant

## Troubleshooting

### Authentication fails

1. Check server logs for detailed error messages
2. Verify OAuth2 client credentials are correct
3. Ensure Direct Access Grants are enabled in your OAuth2 client
4. Test OAuth2 authentication directly using curl:

```bash
curl -X POST https://your-keycloak/realms/your-realm/protocol/openid-connect/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=password" \
  -d "client_id=your-client-id" \
  -d "client_secret=your-client-secret" \
  -d "username=testuser" \
  -d "password=testpass"
```

### Connection refused

- Verify the server is running: `sockstat -l | grep 3893`
- Check firewall rules
- Ensure the application can reach the bridge server

### DN parsing errors

- Ensure username is formatted as `cn=username,dc=example,dc=com` or `uid=username,dc=example,dc=com`
- Check server logs for DN parsing details

## License

MIT License - See LICENSE file for details

## Contributing

Contributions are welcome! Please submit pull requests or open issues on the repository.

## Author

Created for use with TheLounge IRC client and Keycloak authentication.

## Acknowledgments

- Built with [ldapjs](https://github.com/ldapjs/node-ldapjs)
- OAuth2 requests powered by [axios](https://github.com/axios/axios)
