I am reluctant to write a post on authenticating users with ColdFusion and Active Directory trusts because I am not confident my method is the most efficient solution. However, I realized that something in this post may help another developer. Before I start, I should explain the Active Directory paradigm and the basic requirements my solution addresses.
In the image below, I’ve created a fictional diagram of a trusted Active Directory for the purpose of this article.
The Kentucky domain (b) contains the groups containers, while the USA Domain (A) contains all the user accounts. The requirements for our solution are (1) the solution must be developed in such a way legacy application can be migrated to use and (2) it must return generic information for the application to use in authentication decisions.
Using ColdFusion’s ldap command to login the USA domain as the user is fairly easy.
Coldfusion 2016 CF SCRIPT
cfldap(
action="query",
attributes="objectSID,dn,cn,title,mail,telephonenumber,memberof,l,samAccountName",
filter="(&(objectCategory=person)(objectClass=user)(sAMAccountName=#arguments.username#))",
returnAsBinary = "objectSID",
name="ADResults",
maxrows="1",
password="#arguments.password#",
port="389",
scope="subtree",
server="usa.northwind.com",
separator="|",
start="OU=USA,DC=NORTHWIND,DC=COM"",
username="USA\#arguments.username#");
This AD query returns the Distinguished Name, Common Name, Title, e-Mail address, phone number, location, username, and the groups the user is a member of the in the USA domain. Since the user is a member of groups in the Kentucky Domain, our solutions needs to query the Kentucky domain and add this information to the data returned to the application. However, user accounts from the USA domain are stored as placeholders in the Kentucky Domain. Therefore, directly querying the Kentucky domain using attributes from the USA domain will fail. The distinguished name of the place holder in the Kentucky domain are in this format:
CN=S-#-#-##-##########-##########-##########-#####,CN=ForeignSecurityPrincipals, DC=usa,DC=northwind,DC=com
The key to linking the accounts between the domains is the ObjectSID attribute. This attribute’s value is the same on both domains. However, the objectSID attribute is returned from the USA server as a binary (Fig. 2). It’s important to have ColdFusion convert the objectSID to binary by using returnAsBinary attribute on the cfldap command in your active directory queries (fig. 3).
The objectSID must be converted to Octal and each octet be escaped before we can use it in another query.
objectSID = binaryencode(QryResults.objectSID,"HEX");
I decided to return the objectSID in as a 56 character, unescaped hexadecimal string, along with other authentication information, back to the application. We must query the Kentucky domain to get the distinguished name for the objectSID of the user.
Coldfusion 2016 CF SCRIPT
cfldap(
action="query",
attributes="cn, dn",
filter="(objectSID=#EscapeHexString(objectSID)#)",
name="ADQryResults",
maxrows="1",
password="<Password>",
port="389",
scope="subtree",
server="kentucky.northwind.com",
start="OU=KENTUCKY,DC=NORTHWIND,DC=COM"",
username="<USERNAME");
Each octet of the objectSID must be escaped before it can be used. I am unaware of any ColdFusion functions to escape a hex string; therefore, I wrote the following function:
Coldfusion 2016 CF SCRIPT
private string function EscapeHexString(required string MyString) {
try {
retVal = "";
while (len(MyString) GT 0) {
retVal &= "\" & left(MyString,2);
arguments.MyString = mid(arguments.MyString, 3, len(arguments.MyString));
}
} catch (any e) {
throw(type="EscapeHexStringFailed", message="#e.message#");
}
return retVal;
}
This function returns a hex string such as, “0105000000…” as “\01\05\00\00\00…”
Finally, We can query the Kentucky domain for the groups our user is logged in.
Coldfusion 2016 CF SCRIPT
cfldap(
action="query",
attributes="CN, DN",
filter="(&(objectCategory=group)(member=#ADQryResults.dn#))",
name="qryADGroups",
password="<PASSWORD>",
port="389",
scope="subtree",
server="kentucky.northwind.com",
start="OU=KENTUCKY,DC=NORTHWIND,DC=COM"",
username="<USERNAME");
I combine the groups returned from the Kentucky domain with the groups that was returned from the USA Domain as a pipe delimited list and return all the data back to the application as JSON data.
This solution returns the following data: isAuthenicated (boolean), isDeveloper (boolean, based on a specific group membership), Distinguished Name, Common Name, ForeignSecurityPrincipals Distinguished Name, ForeignSecurityPrincipals Common Name, Title, e-Mail address, phone number, location, username, and which groups the user is a member of according to both Active Directory servers. I allow the calling application to make security decisions such as access to application, the user’s role, etc.
Lastly, its import to note that I use the credentials provided by the person logging on the USA domain to verify their username and password after which, I use service accounts to query the AD servers. It has been my experience that ColdFusion’s LDAP command doesn’t allow the user of the USA\USERNAME on the Kentucky server, even though the user has access and can connect to the AD using the “Users and Computer” application.
Hi Bryan. Nice article. I’ve been searching all over the web but have not found a solution. My problem is how to authenticate user (& password) using LDAP against Active Directory that doesn’t allow anonymous bind (must bind/connect using Service Account).
So, I can bind and connect using the Service Account and filter the actual username to verify that the user(name) is valid and exists. However, I can’t a second set of the password (the actual user password) in the CFLDAP parameters.
The username and password parameter was used for the Service Account username and Service Account password. We are using ColdFusion 2018 in an Linux environment. So far, I have googled a lot and haven’t found an solution.
We have ColdFusion support but our CF Admin (an network admin) is out this week and I’m one of the developer that’s not registered to submit a support request ticket. Do you have any idea on how to do this authenticate username and password against Active Directory that comes default with not accepting anonymous bind/connect and must use Service Account to connect. Thanks in advance for any advice and/or solution.
Hi John
If I understand your question correctly, you are asking how to authenticate someone’s username and password in ColdFusion. Let me explain the process I use.
1. The visitor is presented with a Login Form and they enter their information and press “Login” button.
2. I use their username and password to login to Active Directory; and with their credentials, I query for the necessary information the app needs such as group membership, etc. If their username and password fail, then there is an issue and they need to retry their login.
3. I cache all the necessary information into a session struct variable. For example, I use the following in the Application.cfc
public void function onSessionStart() {
…
session.PresidingJudgeDraft.authenication.isAuthorized = false;
…
session.PresidingJudgeDraft.LoginInfo = StructNew();
session..LoginInfo.cn = ”;
session..LoginInfo.dn = ”;
…
return;
}