I am new to Azure LDAP, I am creating Nextjs Site in which user can Authenticate With their Email Password details that are created on Microsoft Entra ID (Azure Active Directory). it's Authenticate fine on local http://localhost:3000 user can able to SignIn but when I uploaded my nextjs site on digital ocean cloud hosting it is generating and error and refusing to connect and When I try to use on other laptops it also refusing to connect am I missing something or I need to generate Certificate for cloud hosting?
Here is my Code that attemp to connecting to LDAP
import { NextResponse } from "next/server";
import ldap from "ldapjs";
import fs from "fs";
import path from "path";
import mysql from "mysql2/promise";
import jwt from "jsonwebtoken";
export async function POST(req) {
if (req.method === 'POST') {
// const token = req.cookies.get("token");
// if (!token) {
// return NextResponse.json({ success: false, "error": "Authorization Failed!" }, { status: 401 })
// }
// const SECRET_KEY = process.env.JWT_SECRET || 'your_secret_key';
// const decodedToken = jwt.verify(token["value"], SECRET_KEY);
// let adminId = decodedToken.admin_id == 0 ? decodedToken.id : decodedToken.admin_id;//if already admin than it's id if
//not admin then employee's amdin id
const { username, email, password, token = '0' } = await req.json();
var db = null;
let ldapSettings = null;
try {
db = await mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
})
const [ldapSettingsData] = await db.execute(`SELECT * from ldap_settings WHERE login_token='${token}'`);
if (ldapSettingsData.length == 0) {
return NextResponse.json({ success: false, "error": "LDAP Settings not found!" }, { status: 400 })
}
if (!ldapSettingsData[0]['ldap_enabled']) {
return NextResponse.json({ success: false, "error": "LDAP not available, Contact admin for LDAP Login!" }, { status: 400 })
}
ldapSettings = ldapSettingsData;
} catch (e) {
return NextResponse.json({ success: false, "error": "Server Error Occured!" }, { status: 500 })
} finally {
if (db) {
db.destroy();
}
}
// console.log(username, email, password);
let ldapCert = null;
try {
ldapCert = path.join(process.cwd(), 'public', "certificates", (ldapSettings?.[0]?.["ldap_cert_file"] ?? "")); //Certificate file //"aadds-cert.cer"//(ldapSettings?.[0]?.["ldap_cert_file"] ?? "")
ldapCert = fs.readFileSync(ldapCert); // Adjust the path as needed
} catch (e) {
// console.log("Client Error");
// console.log(e);
return NextResponse.json({ error: "Certificate File not found!" }, { status: 404 });
}
const client = ldap.createClient({// ldap host
//If use_tls is checked than we use 389 port for startTLS
url: ldapSettings[0]['ldap_server'] + ":" + (ldapSettings[0]['use_tls'] ? "389" : "636"),
// Use LDAPS (LDAP over SSL) 636 for SSL and TLS and 389 is for STARTTLS
tlsOptions: {
ca: [ldapCert], // Add the self-signed certificate to the trusted list
//rejectUnauthorized will enable if ldap_ssl_cert_verif = 1 and LDAP SSL certificate validation enabled from settings
rejectUnauthorized: (ldapSettings?.[0]?.ldap_ssl_cert_verif != null && ldapSettings[0]['ldap_ssl_cert_verif'] ? false : true) // Set to true to reject invalid certificates in production
},
reconnect: true, // Optional: Enables auto-reconnect
});
// Handle connection errors
client.on('error', (err) => {
console.error("LDAP connection error:", err.message);
if (err.code) {
console.error("Error code:", err.code);
}
// Additional logging or actions can go here
});
// Handle successful connection
client.on('connect', () => {
console.log("Successfully connected to the LDAP server.");
});
// console.log("LDAP SERVER", ldapSettings[0]['ldap_server'] + ":" + (ldapSettings[0]['use_tls'] ? "389" : "636"));
// Start a simple bind for authentication
return new Promise((resolve) => {
/*client.bind(`uid=${username},ou=users,dc=aadds,dc=jetxxxx,dc=com`, password, (err) => {
let response = null;
console.log("err",err);
if (err) {
console.log(err);
response = NextResponse.json({ success: false, error: "Some Error Occured" }, { status: 401 });
} else {
response = NextResponse.json({ success: true, message: "Authenticated successfully" }, { status: 200 });
}
// Unbind the client after the authentication process is done
client.unbind();
resolve(response);
});*/
let response = null;
//User name and password
// console.log("User Name:", username.trim() , " Password:" + password.trim());
//Start STARTTLS if enabled from settings
// Perform STARTTLS Enabled if TLS Enabled
/*client.starttls(tlsOptions, null, (err) => {
if (err) {
console.error('Error enabling STARTTLS:', err);
client.unbind(); // Close the connection if an error occurs
}
response = NextResponse.json({ success: false, error: "STARTTLS Connection Error!" }, { status: 400 });
resolve(response); // Ensure to resolve with response here
return;
console.log('STARTTLS connection established');
});*/
/* IF Azure Active Directory is enable than ADD OU="AADDC Users" Other Wise */
// console.log("Binding With", typeof ("CN=" + username + `,"${ldapSettings[0]['is_azure_ad'] ? ldapSettings[0]['base_bind_dn'].replace(/ou=|OU=/g, "ou=AADDC ") : ldapSettings[0]['base_bind_dn']}"`), username, password);
client.bind(("CN=" + username + `,${ldapSettings[0]['is_azure_ad'] ? ldapSettings[0]['base_bind_dn'].replace(/ou=|OU=/g, "ou=AADDC ") : ldapSettings[0]['base_bind_dn']}`), password, (error) => {
// client.bind("CN=" + username + ",OU=AADDC Users,dc=aadds,dc=jetxxxxxx,dc=com", password, (error) => {
// console.log("Error: ", error);
if (error) {
// console.error("Failed")
// reject()
// console.log(error, "LDAP ERROR 101");
response = NextResponse.json({ success: false, error: "Invalid LDAP Credentials or LDAP Error Occured!" }, { status: 401 });
client.unbind();
resolve(response); // Ensure to resolve with response here
return;
} else {
// console.log("Logged in")
// resolve({
// username: credentials.username,
// password: credentials.password,
// })
// Define the search options
const opts = {
filter: ldapSettings[0]['ldap_filter'].replace(/{email}/g, email).replace(/{username}/g, username),
scope: 'sub',
attributes: [],
}
// console.log("Logged in");
let user_exists = false;
client.search("CN=" + username + `,${ldapSettings[0]['is_azure_ad'] ? ldapSettings[0]['base_bind_dn'].replace(/ou=|OU=/g, "ou=AADDC ") : ldapSettings[0]['base_bind_dn']}`, opts, (err, res) => {
if (err) {
response = NextResponse.json({ success: false, error: "Search Query Error" }, { status: 401 });
client.unbind();
resolve(response); // Ensure to resolve with response here
return;
}
res.on('searchEntry', (entry) => {
user_exists = true;
});
res.on('error', (err) => {
console.log(err);
response = NextResponse.json({ success: false, error: "User finding error!" }, { status: 401 });
client.unbind();
resolve(response);
});
res.on('end', async (result) => {
if (user_exists) {
let db = null;
try {
db = await mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
port: process.env.DB_PORT
});
const [rows] = await db.execute('SELECT id, source, admin, name from users WHERE email=? and source=?', [email, "ldap"]);
let user_id = '';
let adminId = ldapSettings[0]['admin_id'];
let user_dp = '';
let user_name = '';
if (rows.length === 0) {
//User not exists in the system, Add New User //admin id will the id whose id use for intergration with LDAP
await db.execute("INSERT INTO users SET admin=?, name=?, email=?, source=?, role=?", [adminId, username, email, 'ldap', 'employee']).then(([result]) => {
user_id = result.insertId; // Get the inserted ID
});
user_name = username;
} else {
//User already exists in the system
user_id = rows[0]['id'];
adminId = rows[0]['admin'];
user_dp = (rows[0]?.dp ?? "");
user_name = (rows[0]?.name ?? "");
}
const SECRET_KEY = process.env.JWT_SECRET || 'your_secret_key';
// JWT token generation
const token = jwt.sign(
{ id: user_id, admin_id: adminId, name: user_name, email: email, source: "LDAP", dp: (user_dp ?? ''), role: 'employee'}, // Payload: user data
SECRET_KEY, // Secret key
{ expiresIn: process.env.JWT_TOKEN_EXPIRES || '24h' } // Token expiration time
);
// Set token in a cookie (httpOnly and secure for better security)
const response = NextResponse.json({
message: 'Authenticated successfully!',
}, { status: 200 });
response.cookies.set('token', token, {
httpOnly: true, // Prevent client-side access to the token
maxAge: process.env.COOKIES_EXPIRES_SECONDS || (24 * 60 * 60), // 1 hour expiration
path: '/',
});
client.unbind();
resolve(response);
} catch (error) {
console.error('Error logging in:', error);
response = NextResponse.json({ error: 'Error Occured within Server!' }, { status: 500 });
client.unbind();
resolve(response);
} finally {
if (db) {
db.destroy();
}
}
// response = NextResponse.json({ success: true, message: "Authenticated successfully!", status: result.status }, { status: 200 });
} else {
response = NextResponse.json({ success: false, error: "User not found!" }, { status: 401 });
}
client.unbind();
resolve(response); // Resolve response here if an error occurs
});
});
// response = NextResponse.json({ success: true, message: "Authenticated successfully" }, { status: 200 });
}
});
});
} else {
// console.log("Only POST requests are allowed");
return NextResponse.json({ error: 'Only POST requests are allowed' }, { status: 405 });
}
}
Getting Error refuse to connect
Azure LDAP Authentication Connection Refused On Cloud Server or Other Desktops
The issue is related to network access, certificates, or configuration.
Localhost works because the network allows access to the LDAP server. check that the DigitalOcean server or other devices can connect to the LDAP server.
Confirm the LDAP server’s firewall rules allow connections from the IP address of the DigitalOcean host. Contact the IT/network team to whitelist the public IP of the DigitalOcean server.
Test connectivity to the LDAP server from the DigitalOcean server using a tool like telnet
or openssl
:
telnet your-ldap-server 389
openssl s_client -connect your-ldap-server:636
For secure connections (port 636 or STARTTLS on port 389), the LDAP server requires a trusted certificate. If the LDAP server uses a self-signed certificate, you must include the certificate on the DigitalOcean server. check the code correctly loads the certificate via tlsOptions
:
tlsOptions: {
ca: [fs.readFileSync('/path/to/certificate.pem')],
rejectUnauthorized: true,
}
Adding user to the database: