The LogonUser API
page 1 of 1
Published: 27 Sep 2003
Unedited - Community Contributed
Abstract
Learn how to authenticate domain users via web forms using a simple Win32 API. Users will not be prompted with the standard and oftentimes confusing Windows Logon dialog.
by Brian Desmond
Feedback
Average Rating: 
Views (Total / Last 10 Days): 80266/ 70

Download C# Demo
Download VB.Net Demo

 
 

Introduction

The Windows Authentication prompt can often be an intimidating dialog for users. It asks for two or three things: username, password, and sometimes domain. Users may (and should) know their network username and password combination, but how many of them know the name of the domain their account is kept on? To make matters more complex, depending on the operating system and browser version, the domain entry box isnt shown. In this case, users need to prefix their username with DomainName\:

Another solution to this is to use a standard WebForm in conjunction with a Windows API, LogonUser. The LogonUser API is a function found in the advapi32.dll file of All Windows NT, 2000, and newer based servers and workstations.

The LogonUser API

The LogonUser function is defined in the Microsoft Platform SDK as follows:

BOOL LogonUser(
LPTSTR lpszUsername,
LPTSTR lpszDomain,
LPTSTR lpszPassword,
DWORD dwLogonType,
DWORD dwLogonProvider,
PHANDLE
phToken
);

This function takes five input parameters, and has a sixth output parameter, a token. A bit more on tokens will follow this. The .Net version of the API declaration is as follows (there are multiple ways to do this, all of which are functionally equivalent):

Private Declare Auto Function LogonUser Lib "advapi32.dll" ( _
ByVal
lpszUsername As String, _

ByVal
lpszDomain As String, _
ByVal
lpszPassword As String, _
ByVal
dwLogonType As Integer, _
ByVal
dwLogonProvider As Integer, _
ByRef
phToken As IntPtr) As Boolean

There are two input parameters which LogonUser requires that are of specific interest: dwLogonType, and dwLogonProvider. The two of these control the type of logon that is initiated, and the way the credentials are passed, respectively.

There are only a couple of logon types that really apply to this context: LOGON32_LOGON_NETWORK and LOGON32_LOGON_INTERACTIVE. If your need is solely to validate the supplied credentials, then LOGON32_LOGON_NETWORK is your best choice. It is the fastest, and does not cache the logins on the server. However, if you plan to impersonate the user whose credentials were supplied, LOGON32_LOGON_INTERACTIVE is necessary. It outputs the proper type of token for use with the WindowsIdentity.Impersonate method of the .Net Framework.

Access tokens are used to by the Windows Kernel to create processes as users, and do other security type things. There are various types of tokens which can be returned, depending on the logon type, and their specifics are beyond the scope of this article. Note, it is possible to use LOGON32_LOGON_NETWORK and use the access token to impersonate a user. Additional API calls are necessary to convert the returned token to the type used by the WindowsIdentity class.

dwLogonProvider dictates the method in which the web server will pass the users credentials to the domain. There are four possible values:

LOGON32_PROVIDER_DEFAULT
LOGON32_PROVIDER_WINNT35
LOGON32_PROVIDER_WINNT40
LOGON32_PROVIDER_WINNT50

The first of the four will use the default logon protocol for the system. By default on Windows 2000, this is NTLM, which will work with Windows NT 4.0 and newer domains. If one or more of the domain controllers youll be authenticating against is still running Windows NT 3.51, youll need to use LOGON32_PROVIDER_WINNT35. If the web server is running Windows XP or Windows Server 2003, and you have a Windows NT4 or NT351 based domain, youll need to use the appropriate provider specific to that type of domain, because the default provider is LOGON32_PROVIDER_WINNT50, which is not supported by Windows NT4.

One final parameter which has some special cases is the lpszDomain parameter. To authenticate against a domain, specify the NetBIOS name of the domain in which the account resides (this is the name selected in the dropdown of a Windows Control + Alt + Del when you login to your computer). If a . Is specified, the LogonUser API will attempt to login to the local machine (web server). The name of another workstation or member server on the network may also be specified for lpszDomain.

Code

With all that said and done, its time for some code! The download for this article contains a sample webform and the associated code in Visual Basic.Net and C#. All the code shown in the article is written in Visual Basic.Net.

The first step is to declare the necessary API call, and the constants associated with it:

Private Declare Auto Function LogonUser Lib "advapi32.dll" ( _
ByVal
lpszUsername As String, _
ByVal lpszDomain As String, _
ByVal lpszPassword As String, _
ByVal dwLogonType As Integer, _

ByVal dwLogonProvider As Integer, _

ByRef phToken As IntPtr) As Boolean

Const LOGON32_LOGON_INTERACTIVE As Long = 2
Const
LOGON32_LOGON_NETWORK As Long = 3

Const
LOGON32_PROVIDER_DEFAULT As Long = 0

Const
LOGON32_PROVIDER_WINNT50 As Long = 3

Const
LOGON32_PROVIDER_WINNT40 As Long = 2

Const
LOGON32_PROVIDER_WINNT35 As Long = 1


Step two, actually use the API that has been defined:

Private Function ValidateLogin( _
ByVal Username As String, _
ByVal Password As String, _
ByVal Domain As String) As Boolean

Dim token As IntPtr

If LogonUser(Username, _
Domain, _
Password, _

LOGON32_LOGON_INTERACTIVE, _

LOGON32_PROVIDER_DEFAULT, token) = True Then

Return True
Else
Return False
End If
End Function

In this example, Im using interactive login, to allow for easy use of the access token which is returned (and stored in the IntPtr variable token) if the API call is successful. In this example, I havent done anything with the access token which is returned, and I will not do so at all in this article.

In Conjunction with Built-in ASP.Net Authentication

Since the point of the LogonUser code is to replace the ugly Windows Auth dialog, Windows Authentication obviously isnt the solution, in terms of choosing an authentication provider, if any.

Forms authentication, on the other hand, does the trick. The page which calls the LogonUser API is a standard web form, and hence can be used for forms authentication. In the included demo files, forms authentication is demonstrated, in conjunction with the LogonUser API. The following line of code does the forms authentication:

FormsAuthentication.RedirectFromLoginPage(Username, False)

The first parameter specifies the username of the user being logged in, accessible after this via Context.User.Identity.Name, and the second parameter specifies whether or not a persistent cookie should be saved.

Handling Errors

If the API Call returns false, use this line of code to get the Win32 error code back for debugging:

System.Runtime.InteropServices.Marshal.GetLastWin32Error()

A listing of Win32 error codes can be found online at: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/system_error_codes.asp

Server Setup

This section applies only if youre running a Windows 2000 server. Windows XP, and Windows Server 2003 handle this behavior automatically, so no further configuration is necessary. The ASPNET account, or whatever account your server is configured to run the ASP.Net worker process account as requires the Act as Part of the Operating System user right. This is also known by the operating system, and in error messages as SE_TCB_NAME. Configuring this right is a cinch:

  1. Logon to the server as an administrator
  2. Open up the machines local security policy (start>run>secpol.msc)
  3. Expand Security Settings, Local Policies, User Rights Assignment
  4. Double click Act as part of the operating system
  5. Click Add User or Group, enter the worker process account name and click OK, and OK again

In order for this change to become effective, the web server will have to be rebooted. This setting is applied when the start dialog reads Applying Security Settings.

From the Frontline

Ive used the LogonUser API in conjunction with two web applications already. It worked well. The reason I chose the API route is because of the user base Im targeting about 650 high-school students. The computers they were going to use to login were running two different operating systems, Windows NT4, and Windows 2000. The LogonUser API guaranteed me an interface which was conformant with the look of the rest of our homepage, and the ability to upgrade to Windows 2000 without changing a line of code.

I learnt the hard way that LOGON32_LOGON_INTERACTIVE is definitely not the provider to use when servicing a high volume of simultaneous login events. The first time I used the LogonUser API in a production app, I had about six-hundred users login over a thirty minute period. For reasons unbeknownst to me, the web server would reject valid credentials from time to time. It was as though the API call never executed because nothing was returned. Moral of this story, use LOGON32_LOGON_NETWORK for high load scenarios.

For my production environment, Ive encapsulated the LogonUser API call, along with several other related API calls for easy use from any web or windows application.

In Summary

For all the details of how to work with the LogonUser API call, its actually pretty simple to use. A few things to consider:

When deciding which logon provider to use, you must be sure that all of the domain controllers on the network support the protocol youre using. This means that if all but one of the domain controllers in your network is running Windows 2000, and this lone NT4 domain controller is located halfway across the globe, you still need to use the NT40 logon provider. This is because your web server could theoretically contact the NT4 domain controller, and fail to communicate the given credentials.

If you have no plans to impersonate the logged in user, use the LOGON32_LOGON_NETWORK login type. It is faster, and consumes less network resources.

If you have one account domain, I'd recommend that you store it in the appSettings area of your web.config file. That way, if your network admins ever rename the domain, you wont have to recompile the application. Obviously, if there are multiple account domains, your users or your application will have to decide which one to query.



User Comments

Title: Rasis   
Name: E_ALI
Date: 6/10/2010 6:57:40 AM
Comment:
how it works
Title: Website deployed on https logonuser API stopped working   
Name: Amit
Date: 12/14/2009 1:10:30 AM
Comment:
Hi,

I tried to use the code on my website. When I use it on my machine it works perfect. But as soon as I deploy it on the server which has SSL, it outputs ERROR: 2 - ERROR_FILE_NOT_FOUND. Is this because I'm using ssl or is it because of server configuration. We have windows server 2003. Please help.

Thanks a lot.
Title: Compact Framework 2.0   
Name: Jenny
Date: 5/8/2008 1:36:49 PM
Comment:
Would this work for CF 2.0? I need to programmatically pass the credentials for file access....so that the network logon form doesn't come up.
Title: .net variant?   
Name: CDM
Date: 4/30/2008 2:55:13 PM
Comment:
I found this while trying to find a .net way to do this. I'm surprised if there isn't some .net way which doesn't require a call to the SDK ::LogonUser? Not finding it yet. WindowsIdentity looks close, but it seems to take a token returned from the SDK call.

If not, I'd think MS would make the call easier, have predefined function definitions, not make me name the dll its in, etc...
Title: get error 1326 with correct login ID and password (under window server 2000)   
Name: Fion
Date: 2/18/2008 4:47:56 AM
Comment:
I have 2 servers, server1(window server 2003) and server2(window server 2000). The LogonUser work on server1 but show error code 1326 on server2 with both same and correct login detail on same application. ASPNET account has been set in "Act as part of the operating system" too. what else reason that may cause such error? I had "ping" the server2, it show server2.OneName.com, is it mean that the domain name is OneName, right? If yes, then i confirm I give the correct domain name too. But then what else reason that may cause such error to occur? Any help is appreciated very much. It is quite urgent. Thanks in advanced.
Title: IIS in workgroup   
Name: Muhammad A Khan
Date: 6/11/2007 6:11:52 AM
Comment:
Can this be used if web server is in workgroup and we want to do authentication from AD(active directory)?
Help and comments would be appreciated
Title: At last...   
Name: Jowie
Date: 5/8/2007 3:44:58 PM
Comment:
The one article that actually help me. Every ware I've looked had a complicated explanation of this API. It can't get any easier that this.
Title: best one for this topic   
Name: patel
Date: 3/29/2007 6:34:53 PM
Comment:
This is THE BEST ...
Title: just what I need   
Name: David Weeden
Date: 1/8/2007 12:03:22 PM
Comment:
just what I need for my application to replace the NT Challenge response from IIS. thanks fella
Title: Useful one   
Name: Prashant Koli
Date: 8/24/2006 3:10:32 AM
Comment:
It's indeed a good article, but I've a small doubt. Does this API sends the user credentials across the network in a clear text manner??
Title: ASP.NET AD Authentication   
Name: Albert Chua
Date: 8/22/2006 11:48:02 AM
Comment:
Very userful article and it solved my problem. Thanks.
Title: Excellent article!   
Name: Christian Torvnes
Date: 7/27/2006 7:51:26 AM
Comment:
Clear, concise and straight to the point. Many thanks!
Title: Perfect solution to windows authentication   
Name: Nagendra
Date: 7/26/2006 2:42:05 PM
Comment:
Thanks for the great article ! I never expected this would be so easy in validating user credentials using windows API. Thanks for saving our time Brian.
Title: G-R-E-A-T   
Name: Dejan
Date: 3/29/2006 9:34:44 AM
Comment:
Cool, finaly I have answer to all my questions on one place!

Thank you Brian
Title: Very useful   
Name: Hariharan
Date: 3/17/2006 4:38:02 AM
Comment:
Hello..This article was very useful for me...
Good Description...You can also add descriptions of Provider argument
Title: Good Techniques   
Name: Neoh Choo Hoo
Date: 3/3/2006 9:23:16 PM
Comment:
It is a great article which I search for so long. :)
Title: Great   
Name: Abdullah AlSubaiee
Date: 2/15/2006 8:03:12 AM
Comment:
Really Thank you a lot, Mr.Brian Desmond
It is a great article!
Title: AD ASP.Net Integration   
Name: BinaryThoughts
Date: 2/15/2006 3:24:53 AM
Comment:
Hi Brian,
Love this article. Amazing stuff. Well atleast I can sleep in peace now after two nights of trying to figure out the usage of this API.

BinThougts
Title: Question about using the domain accounts   
Name: Herb Meehan
Date: 7/5/2005 3:08:10 PM
Comment:
It will only view the machine you're logging onto. The Domain field seems to be a dummy field. Help :(
Title: Very good   
Name: VBGOD
Date: 7/2/2005 6:08:43 PM
Comment:
This is what I've been looking for. Thank you.
Title: Zowy   
Name: David McClurg
Date: 6/27/2005 4:48:27 PM
Comment:
Wow I didn't know programming could be so cool.
Title: Great article!   
Name: John Elias
Date: 6/27/2005 4:47:04 PM
Comment:
Thanks, this was just what I needed.






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2014 ASPAlliance.com  |  Page Processed at 4/21/2014 3:33:22 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search