Enterprise Software

How do I... Use NDS to authenticate users of a Web-based application?


This blog entry is also available as a TechRepublic download.

In many Web-based applications, functionality is controlled and/or personalized by knowing who the logged-in user is. This varies from simply displaying a greeting to the creation of custom menus depending on the user's login.

Additionally, many organizations make use of directories such as Microsoft's Active Directory (AD) and Novell's Netware Directory Services (NDS), which can be deployed in conjunction with an appropriate Web server, to provide user identification at the Web server level. In the context of user authentication, which is usually done via a login page in a Web-based application, the presence of a directory can be used as an alternative authentication method. This is referred to as Single Sign On (SSO). Let's look at how to use NDS as our directory service in a Novell NetWare environment. To provide the link between our code and NDS, we are using Novell's NWDir1 control.

Client-side detection

First, we will look at client-side detection using this control. In the following simple example (Listing A), we use VBScript to provide the functionality.

Listing A

<HTML>

<HEAD>

<TITLE>Netware User Detection Sample</TITLE>

<SCRIPT LANGUAGE="VBScript">

Sub WhoAmI_OnClick()

if (NWDir1.LoginName) then

msgbox NWDir1.LoginName

else

msgbox "No Logged In User"

end

End Sub

</SCRIPT>

</HEAD>

<OBJECT ID="NWDir1" CLASSID="CLSID:4F021AE3-9E98-11D0-A808-00C04FDCD94A" CODEBASE="http://www.novell.com/nds/controls/nwdir.ocx">

</OBJECT>

<BODY>

<INPUT NAME = "WhoAmI" type="button" VALUE="Who Am I ?"><p>

</BODY>

</HTML>

When the button is pressed, the code displays the value of the LoginName property of the NWDir1 control or a message if there is no user currently logged in. Although in the example above we are using a message box to display the info, we could also use the information to display the username in a dashboard or use it to log the user into an application without the user having to see or complete the login screen.

User detection on the server side

Now that we have covered client-side detection, let's examine how to do user detection on the server side. In this example, we use the login screen for a Web-based application. To start with, we need a sample login page, which could look something like the Listing B.

Listing B

<HTML>

<HEAD>

<TITLE>NDS Authentication Login Screen</TITLE>

<SCRIPT LANGUAGE="javascript" TYPE="text/javascript">

function checkUser()

{

var URL="/scripts/usercheck.asp?"

URL=URL + "username=" + escape(document.slf.txtUsername.value);

URL=URL + "&password=" + escape(document.slf.txtPassword.value);

URL=URL + "&domain=" + escape(document.slf.dropOffices.value);

// get the IFrame

var myIFramesArray=document.getElementsByName('ajaxframe');

// if we have the IFrame

if (myIFramesArray.length>0)

{

// change the URL of the IFrame

myIFramesArray[0].src=URL;

}

} </HEAD>

<BODY>

<DIV ID='loginForm' NAME='loginForm'>

<FORM NAME='slf'>

User Name : <INPUT TYPE='text' NAME='txtUsername' SIZE='20'><p>

Password : <INPUT TYPE='password' NAME='txtPassword' SIZE='20'><p>

Office Location :

<SELECT NAME='dropOffices' ID='dropOffices' SIZE='1' CLASS='Select'>

<OPTION VALUE='NDS:DEMO_TREESite1'>Site 1</OPTION>

<OPTION VALUE='NDS:DEMO_TREESite2'>Site 2</OPTION>

<OPTION VALUE='NDS:DEMO_TREESite3'>Site 3</OPTION>

</SELECT>

<P>

<INPUT TYPE='button' VALUE='Log In' ONCLICK="checkUser()">

</FORM>

</DIV>

<IFRAME NAME="ajaxframe" ID="ajaxframe" WIDTH="800" HEIGHT="200" STYLE="display:none"></IFRAME>

</BODY>

</HTML>

When the login button is pressed, the checkUser function creates a URL with some QueryString parameters, which are the user-entered fields on the form. Once the URL has been constructed, it is passed as the source URL for the IFrame and will in turn pass the parameters to the server-side script. This is identical to a standard HTTP GET request where the TARGET attribute is set to be the IFrame. However, in this example I've done the work in the code to help with the clarity of the example.

Authentication on the server side

The next example will perform the authentication on the server side. This simple Classic ASP page (Listing C) will try to authenticate the user-provided credentials against the selected NDS Domain and then send a response, in the form of a simple HTML page, back to the IFrame. In the code, we create an instance of the NWDir control, then try to locate the user, using the UserName and Domain within the NDS Tree. If the user can't be located, the code will inform the user. If the user can be located, we check the provided password using the ValidatePassword function. If the login is valid, we create a simple HTML page to be returned to the IFrame, which will inform the parent page that the login is validated.

Listing C

<%

OPTION EXPLICIT

DIM sCurrentUsername

DIM sCurrentPassword

DIM sCurrentNetworkPath

DIM sCheckLogin

' get the data off the form

sCurrentUsername = Request.QueryString("username")

sCurrentPassword = Request.QueryString("password")

sCurrentNetworkPath = Request.QueryString("domain")

' create the NDDS object

SET NWDir1=Server.CreateObject("NWDirLib.NWDirCtrl.1")

' set the value

NWDir1.Fullname = sCurrentNetworkPath

SET entry=NWDir1.FindEntry(sCurrentNetworkPath & "\" & sCurrentUsername)

' if we can't find the user then redirect to the login page

IF entry IS nothing Then

loginFailed

ELSE

' attempt a login

sCheckLogin = NWDir1.Entries(sCurrentUserName).ValidatePassword(sCurrentPassword)

' if the login is ok

IF sCheckLogin = True THEN

' close the HTML header and start the doc

Response.Write("<HTML>" & Chr(13))

Response.Write("<HEAD>" & Chr(13))

Response.Write("</HEAD>" & Chr(13))

Response.Write("<BODY ONLOAD='alert(" & Chr(34) & sCurrentUsername & Chr(34) & ")'>" & Chr(13))

Response.Write("</BODY>" & Chr(13))

Response.Write("</HTML>" & Chr(13))

' otherwise login failed, so bounce back to the login page

ELSE

loginFailed

END IF

END IF

' drop the connection to the NDS object

SET NWDir1=Nothing

SUB loginFailed

Response.Write("<HTML>" & Chr(13))

Response.Write("<HEAD>" & Chr(13))

Response.Write("<SCRIPT LANGUAGE='javascript' TYPE='text/javascript'>" & Chr(13))

Response.Write(" function loginFailed()" & Chr(13))

Response.Write(" {" & Chr(13))

Response.Write(" alert(' Login Failed for user " & sCurrentUsername & "');" & Chr(13))

Response.Write(" }" & Chr(13))

Response.Write("</SCRIPT>" & Chr(13))

Response.Write("</HEAD>" & Chr(13))

Response.Write("<BODY ONLOAD='loginFailed()'>")

Response.Write("</BODY>" & Chr(13))

Response.Write("</HTML>" & Chr(13))

end sub

%>

The end result is that there will be a popup message informing the user that either they have been successfully logged in or they have not. While this is a trivial example, it could be easily amended to create an ASP session or a cookie storing a user's credentials for an application for example.

If you combine both approaches, you could create a login screen that appears only if the user is not already authenticated against the NDS directory; otherwise, they could be seamlessly processed as if they had logged in via the login screen. This would be an example of an SSO application.

Further implementation

In the three examples, we have created a client- and server-side method of detecting and authenticating users against a Novell NDS directory. A similar implementation could be written for a Microsoft Active Directory environment and could be based on the sample code from MSDN.

4 comments
Meesha
Meesha

Excuse my ignorance but how does this all work with SSL and iChain/edirectory?

mike.d.howell
mike.d.howell

We use Apache's built-in LDAP authentication mechanisms along with a folder-level acl. No need for scripts, as when Apache attempts to serve the index file of a protected folder, it automatically sends a login header first. The acl tells Apache how to connect to our LDAP authentication service (which runs on Netware, but could be AD as well) to authenticate the user.

Greg Griffiths
Greg Griffiths

This code simply processes the request, so even if it is behind SSL then the process should still work.

Greg Griffiths
Greg Griffiths

Michael, Thanks for the post, we had considered using Apache, but the customer insisted on IIS and the back end tool we had to authenticate for require a REMOTE_USER header. This situation was the basis of the work that I had to do that led to the code in the article.