Link to home
Start Free TrialLog in
Avatar of paultran00
paultran00Flag for United States of America

asked on

coldfusion authenticate using active directory ldap

Hi experts.  I am using Coldfusion 5 on IIS6 server and SQL SERVER 2000.

Question: In the past, I would have a table of username and passwords and i would have a login page that queries the database for the username and password to allow a user to logon.  But this time, instead of a having a login form, I would like the workstation use the network login and authenticate in Active DIrectory.

Please show me a full code example that will:
1) authenticate a user to Active Directory.
2) if user is authenticated, then redirect them to the application's main page, else redirect them to a page that says that they can't be authenticated so contact an administrator.

Thank you.
Avatar of Big Monty
Big Monty
Flag of United States of America image

i'm not much of a coldfusion guy, but this article demonstrates how to connect via AD using ldap:

http://www.oxalto.co.uk/2011/09/cfwheels-active-directory-ldap-authentication/

If you're not comfortable doing the coding yourself, you may want to hire someone to do it for you, either here through the "Hire Me" link on an EE members profile, or through some other site. If you want to do it yourself, please give us your attempt so far.
ASKER CERTIFIED SOLUTION
Avatar of dgrafx
dgrafx
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of paultran00

ASKER

TO DGRAFX:

I created 2 files a form and an action page.  When I run it and enter my username and password, it is not authenticating and I get the message "Get out foul beast!".

QUESTION: don't I have to pass in a service account and its password in order to query the Active Directory?



1. ldap_authenticate4a.cfm

<cfoutput>        
      <form action="ldap_authenticate4b.cfm" method="POST">        
            <p>Enter a your login and pwd to see if you authenticate        
            <p>Username <input type="Text" name="username" <cfif (IsDefined("form.username") AND form.username is not "")>value="#form.username#"</cfif>>        
            <br>password<input type="password" name="password"             <cfif (IsDefined("form.password") AND form.password is not "")>value="#form.password#"</cfif>>        
            <br><input type="Submit" value="Login" name="">      
      </form>  
</cfoutput>

2. ldap_authenticate4b.cfm

<cfparam name="logindomain" default="shc.org">  
<cfparam name="ldapServer" default="XX.XX.XX.XX:XXX">  
<cfparam name="dcStart" default="DC=shc,DC=org">    
<cfif IsDefined("form.username") AND form.username is not "" AND IsDefined("form.password") AND form.password is not "">         
      <cftry>  
            <cfset isValid=0>                
            <cfldap action="QUERY"                        
                        name="Results"                        
                        server="#ldapServer#"                        
                        start="#dcStart#"  
                        filter="(&(objectclass=user)(SamAccountName=#form.username#))"
                        username="#logindomain#\#form.username#"                        
                        password="#form.password#"                  
                        attributes="sAMAccountName">
                        <cfif results.recordcount is 1>
                        <cfset isValid=1>
                    </cfif>
          <cfcatch>
                <cfset isValid=0>
            </cfcatch>
      </cftry>  
</cfif>    


<cfoutput>        
      <cfif isValid>                
            <p>You are authenticated</p>
      <cfelse>                
            <p>Get out foul beast!  </p>      
      </cfif>      
</cfoutput>
Depends on the server setup - sounds like you don't allow public queries ...
In this case then yes you need to supply your username & password (authenticated username & password) but keep #form.username# in the filter.
There would be an additional step for this scenario which would be to test the validity of the password.
A DB query is used in conjunction with the ldap query.
So you'd say something like this :

<cftry>  
            <cfset isValid=0>                
            <cfldap action="QUERY"                        
                        name="Results"                        
                        server="#ldapServer#"                        
                        start="#dcStart#"  
                        filter="(&(objectclass=user)(SamAccountName=#form.username#))"
                        username="#logindomain#\#authenticatedusername#"                        
                        password="#authenticatedpassword#"                  
                        attributes="sAMAccountName">
                        <cfif results.recordcount is 1>
                        <cfset isValid=1>
                    </cfif>
          <cfcatch>
                <cfset isValid=0>
            </cfcatch>
      </cftry>  

<cfif isValid>
<cfquery name="getUser">
select username
from users_table
where username '#form.username#'
and password = '#form.password#'
</cfquery>
<cfif getUser.recordcount is 1>
    <cfset isValid=1>
<cfelse>
    <cfset isValid=0>
</cfif>
</cfif>

<cfif isValid>                
<p>You are authenticated</p>
<cfelse>                
<p>Get out foul beast!  </p>      
</cfif>
Getting closer but still doesn't work so this time I changed these lines:

<cfparam name="logindomain" default="slhnaz.org">     (I changed the domain from shc.org to slhnaz.org because I can ping the domain controller in the domain slhnaz.org successfully)

<cfparam name="dcStart" default="dc=slhnaz,dc=org">  


I added this line:
          port="XXX"


But when I run it, I get this message to display the isValid value:

    The IsValid value=0
    Get out foul beast!

======================
<cfparam name="logindomain" default="slhnaz.org">  
<cfparam name="ldapServer" default="mydomaincontroller_name">  
<cfparam name="dcStart" default="dc=slhnaz,dc=org">    
<cfif IsDefined("form.username") AND form.username is not "" AND IsDefined("form.password") AND form.password is not "">         
      <cftry>  
            <cfset isValid=0>                
            <cfldap action="query"                        
                        name="Results"                        
                        server="#ldapServer#"                        
                        start="#dcStart#"  
                        filter="(&(objectclass=user)(SamAccountName=#form.username#))"
                        username="#logindomain#\svcXXX"                        
                                                                      password="pwdXXX"                  
                        attributes="sAMAccountName">
                        port="XXX"
                        <cfif results.recordcount is 1>
                        <cfset isValid=1>
                    </cfif>
          <cfcatch>
                <cfset isValid=0>
            </cfcatch>
      </cftry>  
</cfif>    


<cfoutput>      
      The IsValid value=#isValid#
      
      <cfif isValid>
      <cfquery name="getUser">
      select username
      from users_table
      where username '#form.username#'
      and password = '#form.password#'
      </cfquery>
      <cfif getUser.recordcount is 1>
          <cfset isValid=1>
      <cfelse>
          <cfset isValid=0>
      </cfif>
      </cfif>
 

      <cfif isValid>                
            <p>You are authenticated</p>
      <cfelse>                
            <p>Get out foul beast!  </p>      
      </cfif>      
</cfoutput>
TO DGRAFX:

I think I found the problem, the password has a # symbol in the middle of it.  How do I pass the literal symbol for #

password="#authenticated#password#"
1. i wouldn't use cfparam for those settings - just set them if that is what they are
i.e. instead of <cfparam name="domain" default="xyz"> use <cfset domain="xyz">

2. you'll need to ask someone besides me what your domain is and what your server is and what your start is - they can easily be much different ...
are you at a company with some network guys that you can ask?
I'm not using a cfparam for the password, it currently looks like this with only 1 # symbol in the middle:

password="authenticated#password"
Error Occurred While Processing Request  
Invalid CFML construct found on line 16 at column 39.  
ColdFusion was looking at the following text:
\"

The CFML compiler was processing:

An expression that began on line 16, column 30.
The expression might be missing an ending #, for example, #expr instead of #expr#.
The tag attribute password, on line 16, column 17.
A cfldap tag beginning on line 10, column 18.
A cfldap tag beginning on line 10, column 18.
A cfldap tag beginning on line 10, column 18.
for the number sign (#) you need to simply do ## - that will translate to #

but if this is your password: "#authenticated#password#"
you'll need to do <cfset password="##authenticated##password##"> and in the cfldap tag use password="#password#"

good luck ...
TO DGRAFX:

I got it to authenticate by making the following line look like this:                 username="myuser@#logindomain#"                          

QUESTION: This code only checks that myuser is in Active Directory and not the password because when I enter anything for a password, it returns isValid=1.  
1) How do I get it to also check the password?    
2)  how do I obtain the username that the user is logged into as from AD  
3) This example uses a login form to get the username and password from a user then checks AD; Ideally because a user is already logged into a workstation, I would like to just check AD and return the username that the user is using.
 

Code now looks like this:

<cfparam name="logindomain" default="shc.org">  
<cfparam name="ldapServer" default="svdc01">  
<cfparam name="dcStart" default="dc=shc,dc=org">    
<cfif IsDefined("form.username") AND form.username is not "" AND IsDefined("form.password") AND form.password is not "">         
      <cftry>  
            <cfset isValid=0>                
            <cfldap action="query"                        
                        name="Results"                        
                        server="#ldapServer#"                        
                        start="#dcStart#"  
                        filter="(&(objectclass=user)(SamAccountName=#form.username#))"
                        username="myuser@#logindomain#"                        
                                                                   password="mypassword"                  
                        attributes="sAMAccountName">
                        port="389"
                        <cfif results.recordcount is 1>
                        <cfset isValid=1>
                    </cfif>
          <cfcatch>
                <cfset isValid=0>
            </cfcatch>
      </cftry>  
</cfif>    


<cfoutput>      
      The IsValid value=#isValid#
      
<!---
      <cfif isValid>
      <cfquery name="getUser">
      select username
      from users_table
      where username '#form.username#'
      and password = '#form.password#'
      </cfquery>
      <cfif getUser.recordcount is 1>
          <cfset isValid=1>
      <cfelse>
          <cfset isValid=0>
      </cfif>
      </cfif>
 --->
 

      <cfif isValid>                
            <p>You are authenticated</p>
      <cfelse>                
            <p>Get out foul beast!  </p>      
      </cfif>      
</cfoutput>
TO DGRAFX:

4) How do I know from what AD returns if the user account is still active and not disabled?
you can't check password from AD - that's the reason for the workaround ...
is there some reason why your company is not allowing authenticated people - no just admins - to query ldap from a form?
they wouldn't be able to glean any important info.
then you can simply do what I said in my first post - which is to query ldap with the users username & password and if the recordcount is 1 then they are valid and if not 1 (including if error because of invalid creds then they are not valid).

but anyway - whenever users / passwords are created / edited the info (specifically the password) needs to be stored in a DB at the same time it is entered into ldap - so you can query for it like for logins ...

hope that clears things up
AD returns the login used in the field sAMAccountName.  Question is how do I assign sAMAccountName value to a variable in coldfusion so I can use it elsewhere in the code?
#results.sAMAccountName#

but if thats what you are matching against - look at your filter attribute - then it will be the same as your form.username - right ?
(sAMAccountName=#form.username#) this is part of your query that you are matching on - get it?
so in this case sAMAccountName will always be the same as form.username IF the query returns a result ...
4) How do I know from what AD returns if the user account is still active and not disabled?
i think you are misunderstanding what you are querying for ...
let me ask you this: what query are you using to look for an ldap record?
1) Query 1:  The login form asks a user for a username and password.  But the LDAP query only checks if a username is in AD but I don't know if they are really who they say they are because anyone can enter a username in the login screen; I need to check the password too so I know who is actually logged in.  

2) Query 2 (see below) that you sent me will use the  info from step 1 #results.sAMAccountName#
to search my application for this username to determine what permissions they have.

<!---
      <cfif isValid>
      <cfquery name="getUser">
      select username
      from users_table
      where username '#form.username#'
      and password = '#form.password#'
      </cfquery>
      <cfif getUser.recordcount is 1>
          <cfset isValid=1>
      <cfelse>
          <cfset isValid=0>
      </cfif>
      </cfif>
 --->
step 2 is where the password is matched - it's a 2 step process - for the reasons i explained above.
it sounds like you already have a users table - you just need to write some code that updates both ldap and your DB.
Step2 you sent me checks the password in a local table; but the whole point of doing this AD Aware is that the user has a single signon using Active Directory instead of having to remember another pasword stored in a local table.  

I would only use the Step2 you sent me to match the username from AD to the username in the local table to determine what they can do in  my application.

---------------------------

AD has a field named userPassword but it is hidden.

Can the following line be modified to include the password so I can authenticate the user?

    filter="(&(objectclass=user)(SamAccountName=#form.username#))"
i've already told you the answers you seek but you keep circling around thinking that I'm not understanding (or something - maybe hiding the info from you) ...

and i'm not referring to a DIFFERENT password in the DB - this is the SAME password! This is a SINGLE sign on!
I don't know where you got "having to remember another password" when I said multiple times that you need to sync the 2 as far as username / password.
to the user logging in it is seamless - you and your code are the only ones that know that you are checking a DB as well as AD ...
Yes!  It's working now.  When I was trying to troubleshoot earlier, I hardcoded in the username and password but now I changed them back to a variable.  Thank you so much for your help.


<cfparam name="logindomain" default="shc.org">  
<cfparam name="ldapServer" default="xxxxx">  
<cfparam name="dcStart" default="dc=shc,dc=org">    
<cfif IsDefined("form.username") AND form.username is not "" AND IsDefined("form.password") AND form.password is not "">         
      <cftry>  
            <cfset isValid=0>                
            <cfldap action="query"                        
                        name="Results"                        
                        server="#ldapServer#"                        
                        start="#dcStart#"  
                        filter="(&(objectclass=user)(SamAccountName=#form.username#))"
                        username="#form.username#@#logindomain#"                        
                                                                   password="#form.password#"
                        attributes="sAMAccountName">
<!---
                        port="389"
 --->
                        <cfif results.recordcount is 1>
                        <cfset isValid=1>
                    </cfif>
          <cfcatch>
                <cfset isValid=0>
            </cfcatch>
      </cftry>  


<cfoutput>      
      <cfif isValid>
            The IsValid value=#isValid# , username=#results.sAMAccountName#
            <p>You are authenticated</p>
      <cfelse>                
            <p>Get out foul beast!  </p>      
      </cfif>      
            
<!---
      <cfif isValid>
      <cfquery name="getUser">
      select username
      from users_table
      where username '#form.username#'
      and password = '#form.password#'
      </cfquery>
      <cfif getUser.recordcount is 1>
          <cfset isValid=1>
      <cfelse>
          <cfset isValid=0>
      </cfif>
      </cfif>
 --->
 

</cfoutput>



<cfelse>
      <p>Username or Password is incorrect.</p>
</cfif>
The solution needs a line changed in order to work:

from this:   username=username="mydomain.com\#form.username#"

to this:        username="#form.username#@#logindomain#"
Glad you got it working!

good luck ...
So, I got it working on my workstation which has Coldfusion 9 installed.

However, when I ran it on the production server with the older Coldfusion 5, it did not authenticate and I got the message "Get out foul beast!"
the first thing i'd look at is the server and the start and what about your authenticated username & password?
I copied the 2 files (ldap_authenticate4a.cfm, ldap_authenticate4b.cfm)  to the production server.
How do I know if Coldfusion 5 has the function cfldap ?
by server i mean server attribute - ya know server="ldap://xxx.xx.x.xx"
yes - cf5 has cfldap
might want to research it for differences ...
I am still having problems running the code in the production server with cf5.
1. is there a document that says cf5 has cfldap?

2.  In the action page ldap_authenticateb.cfm, I changed the following but it did not work  (the error is below the code).  
username="#form.username#@#logindomain#"  to username="#logindomain#\#form.username#"


<cfparam name="logindomain" default="shc.org">  
<cfparam name="ldapServer" default="XXXXXX">  
<cfparam name="dcStart" default="dc=shc,dc=org">    
<cfif IsDefined("form.username") AND form.username is not "" AND IsDefined("form.password") AND form.password is not "">         
      <cftry>  
            <cfset isValid=0>                
            <cfldap action="query"                        
                        name="Results"                        
                        server="#ldapServer#"                        
                        start="#dcStart#"  
                        filter="(&(objectclass=user)(SamAccountName=#form.username#))"
<!---
                        username="#form.username#@#logindomain#"                        
 --->
                        username="#logindomain#\#form.username#"
                                                                  password="#form.password#"
                        attributes="sAMAccountName">
<!---
                        port="389"
 --->
                        <cfif results.recordcount is 1>
                        <cfset isValid=1>
                    </cfif>
          <cfcatch>
                <cfset isValid=0>
            </cfcatch>
      </cftry>  


<cfoutput>      
      <cfif isValid>
            The IsValid value=#isValid# , username=#results.sAMAccountName#
            <p>You are authenticated</p>
      <cfelse>                
            <p>Get out foul beast!  </p>      
      </cfif>      
            
<!---
      <cfif isValid>
      <cfquery name="getUser">
      select username
      from users_table
      where username '#form.username#'
      and password = '#form.password#'
      </cfquery>
      <cfif getUser.recordcount is 1>
          <cfset isValid=1>
      <cfelse>
          <cfset isValid=0>
      </cfif>
      </cfif>
 --->
 

</cfoutput>



<cfelse>
      <p>Username or Password is incorrect.</p>
</cfif>    


-----------

ERROR:

Error Diagnostic Information
Just in time compilation error

Invalid token found on line 12 at position 1. ColdFusion was looking at the following text:

<
Invalid expression element. The usual cause of this error is a misspelling in the expression text.
The last successfully parsed CFML construct was a CFLDAP tag occupying document position (7:3) to (7:9).

The specific sequence of files included or processed is:
D:\Inetpub\wwwroot\PhysiciansPreference\ldap_authenticate4b.cfm      


Date/Time: 08/01/14 08:39:03
Browser: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; BTRS28059; GTB7.5; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 1.1.4322; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; MS-RTC LM 8; MS-RTC EA 2)
Remote Address: 167.94.20.73
HTTP Referrer: http://intranet/PhysiciansPreference/ldap_authenticate4a.cfm
1. CF 5 docs: https://www.adobe.com/support/documentation/en/coldfusion/documentation50.html

2. I'm hoping you realize that you can't comment out code within a ColdFusion statement like it looks like you are doing. Plus when the error says "invalid token" ...
Thanks.

I removed the comment code within a coldfusion statment so it now looks like this but I don't understand why it works on my workstation which has IIS and Coldfusion9 installed but it doesn't run in production with the older Coldfusion5.  

This time, I replaced hostname with IP in the 2nd line for the variable  ldapServer.

<cfparam name="logindomain" default="shc.org">  
<cfparam name="ldapServer" default="xx.xx.xx.xx">  
<cfparam name="dcStart" default="dc=shc,dc=org">    
<cfif IsDefined("form.username") AND form.username is not "" AND IsDefined("form.password") AND form.password is not "">         
      <cftry>  
            <cfset isValid=0>                
            <cfldap action="query"                        
                        name="Results"                        
                        server="#ldapServer#"                        
                        start="#dcStart#"  
                        filter="(&(objectclass=user)(SamAccountName=#form.username#))"
                        username="#form.username#@#logindomain#"    
                                                                   password="#form.password#"
                        attributes="sAMAccountName">
                        <cfif results.recordcount is 1>
                        <cfset isValid=1>
                    </cfif>
          <cfcatch>
                <cfset isValid=0>
            </cfcatch>
      </cftry>  


<cfoutput>      
      <cfif isValid>
            The IsValid value=#isValid# , username=#results.sAMAccountName#
            <p>You are authenticated</p>
      <cfelse>                
            <p>Get out foul beast!  </p>      
      </cfif>      
            
<!---
      <cfif isValid>
      <cfquery name="getUser">
      select username
      from users_table
      where username '#form.username#'
      and password = '#form.password#'
      </cfquery>
      <cfif getUser.recordcount is 1>
          <cfset isValid=1>
      <cfelse>
          <cfset isValid=0>
      </cfif>
      </cfif>
 --->
 

</cfoutput>



<cfelse>
      <p>Username or Password is incorrect.</p>
</cfif>
again - i can't answer for you what your server setting should be - you must have some network people around there who can provide you all the particulars including port ...

By not working - are you saying you are not able to login when the credentials are correct or are you saying you are erroring?
I notice that you are logging in as form.username and form.password instead of an authenticated login - is that your intention?

And have you checked with network people what the correct form of the username param should be?
production uses the same domain and ldap servers
To dgrafx:

I got the code to work on the production server.  Here's what's also needed in the cfldap:

scope="subtree"
rebind="Yes"

so that it now looks like this:



So I would like to move on to the 2nd part of my task: How to secure the form so that it's not sending clear text to the IIS6 server with Coldfusion 5.  In the cfldap, I tried using secure="CFSSL_BASIC" and I tried both port="389"   and port="636" but it does NOT work because I get the message "Get out foul beast!":

<cfparam name="logindomain" default="shc.org">  
<cfparam name="ldapServer" default="XXXXXX">  
<cfparam name="dcStart" default="dc=shc,dc=org">    
<cfif IsDefined("form.username") AND form.username is not "" AND IsDefined("form.password") AND form.password is not "">         
      <cftry>  
            <cfset isValid=0>                
            <cfldap action="query"                        
                        name="Results"                        
                        server="#ldapServer#"                        
                        start="#dcStart#"  
                        filter="(&(objectclass=user)(SamAccountName=#form.username#))"
                        username="#form.username#@#logindomain#"                        
                password="#form.password#"
                        attributes="sAMAccountName"
                        scope="subtree"
                        rebind="Yes">
                        <cfif results.recordcount is 1>
                        <cfset isValid=1>
                    </cfif>
          <cfcatch>
                <cfset isValid=0>
            </cfcatch>
      </cftry>  


<cfoutput>      
      <cfif isValid>
            The IsValid value=#isValid# , username=#results.sAMAccountName#
            <p>You are authenticated</p>
      <cfelse>                
            <p>Get out foul beast!  </p>      
      </cfif>      
            
 

</cfoutput>



<cfelse>
      <p>Username or Password is incorrect.</p>
</cfif>
you should open another question, but i can say that you should start trying to analyze errors with <cftry><cfcatch> & dumping the cfcatch to see whats up - not just your foul beast statement which tells you nothing other than "it didn't work" ...
To dgrafx:

I posted a new related question about how to make it secure via SSL:

https://www.experts-exchange.com/questions/28490526/Coldfusion-and-cfldap-using-SSL.html

Thanks.