Guest Authentication with HTTP Modules
Create a Reusable HTTP Module to Authorize Guest Accounts
October 30, 2009
ASP.NET Under the Hood
LANGUAGES:VB.NET | C#
ASP.NETVERSIONS: 2.0
Guest Authentication with HTTP Modules
Create a Reusable HTTP Module to Authorize Guest Accounts
By Michele Leroux Bustamante
Welcome back ASP.NET architects and developers! This monthwe ll explore a practical implementation of the HTTP module. As always, samplesare provided in C# and VB.NET (see end of article for download details). Pleasesend your questions and requests to [email protected] let me unravel some mysteries for you!
Q. I have an ASP.NET site that restricts access usingforms authentication and uses the SQL membership provider. My problem is that Iwant to assign a guest account to unauthenticated users so they can access thesite with a limited set of features. I know how to use all the login controlsto provide role-based access, but I m not sure how to approach authorizing aguest account. Can you provide an example?
A. HTTP modules are one of my favorite extensibilitypoints for ASP.NET. At a glance, the steps for implementing a custom module arequite simple:
Create a type that implements the IHttpModuleinterface.
Subscribe to one or more application events.
Write code to respond to those events, based onthe desired behavior.
Enable the module in configuration and theruntime takes care of the rest.
The real challenge when implementing a custom HTTP moduleis in understanding the application events that you can subscribe to, andunderstanding how you can interact with the ASP.NET runtime during a request toimplement the behavior you seek.
In this article, I ll address how you can implement acustom HTTP module to add custom authentication behavior to allow guest accessto an application. But first, let s quickly review the fundamentals of theASP.NET runtime, and where modules fit within the context of a round trip.
HTTP Modules and the ASP.NET Runtime
There are plenty of resources available that describe theASP.NET runtime and its core objects: HTTP modules, HTTP handlers and handlerfactories, the application object, and the request context. My intention hereis to present a brief overview to provide context to the article, but I ll citesome references at the end of this article for those new to these concepts.
When IIS receives a request that matches one of theconfigured ASP.NET extensions (i.e., *.aspx, *.asmx, *.axd), it passes therequest to the ASP.NET worker process. The worker process runtime (HttpRuntime)allocates a pooled instance of the application object (HttpApplication) tohandle the request and creates an HttpContext instance that is the bucket ofall important information related to the request, including the request andresponse streams, the session, the authenticated user, and more. ThisHttpApplication instance is responsible for finding the correct handler type(IHttpHandler) to process the actual request, but along the way it also loadsany modules configured for the application so they can receive applicationevents and interact with the runtime. As illustrated by Figure 1, theHttpApplication instance, modules, and handlers are loaded into the applicationdomain processing the request, and events are fired from the HttpApplicationobject to any modules that subscribe to each event. If multiple modulessubscribe to the same event, they receive the event in the order they areconfigured in the module stack (see Figure 2).
Figure 1: HTTP modules receiveevents from the HttpApplication instance, and can interact with that instanceand other elements available to the HttpContext.
The ASP.NET runtime relies on many predefined modules tohandle caching, session state, authentication, authorization, profile, errors,and more. Figure 2 illustrates the order that these default modules areconfigured when you first install ASP.NET, and where the customGuestAuthenticationModule from this article will be configured.
Figure 2: Many predefined modulesare loaded into the application domain by default. Only one Authenticationmodule is loaded, and other grayed items represent optionally configuredmodules.
Each module in the stack may subscribe to differentapplication events, based on the functionality they provide and the timing whenthey must interact with the runtime to produce that functionality.
Timing Is Everything
When creating a custom module, it helps to know what theapplication events are, and the order in which they are raised during thelifecycle of a request. Figure 3 illustrates a default set of events raised bythe HttpApplication instance before and after the designated page handler isexecuted. You can hook events as early as BeginRequest; immediately prior tothe page handler with PreRequestHandlerExecute; immediately after the pagehandler with PostRequestHandlerExecute; and immediately prior to sendingcontent to the response output stream with PreSendRequestHeaders andPreSendRequestContent. This should tell you that you can intercept events priorto processing the request via its handler, thereby circumventing how therequest is processed, or even if the request should be processed. You can alsointeract with the response stream after the handler has completed its work. Thehandler is the primary party responsible for processing the incoming requeststream and populating the outgoing response stream. For Web form requests(*.aspx) the Page object is the IHttpHandler instance responsible for thisactivity.
Figure 3: TheGuestAuthenticationModule will intercept AuthenticateRequest to authenticatecallers with a guest account if they are not already authenticated.
Hooking the Right Application Events
It s not about how many application events your modulesubscribes to, but rather what the module does with those events it intercepts.Let s review the purpose for this GuestAuthenticationModule: If users are notauthenticated, log them in under a guest account. This implies letting theruntime try to authenticate the user based on credentials provided, and if theuser remains unauthenticated, provide guest credentials to access the site.This makes it possible to provide some minimal set of features to all sitevisitors, even if they do not have credentials to provide.
There are several events related to authentication andauthorization:
AuthenticateRequest. Raised by the runtime whenit is time to authenticate the caller.
PostAuthenticateRequest. Raised by the runtimeafter all subscribing modules have had a chance to authenticate.
AuthorizeRequest. Raised by the runtime when itis time to authorize the caller.
PostAuthorizeRequest. Raised by the runtimeafter all subscribing modules have had a chance to authorize the caller.
To achieve the desired result for thisGuestAuthenticationModule, we need to authenticate the caller before theruntime decides to redirect calls to the login page, and before theRoleManagerModule gathers role information to attach a RolePrincipal to theHttpContext.User property. If we hook the AuthenticateRequest event, our modulewill be invoked after the default FormsAuthenticationModule is given anopportunity to authenticate the caller. That s exactly where we want to be.
Implementing GuestAuthenticationModule
The implementation of the GuestAuthenticationModule willhook the AuthenticateRequest event in the Init method of the module, as shown inFigure 4.
Public Class GuestAuthenticationModule
Implements IHttpModule
Public Sub New()
End Sub
Public Sub Dispose()Implements IHttpModule.Dispose
End Sub
Public ReadOnly PropertyModuleName() As String
Get
Return"GuestAuthenticationModule"
End Get
End Property
Public Sub Init(ByValapplication As HttpApplication) Implements IHttpModule.Init
AddHandlerapplication.AuthenticateRequest, AddressOf Me.Application_AuthenticateRequest
End Sub
Protected Sub Application_AuthenticateRequest(ByValsender As Object, ByVal e As EventArgs)
If NotSystem.Web.HttpContext.Current.Request.IsAuthenticated Then
Dim authSection AsAuthenticationSection =
HttpContext.Current.GetSection("system.web/authentication")
LogonFormsGuest()
End Sub
...other methods
End Class
Figure 4: GuestAuthenticationModulewill hook the AuthenticateRequest event in the Init method of the module.
The guest module AuthenticateRequest handler will check tosee if the user is authenticated, and if not, proceed to authenticate a guestaccount. LogonFormsGuest is the function where it all happens (see Figure 5).
Private Sub LogonFormsGuest()
Dim userName As String =
ConfigurationManager.AppSettings("guestUser")
Dim password As String =
ConfigurationManager.AppSettings("guestPassword")
Dim authenticated AsBoolean =
Membership.ValidateUser(userName, password)
If authenticated Then
FormsAuthentication.SetAuthCookie(userName, False)
Dim user As IIdentity =New GenericIdentity(userName)
Dim userRoles() AsString = Roles.GetRolesForUser(user)
Dim principal AsGenericPrincipal = New
GenericPrincipal(user, userRoles)
HttpContext.Current.User = principal
End If
End Sub
Figure 5: Check tosee if the user is authenticated; if not, proceed to authenticate a guestaccount.
This function performs several important actions. First,it retrieves the configurable guest account from application settings (which,by the way, should be encrypted). This account information is passed to themembership provider to validate the user, which in this case invokes theSqlMembershipProvider. It is important to use the membership provider herebecause it updates table entries related to the user s login activity, and incrementsASP.NET performance counters related to authentication success or failure.
If the user was successfully authenticated against thecredential store, the forms authentication ticket is set using theFormsAuthentication utility component. Had the user entered credentials in thelogin page, an authentication ticket would have been created in the same way.Lastly, this method creates a generic security principal for the guest useraccount, including its role assignments, and attaches this principal to theUser property of the HttpContext instance. A security principal must always beattached to the context for authorization activities to be properly executed.
The DefaultAuthenticationModule (the last module in thestack) always maps the security principal attached to the context to theexecuting thread so they are aligned. This ensures that all role-based securitychecks are compared against the correct security principal:
Thread.CurrentPrincipal = HttpContext.Current.User
GuestAuthenticationModule for Windows Accounts
In fact, the code sample also provides functionality forguest authentication for Windows accounts. First, theApplication_AuthenticateRequest handler checks if the authentication mode isForms or Windows, to determine which type of guest account to authenticate (seeFigure 6).
Protected Sub Application_AuthenticateRequest(
ByVal sender As Object,ByVal e As EventArgs)
If NotSystem.Web.HttpContext.Current.Request.IsAuthenticated
Then
Dim authSection AsAuthenticationSection =
HttpContext.Current.GetSection("system.web/authentication")
If authSection.Mode =AuthenticationMode.Forms Then
LogonFormsGuest()
ElseIf authSection.Mode= AuthenticationMode.Windows Then
LogonWindowsGuest()
End If
End If
End Sub
Figure 6: Determinewhich type of guest account to authenticate.
Instead of invoking LogonFormsGuest as discussed earlier,the LogonWindowsGuest method is invoked when Windows authentication isconfigured (see Figure 7).
Private Sub LogonWindowsGuest()
Dim refToken As IntPtr =0
Dim ret As Integer
Dim domain As String =
ConfigurationManager.AppSettings("domain")
Dim userName As String =
ConfigurationManager.AppSettings("guestUser")
Dim password As String =
ConfigurationManager.AppSettings("guestPassword")
ret =LogonUser(userName, domain, password,
LogonType.LOGON32_LOGON_NETWORK,
LogonProvider.LOGON32_PROVIDER_DEFAULT, refToken)
If ret <> 0 Then
Dim user As IIdentity= New WindowsIdentity(refToken)
Dim p AsWindowsPrincipal = New WindowsPrincipal(user)
HttpContext.Current.User = p
Else
Dim err As Integer =Marshal.GetLastWin32Error()
' TODO: somethingwith error message
End If
End Sub
Figure 7: TheLogonWindowsGuest method is invoked when Windows authentication is configured.
This time the Win32 API function, LogonUser, authenticatesthe user and creates a valid Windows token. This token is wrapped in aWindowsPrincipal, as opposed to a GenericPrincipal as illustrated earlier.
What It All Means
Now that you have a module that will grant guest accountaccess for unauthenticated users, you can limit what your guests can see oneach page with role-based security. You can use the LoginView control tospecify which page elements should be visible to guests, restrict pages bydenying guests, and perform runtime role-based security checks that eitherallow or prevent guests from accessing functionality. The code sample accompanyingthis article illustrates these ASP.NET 2.0 features in conjunction with theGuestAuthenticationModule ... so have a look and enjoy!
Additional Resources
http://msdn.microsoft.com/msdnmag/issues/02/05/asp/
http://msdn.microsoft.com/library/en-us/dnpag2/html/PAGExplained0002.asp
http://msdn.microsoft.com/library/en-us/dnpag2/html/PAGExplained0001.asp
http://msdn.microsoft.com/library/en-us/dnpag2/html/PAGHT000025.asp
http://msdn.microsoft.com/msdnmag/issues/05/04/Security/
If you have questions or comments regarding this column, orany other ASP.NET topics, please drop me a line at [email protected].Thanks for reading!
C# and VB.NET codeexamples are available for download.
Michele LerouxBustamante is Chief Architect at IDesign Inc., Microsoft Regional Directorfor San Diego, Microsoft MVP for XML Web services, and a BEA TechnicalDirector. At IDesign Michele provides training, mentoring, and high-endarchitecture consulting services, specializing in scalable and secure .NETarchitecture design, globalization, Web services, and interoperability withJava platforms. She is a board member for the International Association ofSoftware Architects (IASA), a frequent conference presenter, conference chairof SD s Web Services track, and a frequently published author. She is currentlywriting a book for O Reilly on the Windows Communication Foundation. Reach herat http://www.idesign.net or http://www.dasblonde.net.
About the Author
You May Also Like