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.