Click here to Skip to main content
Email Password   helpLost your password?
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!

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralWindows Authentication in stand-alone app
newspicy
0:50 26 Aug '09  
Hi

This is a good tutorial thanks.

But i have a problem
How could i use Windows authentication and authorization for access my application?
I dont wanna use this for IIS or ASP, im try to write an stand-alone application
but need this kind of authenticaton and authorization principal to work with it,
so different users can access it but some not.

Thanks
Betize
AnswerRe: Windows Authentication in stand-alone app
akhhttar
6:25 26 Aug '09  
Hi,

You can use the same technique to authenticate windows user in desktop application. You can authenticate the user using LogonUser() method, and instead of creating authentication token using FormAuthentication class, you can use you own boolean flag to store the information that either user was authenticated or not. If you are facing any problem while implementing the same technique in desktop application please let me know.

Thanks
-Akhtar
GeneralNice Article.Good going Akhtar
Kamran Shahid
2:47 8 Jul '09  
Nice Article.Good going Akhtar
GeneralUsing this for sharepoint
bereddin
22:30 7 Jul '09  
dear .. how can i use it for sharepoint..
all what i need is to remove defult windows authentication pop up and make new loging page using windows authentication ... as in your post ..
but when i redirect to sharepoint portal after authentication successfully passed i got sharepoint access denied page ..
do you have any idea why is that happen ..
thanks alot ..
GeneralRe: Using this for sharepoint
akhhttar
3:23 10 Jul '09  
Hi,

I would definatly like to help you but you erros seeems a sharepoint error and I have no expierence to work with SharepointFrown.

Because as you are saying that LogonUser is sucessfully authenticating the credentials and error comes when you redirect, it seems that this error is particullary related to sharepoint.

Thank You
Akhtar
GeneralCannot login with non-administrators privilege
Savun
17:04 2 Jul '09  
It works fine when i log in as administrators privilege. But with a normal domain user, it doesn't succeed. Please advise.
GeneralRe: Cannot login with non-administrators privilege
akhhttar
0:55 3 Jul '09  
Hi,

I tested this application with notmail domain user and it works for me. Which exception you are getting?

Thanks
-Akhtar
GeneralRe: Cannot login with non-administrators privilege [modified]
Savun
19:19 5 Jul '09  
Hi Akhtar,

Thanks for your reply but let me write more about my story. I am writing code in my laptop (which is being joined into a domain controller). When debugging in my laptop, the function works fine (with all users in domain controller). When distributing to web server (which is also domain controller), the function always returns false (with no error message) with non-administrator privilege. My web server runs on Windows Server 2003.

Thanks,

modified on Monday, July 6, 2009 12:25 AM

GeneralRe: Cannot login with non-administrators privilege
Savun
19:20 6 Jul '09  
Hi Akhtar,

FYI, the issue has been solved by using dwLogonType 3: LOGON32_LOGON_NETWORK

Anyway, thanks for your reply.

Cheers
Savun
GeneralRe: Cannot login with non-administrators privilege
akhhttar
7:41 7 Jul '09  
Hi Savun,

Its good to hear that your problem is resolved Smile.

Thanks
Akhtar
GeneralNice
Md. Marufuzzaman
0:06 2 Jul '09  
Good work...I think some more description required, I vote 3 for this article.

Md. Marufuzzaman

GeneralInformation
kamarchand
3:00 1 Jul '09  
Hi,

I always see Form Authentification used with an asp.net solution.
Can we use the same Authentification method in a winform solution or WPF solution.


Thanks
GeneralRe: Information
Stephen Inglish
3:34 1 Jul '09  
Yes, you just have to include the System.Web.Security dll into your Windows Forms, or WPF solution.

Stephen Inglish
Consultant
Sogeti USA
MCPD: Windows Developer
MCPD: Web Developer
MCPD: Enterprise Developer

Generalgood one
sarfraz_ch
1:27 1 Jul '09  
nice article. Akhtar keep going
GeneralUnable to download the demo project
Member 4042209
19:58 29 Jun '09  
Unable to download the demo project
GeneralRe: Unable to download the demo project
akhhttar
1:24 1 Jul '09  
Sorry for the Inconvenience, I have changed download link to correct one,
Or you can directly download from
http://www.codeproject.com/KB/aspnet/WinAuthusingFormAuth/WindowsAuthenticationUsingFormAuthentication.zip

Thanks
Akhtar
GeneralRe: Unable to download the demo project
Member 4042209
1:27 1 Jul '09  
Thanks Now working
Generalprepopulate text box
mihasic
0:55 24 Jun '09  
as an addition - you can prepopulate username on the page:

string windowsLogin = httpApp.Request.ServerVariables["LOGON_USER"];

if (string.IsNullOrEmpty(windowsLogin))
{
// the user hasn't logged into domain
// try to get usual local login
windowsLogin = GetWindowsLogin();
}

// ...

/// <summary>
/// Detects windows principal from current thread
/// </summary>
/// <returns></returns>
public static string GetWindowsLogin()
{
string result = null;
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
WindowsPrincipal windowsPrincipal = Thread.CurrentPrincipal as WindowsPrincipal;
if (windowsPrincipal != null)
{
result = windowsPrincipal.Identity.Name;
}
return result;
}


httpApp - is an instance of HttpApplication (we use this code in http module), on the page Request is accessible without application
GeneralRe: prepopulate text box
akhhttar
1:36 24 Jun '09  
Yeah it could be a good addition to pre populate windows logged-in user name in User Name text box of login page.

Thanks buddy Smile
GeneralThnaks for a great article
Faheem Iqbal
0:48 24 Jun '09  
Hi, Its really nice what you can do with you web forms when it comes to do authentication process. Cool
GeneralRe: Thnaks for a great article
akhhttar
0:54 24 Jun '09  
Thanks for the appreciations Smile
QuestionActiveDirectoryMembershipProvider?
King_kLAx
15:10 23 Jun '09  
Why don't you use the ActiveDirectoryMembershipProvider Class to do this?

http://msdn.microsoft.com/en-us/library/system.web.security.activedirectorymembershipprovider.aspx[^]


Ian

AnswerRe: ActiveDirectoryMembershipProvider?
akhhttar
21:49 23 Jun '09  
Yes, you are right we can use Active Directory Membership Provider for that purpose but i think this is an easy and simple solution as compare to Active Directory Membership Provider.

Please correct me if i am wrong, I think we also need an windows user account to configure Active Directory Membership provider? while in this technique we don't need such account.

Thanks

Best Regards,
Muhammad Akhtar Shiekh


Last Updated 1 Jul 2009 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010