Click here to Skip to main content
Click here to Skip to main content

Windows Authentication Using Form Authentication

By , 1 Jul 2009
 
Windows Authentication using Form Authentication

Background

Last month I worked on a small assignment to authenticate windows account (Domain or Local) using form authentication. The purpose of this task was to facilitate our application users to login with any valid windows account (instead of automatic authentication of windows logged in user).

As it was an interesting task, I decided to share my experience with you.

Requirement

The application should authenticate windows user using form authentication so that the currently logged in user shouldn't be bound to logged-in in the application only with his windows account. He should be able to log-in with any valid windows account.

Solution

We need to do the following steps to get the desired functionality:

  1. Configure Authorization and Authentication settings in web.config
  2. A login page and execute logic to authenticate provided credential of windows user
  3. If provided credentials are authenticated in step 2, then generate an authentication token so that user should be able to navigate into the authorized pages of your application.

1. Configure Authorization and Authentication Settings in web.config

We need to use Form authentication. The user will enter his windows credential in the form and we will validate provided windows credential using custom logic in step 2.

<authentication mode="Forms">
	<forms loginUrl="login.aspx" name=".ASPXFORMSAUTH"></forms>
</authentication>

To restrict anonymous access, you need to make the following authorization settings in web.config:

<authorization>
	<deny users="?"/>
</authorization>

2. Create a Login Page and Execute Logic to Authenticate Provided Credential of Windows User

We need to create a login page (e.g. login.aspx) to get username and password information from user and then validate them. We can have different options to validate windows credentials, the way I chose is LogonUser() method of a win32 API called Advapi32.dll.

The LogonUser function attempts to log a user on to the local computer. This method takes username, password and other information as input and returns a Boolean value to indicate that either user is logged or not. If it returns true, it means the provided username and password are correct. To use this method in our class, we need to include the following namespace:

using System.Runtime.InteropServices;

And the add method declaration with DLLImport attribute (as this is a method of Win32 DLL which is an unmanaged DLL).

[DllImport("ADVAPI32.dll", EntryPoint = 
	"LogonUserW", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool LogonUser(string lpszUsername, string lpszDomain, 
	string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

According to the MSDN documentation:

lpszUsername [in]

A pointer to a null-terminated string that specifies the name of the user. This is the name of the user account to log on to. If you use the user principal name (UPN) format, User@DNSDomainName, the lpszDomain parameter must be NULL.

lpszDomain [in, optional] 

A pointer to a null-terminated string that specifies the name of the domain or server whose account database contains the lpszUsername account. If this parameter is NULL, the user name must be specified in UPN format. If this parameter is ".", the function validates the account by using only the local account database.

lpszPassword [in] 

A pointer to a null-terminated string that specifies the plaintext password for the user account specified by lpszUsername. When you have finished using the password, clear the password from memory by calling the SecureZeroMemory function. For more information about protecting passwords, see Handling Passwords.

dwLogonType [in] 

The type of logon operation to perform.

dwLogonProvider [in] 

Specifies the logon provider.

phToken [out] 

A pointer to a handle variable that receives a handle to a token that represents the specified user.

3. Generate an Authentication Token, If Provided Credentials are Authenticated in step 2

If provided credentials are authenticated by LogonUser() method, then we need to generate an authentication token so that users should be able to navigate into the authorized pages of the application.

FormsAuthentication.RedirectFromLoginPage()

Or:

FormsAuthentication.SetAuthCookie()

can be used for this purpose.

Here is login button’s Click handler code for authentication and generating authentication token. The comments will help you to understand the code.

protected void btnLogin_Click(object sender, EventArgs e)
    {
        string domainName = GetDomainName(txtUserName.Text); // Extract domain name 
			// form provided DomainUsername e.g Domainname\Username
        string userName = GetUsername(txtUserName.Text);  // Extract user name 
			// from provided DomainUsername e.g Domainname\Username
        IntPtr token = IntPtr.Zero;

        //userName, domainName and Password parameters are very obvious.
        //dwLogonType (3rd parameter): 
        //    I used LOGON32_LOGON_INTERACTIVE, This logon type 
        //    is intended for users who will be interactively using the computer, 
        //    such as a user being logged on by a terminal server, remote shell, 
        //    or similar process. 
        //    This logon type has the additional expense of caching 
        //    logon information for disconnected operations.
        //    For more details about this parameter please 
        //    see http://msdn.microsoft.com/en-us/library/aa378184(VS.85).aspx
        //dwLogonProvider (4th parameter) :
        //    I used LOGON32_PROVIDER_DEFAUL, This provider use the standard 
        //    logon provider for the system. 
        //    The default security provider is negotiate, unless you pass 
        //    NULL for the domain name and the user name is not in UPN format. 
        //    In this case, the default provider is NTLM. For more details 
        //    about this parameter please see 
        //    http://msdn.microsoft.com/en-us/library/aa378184(VS.85).aspx
        //phToken (5th parameter):
        //    A pointer to a handle variable that receives a handle to 
        //    a token that represents the specified user. 
        //    We can use this handler for impersonation purpose. 
        bool result = LogonUser(userName, domainName, 
				txtPassword.Text, 2, 0, ref token);
        if (result)
        {
            //If Successfully authenticated

            //When an unauthenticated user try to visit any page of your 
            //application that is only allowed to view by authenticated users 
            //then ASP.NET automatically redirect that user to login form 
            //and add ReturnUrl query string parameter that contain the URL of 
            //a page that user want to visit, So that we can redirect the 
            //user to that page after authenticated. 
            //FormsAuthentication.RedirectFromLoginPage() method not only 
            //redirect the user to that page but also generate an authentication 
            //token for that user.
            if (string.IsNullOrEmpty(Request.QueryString["ReturnUrl"]))
            {
                FormsAuthentication.RedirectFromLoginPage(txtUserName.Text, false);
            }
            //If ReturnUrl query string parameter is not present, 
            //then we need to generate authentication token and redirect the user 
            //to any page ( according to your application need). 
            //FormsAuthentication.SetAuthCookie() method will 
            //generate Authentication token 
            else
            {
                FormsAuthentication.SetAuthCookie(txtUserName.Text, false);
                Response.Redirect("default.aspx");
            }
        }
        else
        {
            //If not authenticated then display an error message
            Response.Write("Invalid username or password.");
        }
    }

Let's Put It All Together

Login.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Login.aspx.cs" 
	Inherits="Login" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Windows Authentication Using Form Authentication</title>
    <style type="text/css">
        .style1
        {
            width: 100%;
        }
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    
        <table class="style1">
            <tr>
                <td>
                    <asp:Label ID="lblUserName" runat="server" Text="User Name:">
		 </asp:Label>
                </td>
                <td>
                    <asp:TextBox ID="txtUserName" runat="server"></asp:TextBox>
                </td>
            </tr>
            <tr>
                <td>
                    <asp:Label ID="lblPassword" runat="server" Text="Password:">
		  </asp:Label>
                </td>
                <td>
                    <asp:TextBox ID="txtPassword" runat="server" TextMode="Password" >
		  </asp:TextBox>
                </td>
            </tr>
            <tr>
                <td>
                     </td>
                <td>
                    <asp:Button ID="btnLogin" runat="server" onclick="btnLogin_Click" 
                        Text="Login" />
                </td>
            </tr>
        </table>
    
    </div>
    <p>
         </p>
    </form>
</body>
</html>

Login.aspx.cs

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Runtime.InteropServices;

public partial class Login : System.Web.UI.Page
{
    [DllImport("ADVAPI32.dll", EntryPoint = 
	"LogonUserW", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool LogonUser(string lpszUsername, 
	string lpszDomain, string lpszPassword, int dwLogonType, 
	int dwLogonProvider, ref IntPtr phToken);

    /// <summary>
    /// Parses the string to pull the domain name out.
    /// </summary>
    /// <param name="usernameDomain">The string to parse that must 
    /// contain the domain in either the domain\username or UPN format 
    /// username@domain</param>
    /// <returns>The domain name or "" if not domain is found.</returns>
    public static string GetDomainName(string usernameDomain)
    {
        if (string.IsNullOrEmpty(usernameDomain))
        {
            throw (new ArgumentException("Argument can't be null.", "usernameDomain"));
        }
        if (usernameDomain.Contains("\\"))
        {
            int index = usernameDomain.IndexOf("\\");
            return usernameDomain.Substring(0, index);
        }
        else if (usernameDomain.Contains("@"))
        {
            int index = usernameDomain.IndexOf("@");
            return usernameDomain.Substring(index + 1);
        }
        else
        {
            return "";
        }
    }

    /// <summary>
    /// Parses the string to pull the user name out.
    /// </summary>
    /// <param name="usernameDomain">The string to parse that must 
    /// contain the username in either the domain\username or UPN format 
    /// username@domain</param>
    /// <returns>The username or the string if no domain is found.</returns>
    public static string GetUsername(string usernameDomain)
    {
        if (string.IsNullOrEmpty(usernameDomain))
        {
            throw (new ArgumentException("Argument can't be null.", "usernameDomain"));
        }
        if (usernameDomain.Contains("\\"))
        {
            int index = usernameDomain.IndexOf("\\");
            return usernameDomain.Substring(index + 1);
        }
        else if (usernameDomain.Contains("@"))
        {
            int index = usernameDomain.IndexOf("@");
            return usernameDomain.Substring(0, index);
        }
        else
        {
            return usernameDomain;
        }
    }  

    protected void btnLogin_Click(object sender, EventArgs e)
    {
        string domainName = GetDomainName(txtUserName.Text); // Extract domain name 
			//form provide DomainUsername e.g Domainname\Username
        string userName = GetUsername(txtUserName.Text);  // Extract user name 
			//from provided DomainUsername e.g Domainname\Username
        IntPtr token = IntPtr.Zero;

        //userName, domainName and Password parameters are very obvious.
        //dwLogonType (3rd parameter): 
        //    I used LOGON32_LOGON_INTERACTIVE, This logon type is 
        //    intended for users who will be interactively using the computer, 
        //    such as a user being logged on by a terminal server, remote shell, 
        //    or similar process. 
        //    This logon type has the additional expense of caching 
        //    logon information for disconnected operations.
        //    For more details about this parameter please see 
        //    http://msdn.microsoft.com/en-us/library/aa378184(VS.85).aspx
        //dwLogonProvider (4th parameter) :
        //    I used LOGON32_PROVIDER_DEFAUL, This provider use the standard 
        //    logon provider for the system. 
        //    The default security provider is negotiate, unless you pass 
        //    NULL for the domain name and the user name is not in UPN format. 
        //    In this case, the default provider is NTLM. For more details 
        //    about this parameter please see 
        //    http://msdn.microsoft.com/en-us/library/aa378184(VS.85).aspx
        //phToken (5th parameter):
        //    A pointer to a handle variable that receives a handle to a 
        //    token that represents the specified user. We can use this handler 
        //    for impersonation purpose. 
        bool result = LogonUser(userName, domainName, txtPassword.Text, 2, 0, ref token);
        if (result)
        {
            //If Successfully authenticated

            //When an unauthenticated user try to visit any page of your 
            //application that is only allowed to view by authenticated users then,
            //ASP.NET automatically redirect the user to login form and add 
            //ReturnUrl query string parameter that contain the URL of a page that 
            //user want to visit, So that we can redirect the user to that page after 
            //authenticated. FormsAuthentication.RedirectFromLoginPage() method 
            //not only redirect the user to that page but also generate an 
            //authentication token for that user.
            if (string.IsNullOrEmpty(Request.QueryString["ReturnUrl"]))
            {
                FormsAuthentication.RedirectFromLoginPage(txtUserName.Text, false);
            }
            //If ReturnUrl query string parameter is not present, 
            //then we need to generate authentication token and redirect 
            //the user to any page ( according to your application need). 
            //FormsAuthentication.SetAuthCookie() 
            //method will generate Authentication token 
            else
            {
                FormsAuthentication.SetAuthCookie(txtUserName.Text, false);
                Response.Redirect("default.aspx");
            }
        }
        else
        {
            //If not authenticated then display an error message
            Response.Write("Invalid username or password.");
        }
    }
}

And We Are DONE!!!

We are done with authentication of windows account using form authentication.

Feedback

You feedback will be very helpful for me. You can send me an email at akhhttar@gmail.com. Thanks!

License

This article, along with any associated source code and files, is licensed under The Common Public License Version 1.0 (CPL)

About the Author

Muhammad Akhtar Shiekh
Software Developer Imanami Corporation
Pakistan Pakistan
Member
I am Microsoft Certified Technology Specialist for Web Application Development. I have 4 year experience of Web and Distributed application development.I have considerable experience developing client / server software for major corporate clients using the Windows operating systems and .NET platform ( ASP.NET, C# , VB.NET).I have single and multi-threaded code development experience, as well as experience developing database and enterprise level distributed applications.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionOnly works locally?memberMember 1005149014 May '13 - 4:02 
QuestionDoes it work from DMZmembersp67676 Mar '13 - 9:55 
AnswerRe: Does it work from DMZmemberMuhammad Akhtar Shiekh6 Mar '13 - 17:26 
QuestionLogin with any accountmembertaketoka4 Jan '13 - 16:38 
AnswerRe: Login with any accountmemberMuhammad Akhtar Shiekh4 Jan '13 - 22:27 
QuestionRequest.ServerVariables["LOGON_USER"]memberLluthus20 Nov '12 - 22:51 
AnswerRe: Request.ServerVariables["LOGON_USER"]memberMuhammad Akhtar Shiekh4 Jan '13 - 22:26 
GeneralAppreciationmemberEd Gepulle25 Oct '12 - 9:28 
GeneralRe: AppreciationmemberMuhammad Akhtar Shiekh4 Jan '13 - 22:07 
Questiongetting an error system.argumentexception heremembersusheel311230 May '12 - 20:32 
QuestionRe: getting an error system.argumentexception herememberMember 94688541 Oct '12 - 6:23 
AnswerRe: getting an error system.argumentexception herememberMuhammad Akhtar Shiekh4 Jan '13 - 22:19 
QuestionAuthentication fail for non-domain usermemberankyshah11 Nov '11 - 20:12 
AnswerRe: Authentication fail for non-domain usermemberMuhammad Akhtar Shiekh4 Jan '13 - 22:13 
GeneralAuto AuthenticationmemberWolfram Steinke6 Feb '11 - 17:30 
GeneralRe: Auto AuthenticationmemberIrwan Hassan20 Feb '11 - 18:00 
Hi, I guess you are running the application on the client PC which is Join to the domain controller. If yes, then you can use the Win32 API directly in your application. Just use the LogonUser API in your application. No need a webserver for this.
 
If your client is not connected to the domain controller, then you can use the above source code. Just modify the webform to get the binary which is the one user send to the web server and process the LogonUser. Once success, response the user app the Logon Successfull and end the session else inform the user the Logon is not successfull and you can terminate the session if you want.
 
I guess you understand what I explain above.
 
Thanks.
QuestionHow to log offmemberMember 445189418 Jan '11 - 1:52 
AnswerRe: How to log offmemberMuhammad Akhtar Shiekh4 Jan '13 - 22:01 
GeneralGreat example! Questions: how to add aditional pagesmemberttre4r43r5 May '10 - 11:37 
AnswerRe: Great example! Questions: how to add aditional pagesmemberMuhammad Akhtar Shiekh17 May '10 - 2:59 
GeneralWindows Authentication in stand-alone appmembernewspicy25 Aug '09 - 23:50 
AnswerRe: Windows Authentication in stand-alone appmemberakhhttar26 Aug '09 - 5:25 
GeneralNice Article.Good going AkhtarmemberKamran Shahid8 Jul '09 - 1:47 
GeneralUsing this for sharepointmemberbereddin7 Jul '09 - 21:30 
GeneralRe: Using this for sharepointmemberakhhttar10 Jul '09 - 2:23 
GeneralCannot login with non-administrators privilegememberSavun2 Jul '09 - 16:04 
GeneralRe: Cannot login with non-administrators privilegememberakhhttar2 Jul '09 - 23:55 
GeneralRe: Cannot login with non-administrators privilege [modified]memberSavun5 Jul '09 - 18:19 
GeneralRe: Cannot login with non-administrators privilegememberSavun6 Jul '09 - 18:20 
GeneralRe: Cannot login with non-administrators privilegememberakhhttar7 Jul '09 - 6:41 
GeneralRe: Cannot login with non-administrators privilegememberIrwan Hassan20 Feb '11 - 18:07 
GeneralNicememberMd. Marufuzzaman1 Jul '09 - 23:06 
GeneralInformationmemberkamarchand1 Jul '09 - 2:00 
GeneralRe: InformationmemberStephen Inglish1 Jul '09 - 2:34 
Generalgood onemembersarfraz_ch1 Jul '09 - 0:27 
GeneralUnable to download the demo projectmemberMember 404220929 Jun '09 - 18:58 
GeneralRe: Unable to download the demo projectmemberakhhttar1 Jul '09 - 0:24 
GeneralRe: Unable to download the demo projectmemberMember 40422091 Jul '09 - 0:27 
Generalprepopulate text boxmembermihasic23 Jun '09 - 23:55 
GeneralRe: prepopulate text boxmemberakhhttar24 Jun '09 - 0:36 
GeneralThnaks for a great articlememberFaheem Iqbal23 Jun '09 - 23:48 
GeneralRe: Thnaks for a great articlememberakhhttar23 Jun '09 - 23:54 
QuestionActiveDirectoryMembershipProvider?memberKing_kLAx23 Jun '09 - 14:10 
AnswerRe: ActiveDirectoryMembershipProvider?memberakhhttar23 Jun '09 - 20:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 1 Jul 2009
Article Copyright 2009 by Muhammad Akhtar Shiekh
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid