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

Custom Authentication provider by implementing IHttpModule, IPrincipal and IIdentity

By , 2 Nov 2003
 

Introduction

For a real-world Enterprise application, security plays an important role. Forms-based authentication is a popular technique used by many Web sites. With ASP.NET, writing forms authentication is like a breeze. Forms Authentication provider in ASP.NET exposes cookies-based authentication services to applications. But for some of the applications due to the nature of the security requirements, Forms Authentication provider is not a very good fit. Writing your own Custom Authentication provider offers a solution for such applications. With very little code and effort, you can have a role-based authentication system that is platform-agnostic.

Background: Forms Authentication provider - Not a good fit

Lets take a scenario - An application wants to store the following information for a User:

  1. Username
  2. User Primary key (primary Key of the User table)
  3. E-mail
  4. User Full name
  5. Is user an Administrator
  6. Is user authenticated
  7. Roles for the User

Forms Authentication provider uses the FormsAuthenticationModule, FormsIdentity, FormsAuthenticationTicket and GenericPrincipal classes. The application can store the UserName and roles information in the FormsAuthenticationTicket. But this application needs to store other information, which cannot be done using FormsAuthenticationTicket or FormsIdentity class. To achieve this we can create a custom Identity class by implementing IIdentity interface. However, on each Request application needs to get the User's data from the underlying data source (SQL Server, XML , LDAP ) and create a CustomIdentity object with all the details. The code in Login page will be something like this (Listing 1):

Listing 1 - Login.aspx

//Validate User
//SecurityManager is a helper class of your application
if(SecurityManager.ValidateLogin(txtUserName.Text, txtPassword.Text))
{
    //Get a CustomIdentity object with all the details 
    //for the authenticated user
    CustomIdentity identity = SecurityManager.GetUserIdentity(
          txtUserName.Text);
    if(identity != null && identity.IsAuthenticated)
    {
        //Get user Roles
        ArrayList roles = SecurityManager.GetUserRoles(txtUserName.Text);
        //Create a CustomPrincipal object
        CustomPrincipal newUser = new CustomPrincipal(identity, roles);
        Context.User = newUser;
        //Redirect user to the requested page
        FormsAuthentication.RedirectFromLoginPage(txtUserName.Text, False);
    }
}

The code in Global.asax will be something like this (Listing 2):

Listing 2 - Global.asax.cs

//Implement an Authentication Request Handler to Construct
// a GenericPrincipal Object
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    HttpCookie authCookie = Request.Cookie[
             FormsAuthentication.FormsCookieName];
    if(authCookie != null)
    {
        //Extract the forms authentication cookie
        FormsAuthenticationTicket authTicket = 
               FormsAuthentication.Decrypt(authCookie.Value);
        // Create an Identity object
        CustomIdentity id = SecurityManager.GetUserIdentity(authTicket.Name);
        //Get user Roles
        ArrayList roles = SecurityManager.GetUserRoles(txtUserName.Text);
        //Create a CustomPrincipal object
        CustomPrincipal newUser = new CustomPrincipal(identity, roles);
        Context.User = newUser;
    }
}

For more details on the above example please visit MSDN - How To: Implement Iprincipal

This approach works fine for some applications but think of the unnecessary overhead it has in retrieving the user details on each Request. If your application can live with such a design than be it. But for some of the very interactive, performance savvy applications, this approach is not suitable. Here, writing a Custom Authentication provider serves the purpose.

Custom Authentication module

Implementing the IHttpModule interface allows you to include custom events that participate in every request made to your application. Custom Authentication can be achieved by implementing IHttpModule and writing a Custom HttpModule. I have created a Custom Authentication provider by implementing IHttpModule. My CustomAuthentication provider has the following classes:

  1. CustomIdentity.cs - Implements IIdentity and contains User details (you can change it as per your requirements).
  2. CustomPrincipal.cs - Implements IPrincipal and represents a Custom Principal object (you can change it as per your requirements).
  3. CustomAuthenticationModule.cs - Implements IHttpModule and handles AuthenticateRequest event of the HttpApplication.
  4. CustomAuthentication.cs - Provides static helper methods like creating a encrypted Authentication string etc.
  5. CustomEncryption.cs - Utility class for encrypting and decrypting authentication string (you can change it to implement your own encryption algo).

    The Custom Authentication provider needs the following entries in Web.Config appSettings section:

Listing 3 - Web.config

<!-- Entries required for CustomAuthenticationModule -->
<appSettings>
    <!-- Parameter for the Login page URL (Required) -->
    <add key="CustomAuthentication.LoginUrl" value="/Login.aspx" />
    <!-- Parameter for the Authentication cookie Name (Required) -->
    <add key="CustomAuthentication.Cookie.Name" value=".CUSTOM_AUTH" />
    <!-- Parameter for Timeout for Cookie expiration in minutes 
       (Optional- persist cookie across browser sessions) -->
    <add key="CustomAuthentication.Cookie.Timeout" value="2" />
</appSettings>

Sample Application

There is a sample application, which uses this CustomAuthentication module. To add a custom HttpModule, add the following entries to the Web.config file:

Listing 4 - Web.config

<!-- Add a Custom Authentication module -->
<httpModules>
    <add name="CustomAuthenticationModule" 
       type="CustomSecurity.CustomAuthenticationModule, CustomSecurity" />
</httpModules>

Add the required entries for CustomAuthenticationModule as given above in Listing 3. Please see the required code in login page. This is the only code you need to write (Listing 5).

Listing 5 - Login.aspx

//Write your own Authentication logic here
if(this.username.Text != "" && this.password.Text !="")
{
    //Write your own code to get the User Roles
    ArrayList roles = new ArrayList();
    roles.Add("Manager");

    if(this.username.Text == "superuser")
        roles.Add("Administrator");

    roles.Add("ITUser");

    //Convert roles into pipe "|" separated string
    System.Text.StringBuilder strRoles = new System.Text.StringBuilder();
    foreach(string role in roles)
    {
        strRoles.Append(role);
        strRoles.Append("|");
    }

    //Create a CustomIdentity object
    CustomIdentity userIdentity = new CustomIdentity(this.username.Text, 
        1, true, true, this.username.Text, 
        "someuser@some.com", strRoles.ToString());
    //Create a CustomPrincipal object
    CustomPrincipal principal = new CustomPrincipal(userIdentity, roles);
    Context.User = principal;
    //Redirect user
    CustomAuthentication.RedirectFromLoginPage(userIdentity);
}

You can access the CustomIdentity and CustomPrincipal object in any aspx page or in any class of middle layer of your application using the following code (Listing 6):

Listing 6 - Home.aspx

//Get the CustomIdentity in aspx pages from the HttpContext
this.user1.Text = ((CustomIdentity)Context.User.Identity).UserFullName;

//Get the CustomIdentity in any class in middle layer from the Thread
this.user2.Text = ((CustomIdentity)
    Thread.CurrentPrincipal.Identity).UserFullName;
role.Text =  Thread.CurrentPrincipal.IsInRole("Administrator").ToString();

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

About the Author

I Piscean
Web Developer
United States United States
Member
No Biography provided

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

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralCan you specify a license for this code. Pinmemberhhearst24 Apr '11 - 10:07 
Hi,
 
Thanks for the useful example. Unfortunately, you did not specify a license for the code. Is this code under the CPOL (Code Project License) or some other license? Can I use this in commercial applications?
 
Thanks
-Hal
GeneralMy vote of 5 PinmemberWestieBoy14 Feb '11 - 21:14 
WOW! Needed to hook web app into a SQL DB with an existing authentication model...and this is just the ticket!
GeneralMVC PinmemberPandiani18 Nov '10 - 23:13 
Has anybody tried this technique ...on Asp.Net MVC ??
GeneralNice Job PinmemberHamed J.I25 Sep '10 - 22:25 
Thumbs Up | :thumbsup: It's very nice job. I Love handling something in depth
Hamed JI

QuestionAuthentication across subdomains and localhost? Pinmemberatlas_usr11 May '10 - 15:19 
How can I change this authentication module to handle applications deployed under sub-domains?
 
www.sample.com
www.1.sample.com
www.2.sample.com
 
Thanks
GeneralUse with standard Authorization module PinmemberAngus-AU8 Dec '09 - 15:50 
Some people may want to use this with the standard authorization module (UrlAuthorizationModule), so that you can use the <authorization /> section in web.config to configure which pages are protected and which are not.
 
To do this,
1. set <authentication mode="None" /> in web.config
2. remove the redirect code in the OnAuthenticate handler in CustomAuthenticationModule (so if the user is not valid, just do nothing)
3. During the request, UrlAuthorizationModule will check if the current pricipal is allowed to access the requested resource. If it is not allowed, UrlAuthorizationModule will set request.statuscode = 401 and call HttpApplication.CompleteRequest.
4. add an event handler for the Application.EndRequest event in CustomAuthenticationModule
5. In that event handler, check if the status code == 401 (unauthorized). If it is, then do your redirect to the login page.
6. Now your authentication is controlled by the CustomAuthenticationModule, but you can still use the standard way of setting your Authorization in web.config!
GeneralRe: Use with standard Authorization module Pinmemberreginaldo19829 Dec '09 - 6:30 
GeneralRe: Use with standard Authorization module PinmemberAngus-AU10 Dec '09 - 13:17 
GeneralRequest Error PinmemberMember 250733518 Jul '09 - 0:23 
Hello.
i use FormsAuthentications but i give this error:
if i don't use Request["ID"] in pages beacause raise this Error:
CustomSecurity.CustomPrincipal,CustomSecurity, Version=1.0.3486.25611, Culture=neutral, PublicKeyToken=null
 

This error is very Bad
GeneralRe: Request Error Pinmemberserjoka28 Sep '09 - 1:51 
GeneralRe: Request Error PinmemberMember 250733529 Sep '09 - 3:44 
Questionmissing cookies? PinmemberTheMountain12 Nov '08 - 10:04 
by the time I get to the CustomAuthenticationModule, HttpContext.Current.Request.Cookies.Count == 0;...and nothing happens. I have a VERY simplified version (since I'm only authenticating, not authorizing) anything at this point.
 
void OnAuthenticate(object sender, EventArgs e)
{
 
string cookieName = Globals.Settings.AuthenticationCookieName;
if (cookieName == null || cookieName.Trim() == String.Empty)
throw new Exception(" CustomAuthentication.Cookie.Name entry not found in appSettings section section of Web.config");
 
app = (HttpApplication)sender;
HttpRequest req = app.Request;
HttpResponse res = app.Response;
 
if (req.Cookies.Count > 0 && req.Cookies[cookieName.ToUpper()] != null)
{
HttpCookie cookie = req.Cookies[cookieName.ToUpper()];
if (cookie != null)
{
string _loginID = cookie.Value;
CustomIdentity userIdentity = CustomAuthentication.Decrypt(_loginID);
CustomPrincipal principal = new CustomPrincipal(userIdentity, UserRoles.LoadForUser(_loginID));
app.Context.User = principal;
Thread.CurrentPrincipal = principal;
}
}

}
 
public static void RedirectFromLoginPage(CustomIdentity identity)
{
string cookieName = Globals.Settings.AuthenticationCookieName;
if (cookieName == null || cookieName.Trim() == String.Empty)
throw new Exception(" CustomAuthentication.Cookie.Name entry not found in appSettings section section of Web.config");
 
string cookieExpr = Globals.Settings.AuthenticationCookieExpiration;
 
HttpRequest request = HttpContext.Current.Request;
HttpResponse response = HttpContext.Current.Response;
 
string encryptedUserDetails = Encrypt(identity);
 
HttpCookie userCookie = new HttpCookie(cookieName.ToUpper(), encryptedUserDetails);
if (cookieExpr != null && cookieExpr.Trim() != String.Empty)
userCookie.Expires = DateTime.Now.AddMinutes(int.Parse(cookieExpr));
 
userCookie.HttpOnly = true;
 
response.Cookies.Add(userCookie);
 
string returnUrl = request["ReturnUrl"];
 
if (returnUrl != null && returnUrl.Trim() != String.Empty)
response.Redirect(returnUrl, false);
else
response.Redirect(request.ApplicationPath + "/content/default.aspx", false);
 
}
 
and my logon button event
protected void btnLogin_Click(object sender, EventArgs e)
{
// authenticate the user
if (string.IsNullOrEmpty(txtUserName.Text) == true || string.IsNullOrEmpty(txtPassword.Text) == true)
{
this.lblLoginMessage.Text = "Username & Password are both required.";
return;
}
 
CustomIdentity ident = Telligentz.Core.BLOC.User.ValidateUser(txtUserName.Text, txtPassword.Text);
CustomPrincipal princ = new CustomPrincipal(ident, UserRoles.LoadForUser(txtUserName.Text));
Context.User = princ;
if (ident != null)
CustomAuthentication.RedirectFromLoginPage(ident);
}
GeneralAutoPostBack="True" Pinmemberfeifei_200520 Dec '07 - 16:06 
Add a Control and AutoPostBack="True",
The Exception:
[SerializationException: unparsed members "CustomSecurity.CustomPrincipal, CustomSecurity, Version = 1.0.2911.19864, Culture = neutral, PublicKeyToken = null" type. ]
 
If AutoPostBack="false" is OK!
AutoPostBack
GeneralRe: AutoPostBack="True" Pinmemberhhxs9 Jul '09 - 22:07 
GeneralRe: AutoPostBack="True" PingroupTHE SK22 Feb '11 - 3:51 
Generaldon`t download! Pinmemberfeifei_200518 Dec '07 - 23:52 
I don`t download!
hello

QuestionAuthentication Mode Pinmembersergiosulpizio@yahoo.it19 Feb '07 - 0:51 
I get the following exception:
 
Unable to cast object of type 'System.Security.Principal.WindowsIdentity' to type 'CustomSecurity.CustomIdentity'.
 
I tried to change authentication mode but it doesn't work..
AnswerRe: Authentication Mode Pinmemberbrharsh14 Jun '07 - 11:13 
AnswerRe: Authentication Mode PinmemberSergio Pereira19 Jul '07 - 6:08 
GeneralRe: Authentication Mode PinmemberByttencourt25 Nov '09 - 0:14 
QuestionAnd Logout? Pinmembers.belia14 Mar '06 - 21:56 
How can I implement the logout function with your customauthentication?
AnswerRe: And Logout? Pinmemberboonkerz10 Sep '06 - 9:47 
GeneralRe: And Logout? PinmemberBostonIdiot4 Oct '08 - 7:22 
GeneralRe: And Logout? PinmemberVishwamr150625 Nov '09 - 3:51 
QuestionIsInRole - no longer working? Pinmemberluzer23 Feb '06 - 11:58 
hi
 
i would like to use the CustomIdentity class, to store a users PK, etc.
 
but, my code was working with both Windows and Form based auth.
 
in windows, the Context.User.Identity.IsInRole would work - checking the users AD groups- on the fly.
in Forms, it would check the roles stored in the cookie - that was populated off the DB.
 
this doesnt work anymore in windows mode.
 
any suggestion why not?
Generalextensions caught PinmemberJon Eden10 May '05 - 5:13 
Hi,
 
I'm wondering about the extensions captured by this technique. In IIS you set the specific file types that should be caught and handled by the aspnet_isapi.dll. Is there any good reason not to use the wildcard for any unknowns. I'm thinking that if people are uploading pdfs, jpgs, jpegs etc then eventually certain file types will be uploaded that don't get caught and hence a security risk appears....
 
Any thoughts?
 
Cheers,
 
Jon
QuestionHow does this work with web.config authorization PinmemberJimmy S29 Oct '04 - 23:21 
Works a treat for authentication, but seems to overide any authorization settings I have in my web.config file. Any ideas on how to make it authenticate only on pages/directories I specify in web.config?
GeneralExpiration Problem Pinmemberarulraj00725 Aug '04 - 3:09 
If I set the CustomAuthentication.Cookie.Expiration = 20 Mins.
Application Expires in 20 mins, no matter what you make server calls or not.
My understanding is If we make server call cookies expiration should be reset.
Can anyone let me know how to fix this problem or am I doing something wrong.
Thanks.
 
Arulraj
AnswerRe: Expiration Problem PinmemberSUPER_ZORRO27 Jan '06 - 0:30 
GeneralConfiguration Error Pinsussabee-fish19 Aug '04 - 4:58 
I'm getting an error when trying to run my application. It appear that when trying to load the httpModule it searches in the bin directory. Here's the output:
 
Configuration Error
Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.
 
Parser Error Message: File or assembly name ChangeControl.CustomSecurity, or one of its dependencies, was not found.
 
Source Error:
 

Line 68: <!-- The module we use for authentication -->
Line 69:
Line 70:
Line 72: <!-- //ChangeControl.CustomSecurity" /> -->

 
Source File: c:\inetpub\wwwroot\ChangeControl\web.config Line: 70
 
Assembly Load Trace: The following information can be helpful to determine why the assembly 'ChangeControl.CustomSecurity' could not be loaded.
 

=== Pre-bind state information ===
LOG: DisplayName = ChangeControl.CustomSecurity
(Partial)
LOG: Appbase = file:///c:/inetpub/wwwroot/ChangeControl
LOG: Initial PrivatePath = bin
Calling assembly : (Unknown).
===
 
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Post-policy reference: ChangeControl.CustomSecurity
LOG: Attempting download of new URL file:///C:/WINDOWS/Microsoft.NET/Framework/v1.1.4322/Temporary ASP.NET Files/changecontrol/c1cd7e7e/416a3947/ChangeControl.CustomSecurity.DLL.
LOG: Attempting download of new URL file:///C:/WINDOWS/Microsoft.NET/Framework/v1.1.4322/Temporary ASP.NET Files/changecontrol/c1cd7e7e/416a3947/ChangeControl.CustomSecurity/ChangeControl.CustomSecurity.DLL.
LOG: Attempting download of new URL file:///c:/inetpub/wwwroot/ChangeControl/bin/ChangeControl.CustomSecurity.DLL.
LOG: Attempting download of new URL file:///c:/inetpub/wwwroot/ChangeControl/bin/ChangeControl.CustomSecurity/ChangeControl.CustomSecurity.DLL.
LOG: Attempting download of new URL file:///C:/WINDOWS/Microsoft.NET/Framework/v1.1.4322/Temporary ASP.NET Files/changecontrol/c1cd7e7e/416a3947/ChangeControl.CustomSecurity.EXE.
LOG: Attempting download of new URL file:///C:/WINDOWS/Microsoft.NET/Framework/v1.1.4322/Temporary ASP.NET Files/changecontrol/c1cd7e7e/416a3947/ChangeControl.CustomSecurity/ChangeControl.CustomSecurity.EXE.
LOG: Attempting download of new URL file:///c:/inetpub/wwwroot/ChangeControl/bin/ChangeControl.CustomSecurity.EXE.
LOG: Attempting download of new URL file:///c:/inetpub/wwwroot/ChangeControl/bin/ChangeControl.CustomSecurity/ChangeControl.CustomSecurity.EXE.
 


GeneralRe: Configuration Error Pinsussabee-fish19 Aug '04 - 7:19 
GeneralRE: Custom Auth bet with no cookies Pinmemberpughster23 Apr '04 - 4:35 
It would help me if you already have thoughts on this code when applied to cookieless sessioning (Web.config / sessionState / cookieless="true").
 
If you can get me started with some idea on that I will echo-back my final results in a later post.
 
pughster
GeneralNice code! Pinmemberbhardman23 Apr '04 - 2:51 
Any thoughts on how to go about how to limit access to only certain pages or folders in the site?   With regular forms auth I've been using the <location> tag in web.config.   I suppose a similar solution is in order, just trying to decide where best to put the checks.
 
I added the following signout code to CustomAuthentication.cs, which seems to work ok.
 
          public static void SignOut()
          {
               string cookieName = ConfigurationSettings.AppSettings[AUTHENTICATION_COOKIE_KEY];
 
               HttpRequest request = HttpContext.Current.Request;
               HttpResponse response = HttpContext.Current.Response;
 
               HttpCookie userCookie = new HttpCookie(cookieName.ToUpper(), null);
               userCookie.Expires = DateTime.Now.AddDays(-1);
               response.Cookies.Add(userCookie);
 
               string returnUrl = request["ReturnUrl"];
               if(returnUrl != null && returnUrl.Trim() != String.Empty)
               {
                    response.Redirect(returnUrl, false);
               }
               else
               {
                    response.Redirect(request.ApplicationPath + "/default.aspx", false);
               }
          }

 
/brettman
Generalweb service Pinmemberqjia11 Mar '04 - 3:25 
How does web service use the Custom Authentication ?
thx
Jason
Generalmultiple user login PinmemberTata Taufik23 Feb '04 - 15:01 
Hi,
 
I observe that the custom authentication only works for single user login in one PC. It's simply because the same cookie name used. Is there any solution to enable multiple user login in one PC?
QuestionUpdated Version? PinmemberOzzie23 Jan '04 - 8:00 
I was wondering if you had an updated version of this article? I had found a bug or 2 while compiling, and I could not quite get the cookie to register correctly.
 
A nice addition to the code would be a full scenario - go to a page which forces you to logon, with a logoff page so you could demonstrate removing the cookie from the browser session.
 
Just an idea. Nice article - I have not seen any similiar.
 
It someone has, could they point them out to me?Cool | :cool:
 
Thanks,
Doug
 
Don't spend your life just wishing - http://www.AllGiftRegistry.com

Questionauthentication mode? PinmemberLarkin22 Jan '04 - 10:27 
In your VS project download, the web.config file has authentication mode set to "Windows". Is this correct or is this a mistake? Doesn't make much sense to set authentication mode to Windows when you are implementing a custom authentication scheme.
AnswerRe: authentication mode? Pinmembernetclectic20 Feb '04 - 4:19 

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

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 3 Nov 2003
Article Copyright 2003 by I Piscean
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid