Introduction

This article talks about ASP.NET the custom Forms Authentication mechanism. We will see how we can use
a custom database in unison with the ASP.NET Forms Authentication
mechanism to provide the desired level of security to our applications.

Background

We have already seen how we can use ASP.NET Roles and Membership classes to provide authentication and authorization in our applications
(refer this).
Also, we know about the server controls provided by ASP.NET to facilitate easy implementation of Roles and Membership functionalities in our application.

These Roles and Membership classes come in very handy when we want to provide authentication and authorization
in our applications. ASP.NET also provides a way to implement custom Roles and
Membership to take more granular control over things.

We might still find ourselves in situations where we need to have our own database for tracking users and their roles. The reasons could be:

We have an existing database and we are trying to implement an application using that.

The Roles and Membership functionality is overkill for our application.

The Roles and Membership functionality is not sufficient for our applications and we need custom data.

In all the above scenarios, we find the need to use ASP.NET Forms Authentication but according to our database schema and our custom written code to handle user management.
This is where the beauty of ASP.NET is shown. ASP.NET provides us fully functional and robust functionality for authentication and authorization and yet
it gives us the possibility of taking full control and having full customization of the functionalities (which is actually not limited to Authentication related stuff,
pretty much all areas of ASP.NET does that).

So what we are trying to do can easily be achieved by having a custom Forms Authentication mechanism. Using
a custom Forms Authentication mechanism we can have our
own tables to manage users and yet use the existing form authentication mechanism using GenericPrincipal. Let us try to work out an example and see how this can be done.

Using the code

Let us start by getting our requirements clear. We will be implementing a website that has
the following structure:

The top level default page and the login page can be accessed by anyone. The pages in other folders can only be accessed by users in respective roles. Just to understand it in a clear
way, let us look at the web.config of the PlatinumUser folder.

We have a user table and every user can have multiple Roles. We will be using this table to authenticate the users.

Note: The database is neither optimized nor normalized as that was not the main intent of this article.
A real world example of database will be more optimized and perhaps more complex. The passwords will not be in clear text for sure too.

Now before going ahead, let us look at the little algorithm that we will be following to implement custom forms authentication and achieve the desired functionality.

Configure the application to use Forms Authentication.

Create a login page.

Whenever a user tries to access the restricted area, push him to the Login page.

When the user tries to login, verify his credentials using the database.

If the login is successful, keep the username and his Roles in a Session variable to use it further.

Create an authentication ticket for the user (an encrypted cookie). We can have this persistent or non persistent.

Facilitate the User/Roles extraction using the authentication ticket.

Use the user/Roles found in the last step and create a Principal using that so that the ASP.NET
Forms Authentication mechanism can use this data.

Note: Each point in this algorithm can be found emphasized in the explanation below.

We have the algorithm worked out. We will now look at how each step is done. Let us start with Configure the application to use Forms authentication.
To do this we need to set the authentication mode in the web.config file.

This web.config indicates that Forms Authentication will be used in this application.
The default loginUrl is Login.aspx. The name of the authentication ticket cookie will be MyCustomAuthentication and
if this cookie is created in non persistent mode, then the timeout period for that is 30 minutes.

Now we have our website set to use Forms authentication and we have also specified the default login page so we also took care of #3 of our algorithm, i.e., Whenever
user tries to access the restricted area, push him to the Login page. The next thing is to
Create a login page.

Once this login page is done, we need to have some mechanism so that When
the user tries to login, verify his credentials using the database. To do this, I have created a small helper class. Ideally this logic will be placed in a Data Access layer or might be present in ORMs.
But for the sake of simplicity I have created a
small helper class do to this.

Now whenever the user tries to log in, If the login is successful, keep the username and his Roles in Session variable to use it further, and also we need to
Create an authentication ticket for the user (an encypted cookie). So let us do all this in the Button click event of the login page.

Now before going further, there is one thing to understand. When Forms authentication is being used, whenever the need for authentication arises, the ASP.NET framework
checks with the current IPrinciple type object. The user ID and Role contained in this IPrinciple type object will determine whether the user is allowed
access or not.

So far we have not written code to push our user specific details in this principle. To do that we need to override a method called FormsAuthentication_OnAuthenticate
in global.asax. This method is called each time ASP.NET framework tries to check
authentication and authorization with respect to the current Principle.

What we need to do now is to override this method. Check for the
authentication ticket (since the user has already been validated and the ticket was created) and then
supply this User/Role information in the IPrinciple type object. We can implement our custom
Principle type too but to keep it simple, we will simply create
a GenericPriciple object and set our user specific details into it [#7 and #8 of our algorithm are covered].

Now when we run the application, we will see that the authorization mechanism is working as specified in the
respective web.config files in perfect unison with our own custom database and authentication logic.

Note: Running the sample application, looking at code, and debugging the application is strongly recommended
to get a full understanding of the above mentioned concepts.

Points of interest

When I came across the requirements of having custom forms authentication, I was
rather intimidated. It was
partly because I only knew how the ASP.NET default membership provider worked and partly because the hype about how difficult it
is to do custom forms authentication. When I sat on it, I found it rather
straightforward and easy.

There is one more major area that is also worth looking into to understand the authentication and authorization
mechanism completely and that is implementing a custom Membership Provider. Perhaps I will do that in a separate article.

Share

About the Author

I Started my Programming career with C++. Later got a chance to develop Windows Form applications using C#. Currently using C#, ASP.NET & ASP.NET MVC to create Information Systems, e-commerce/e-governance Portals and Data driven websites.

My interests involves Programming, Website development and Learning/Teaching subjects related to Computer Science/Information Systems. IMO, C# is the best programming language and I love working with C# and other Microsoft Technologies.

Comments and Discussions

Hi !!
Thanks a ton, for such a good article as this one helped me a lot being a beginner.

Question: How can I implement it without showing a particular tab instead directly redirect the respective user to the authorized page??
I went through a lot of article but I didn't got in the basic approach like in this article.Also, I went through your this article where you have used WSAT,So my question is : how can we apply the same role and membership without using WSAT ??(Is it possible?)

The first question where you asked about the process of redirecting the user to their respective page without showing any navigation element, I think you can have a dispatcher class/page which will be called when login is successful. this page will then check the type of the user and send it to the respective location. You will have to write the logic in this page manually by keeping a track of roles and their redirect location.

To answer the second question, the authorization has 2 parts. First having the users with the Roles(which we do in database typically) and configuring the website to run pages based on specific roles.

Now to do the second part, there is one way where I can write code in each page which will check the role and based on that decide whether or not to open the page(imperative approach) but this approach is not very elegant and has maintenance nightmares.

The other way we can do it is by specifying the roles and rights in web.config(declarative approach). This approach is preferable and WSAT is just a tool to put our authorization mappings in web.config. We can do it by hand too but WSAT provides a better abstraction.

Twenty years from now you will be more disappointed by the things that you didn't do than by the ones you did do. So throw off the bowlines. Sail away from the safe harbor. Catch the trade winds in your sails. Explore, Dream. Discover.

I think I might have used the imperative approach in one of my articles (on a very rudimentary level, where I check for user and role in page_load of every page). I cant remember which article but could you please check the two articles named "yaBlogEngine" and "yaMessaging" list of my articles. I think it should be in one of them.

P.S. I still dont recommend that approach.

Twenty years from now you will be more disappointed by the things that you didn't do than by the ones you did do. So throw off the bowlines. Sail away from the safe harbor. Catch the trade winds in your sails. Explore, Dream. Discover.