Overview
This article attempts to work out an example of the impersonation namespace in .NET, to logon a user through a webpage, with his Windows logon credentials. The traditional way of logging in users using their Windows authentication is by enabling the Basic Authentication property of the web application in IIS.
But, using the control, we can eliminate the necessity of controlling logon through IIS, and enable it through our code. This opens up a considerable area of control through code. We can hence have a control on which user login is requested, and on which domain and all that.
We will discuss the creation of the project and the logic I had in mind while developing it. The completed and tested code, that was developed using VS.NET is attached to this article.
Introduction
We create two web user controls.
WindowsLoginControl
This has the implementation and UI for the login pane. It has two UIs, one for new users, one for already logged in users. A session variable maintains the state of the login to determine which UI to show. Code in this control calls the logInUser
class' shared method to process the login.
ErrorControl
This has implementation and UI for an error reporting pane. When errors occur, other controls on the page update a session
variable, which is checked when this control loads. When there's no error, we display an 'Under construction' message (This may be removed in release versions).
NOTE: We could have implemented the logic of this control also into the WindowsLoginControl
, but having this as a separate control allows us to easily move the control on the UI of a target page in VS.NET.
- A LoginUser class with a shared method for processing the login
This has implementation of the login process. A shared method takes username, password and domain as parameters and tries a Windows logon with the data, and we impersonate the user.
- A logoff page which clears user's session and abandons it
This is to cleanup the sessions, and make the totalActiveUser
count on the system more reliable.
The Code
Open an ASP.NET project. Select the project in the Solution Explorer and create a new 'web user control' item. Develop the UI for it... probably two text boxes... for username and password and a 'Login' button.
We make another UI, which shows a viewpane with the details of user login.
We show the login form when the user hasn't logged into the system, and show a login details view after the user logs in. Users login with their Windows authentication (this means that we should have created users on the server and the domain for this to work).
The windowsLoginControl
calls shared function LogInThisUser()
of the LogInUser
class which logs-in the user and impersonates the logged-on user. The code that does this is as below:
Dim loggedOn As Boolean = LogonUser(username, _
domainname, password, 3, 0, token1)
Dim token2 As IntPtr = New IntPtr(token1)
Dim mWIC As WindowsImpersonationContext = _
New WindowsIdentity(token2).Impersonate
For this, we declare the loginuser
class with the proper namespaces, and in a manner to include unmanaged code. We need unmanaged code to be written, because I believe we don't have a managed code implementation of the LogonUser
function of Windows to do the same.
Imports System.Security.Principal
Imports System.Security.Permissions
Imports System.Runtime.InteropServices
<Assembly: SecurityPermissionAttribute
(SecurityAction.RequestMinimum, UnmanagedCode:=True)>
Public Class LogInUser
<DllImport("C:\\WINDOWS\\System32\\advapi32.dll")> _
Private Shared Function LogonUser(ByVal _
lpszUsername As String, ByVal lpszDomain _
As String, ByVal lpszPassword As String, _
ByVal dwLogonType As Integer, _
ByVal dwLogonProvider As Integer, _
ByRef phToken As Integer) As Boolean
End Function
<DllImport("C:\\WINDOWS\\System32\\Kernel32.dll")> _
Private Shared Function GetLastError() As Integer
End Function
We can also find whether the logonuser
function generated errors, by calling the GetLastError
method.
We use session
variables to keep track of the user's login information and last access. We use an application variable to keep track of the total active users in the system.
Below code is part of this implementation (can be found in windowsLoginControl.ascx.vb):
Session.Add("LoggedON", True)
Session.Add("Username", sRetText)
Application.Item("TotalActiveUsers") += 1
lblUserName.Text = Session("Username")
lblLastSession.Text = Session("LastActive")
lblTotalUsers.Text = Application("TotalActiveUsers")
We keep track of the number of active users by simply incrementing the value every time the login method succeeds, and decrementing the value every time session_end
event occurs.
Better means to do this can also be used. The idea of this article is only to communicate the logic.
Testing the Project
Before testing the project, we should check the following.
We keep the domainname
as constant, rather than taking it from the user as an input. Check whether proper domain name is assigned to the constant.
Private Const domainName = "TestDomain"
Check whether location of the DLLs that are being imported are proper.
<DllImport("C:\\WINDOWS\\System32\\advapi32.dll")>
Check whether the logoff page has the correct page name and path to transfer the user, once cleanup is done.
Server.Transfer("webform1.aspx")
Code-Care
Care has to be taken that code implemented doesn't allow for inappropriate usage through various userLogin
s.
I preferred to keep the domain name hard-coded into the application through a constant rather than accept it as an user input... so that it's easy to limit or monitor user login sessions.
In case of intranet projects, we can create a separate domain, and user group for the project and use the above logic to allow users to login to the system only on the particular domain. May be, you can call this an 'Idea' :o)
Using the Controls on Another Web Project
To implement the web user controls in a web project, we simply copy the files related to the two controls, the loginuser
class, the logoff user page, to our new web project, and also copy the code from our global.asax.vb to the new project's global.asax.vb.
In VS.NET, these copied files can easily be included in the target project by right clicking and selecting 'Include in Project' in Solution Explorer.
Code Extensibility
The code that's been worked out in this article will authenticate users on only one page of the web application. Normally, a web application will have content inside the site to be viewed by authenticated users... in this case, the controls will have to have a mechanism of holding the user's authentication across page requests. This can be done by holding the windowsIdentity
object of the authenticated user in a session
variable, and allowing users rights on pages by using FileIOPermission
and other classes in the System.Security
namespace.
License
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.
A list of licenses authors might use can be found here.