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
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   
GeneralRequest ErrormemberMember 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 Errormemberserjoka28-Sep-09 1:51 
Sorry, but... where is the error ?
 
You posted it some month ago. Did you fixed the problem ?
GeneralRe: Request ErrormemberMember 250733529-Sep-09 3:44 
Hello
Yes,I solve this problem and solve it.
Thanks
Questionmissing cookies?memberTheMountain12-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"memberfeifei_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"memberhhxs9-Jul-09 22:07 
Two types of webcontrols are not allowd to be using for custom authentication. first is Login and second is ScripManager(Atlas). I have tried to use one of the above, but AuthentionRequest will be revoked mang times when I just browse the "Login.aspx". and so far I have not found the solution.
GeneralRe: AutoPostBack="True"groupTHE SK22-Feb-11 3:51 
hi
have u found the solution when u r using scriptmanager.
if yes , please share?
Generaldon`t download!memberfeifei_200518-Dec-07 23:52 
I don`t download!
hello

QuestionAuthentication Modemembersergiosulpizio@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 Modememberbrharsh14-Jun-07 11:13 
I get the same error - trying to get this going in 2.0
have the same error in another example casting from FormsIdentity to CustomIdentity
 

 
hey

AnswerRe: Authentication ModememberSergio Pereira19-Jul-07 6:08 
I haven't tried this, but check that you do not have FormsAuthentication turned on in your web.config.
I think this will turn it off :
 


 
... the rest ...
...


 
_______________________
Sergio Pereira

GeneralRe: Authentication ModememberByttencourt25-Nov-09 0:14 
I've try this but still having same error. There is an error when Cassini (Visual Studio Web Server) try to serialize the CustomPrincipal object. Does anybody have a solution for this??
QuestionAnd Logout?members.belia14-Mar-06 21:56 
How can I implement the logout function with your customauthentication?
AnswerRe: And Logout?memberboonkerz10-Sep-06 9:47 
I have the same Problem Smile | :)
GeneralRe: And Logout?memberBostonIdiot4-Oct-08 7:22 
Wouldn't removing the cookie serve as logging out?
 
I am just guessing....
GeneralRe: And Logout?memberVishwamr150625-Nov-09 3:51 
Session.Abandon();
FormsAuthentication.SignOut();
Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", ""));
QuestionIsInRole - no longer working?memberluzer23-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 caughtmemberJon 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 authorizationmemberJimmy 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 Problemmemberarulraj00725-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 ProblemmemberSUPER_ZORRO27-Jan-06 0:30 
Try add theis code to CustomAuthenticationModule
public void Init(HttpApplication httpapp)
		{
			this.app = httpapp;
			app.AuthenticateRequest += new EventHandler(this.OnAuthenticate);
			app.BeginRequest+= new EventHandler(this.OnBeginRequest);
		}
 
		void OnBeginRequest(object sender, EventArgs e)
		{
			app = (HttpApplication)sender;
			HttpRequest req = app.Request;
			HttpResponse res = app.Response;
 
			if(req.Cookies.Count==0) return; 
		
			string cookieName = ConfigurationSettings.AppSettings[CustomAuthentication.AUTHENTICATION_COOKIE_KEY];
			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 = ConfigurationSettings.AppSettings[CustomAuthentication.AUTHENTICATION_COOKIE_EXPIRATION_KEY];
			if(cookieExpr == null || cookieExpr.Trim() == String.Empty)
			{
				throw new Exception("CustomAuthentication.Cookie.Timeout entry not found in appSettings section section of Web.config");
			}
 
			HttpCookie cookie = req.Cookies[cookieName.ToUpper()];
			if(cookie==null) return;
			cookie.Expires = DateTime.Now.AddMinutes(int.Parse(cookieExpr));
		}
 

 


GeneralConfiguration Errorsussabee-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 Errorsussabee-fish19-Aug-04 7:19 
I found my own solution.
 
I'm new to .NET and I didn't realize I needed to create a .dll from the CustomSecurity download and then add that to my references.
 
It all works fine now. I'm just working on the logoff scenario now.
 
abee
GeneralRE: Custom Auth bet with no cookiesmemberpughster23-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!memberbhardman23-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

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

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