Friday, 15 May 2015

Custom Forms Authentication and Authorization in ASP.NET MVC Application

Introduction

In this we will discuss about the ASP.NET Roles and Membership API from MVC perspective. We will see how we can implement custom forms authentication in an ASP.NET MVC application. 

Authentication and Authorization

Authentication means validating users. In this step, we verify user credentials to check whether the person tying to log in is the right one or not. Authorization on the other hand is keeping track of what the current user is allowed to see and what should be hidden from him. It is more like keeping a register to what to show and what not to show to the user.

Whenever a user logs in, he will have to authenticate himself with his credentials. Once he is authenticated, he will be authorized to see resources/pages of the website. Mostly these two concepts go together.

Type of Authentications

Before moving ahead, let us first see the two main type of authentications that are used mostly in ASP.NET applications.

Windows authentication: In this mode, the users are authenticated on their Windows username and password. This method is least recommended in an internet scenario. In an internet scenario, we should always use "Forms based authentication".
Forms based authentication: In this type of authentication, the user will explicitly have to provide his credentials and these credentials, once verified by the server, will let the user to log in.


We will be discussing the form authentication in details in the rest of the article.

Forms Authentication

To enable forms authentication we need to perform following steps in our application.
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.
Custom Forms Authentication:

To implement our own authentication and authorization mechanism we will have take care of implementing all the steps required for forms authentication that we discussed earlier in the article. So let us create an empty MVC4 application and see how we can implement custom forms authentication.

Configuring Forms Authentication


Now the first thing that we need to do is to configure the application to use the Forms authentication. This can be done in the web.config file.


<authentication mode="Forms">
      <forms loginUrl="~/Login/Index" timeout="2880" />

</authentication>

Preparing the user Database

Let us now create a small database that we will use to perform authentication. 



Now to perform data access let us use entity framework so that we don't have to write all the boilerplate code required to create our model and data access logic. The generated entity for our database will look like:

Creating the Controllers and Views

let us now go ahead and create a controller that will take care of the authentication logic. We will create the functionality for Login and Logout but other functionality are user creation and password change can be easily implemented on same lines(its the matter of validating the user Model and performing CRUD operations on the table after encryption or hashing and salting).

Here is our controller with login and logout action:

       public ActionResult Index()
        {
            ViewBag.Title = "Login";
            return View();
        }

        [HttpPost]
        public ActionResult Index(User model, string returnUrl)
        {
            // Lets first check if the Model is valid or not
            if (ModelState.IsValid)
            {
                using (SampleDbEntities entities = new SampleDbEntities())
                {
                    string username = model.UserName;
                    string password = model.Password;

                    // Now if our password was enctypted or hashed we would have done the
                    // same operation on the user entered password here, But for now
                    // since the password is in plain text lets just authenticate directly

                    bool userValid = entities.Users.Any(user => user.UserName == username && user.Password == password);

                    // User found in the database
                    if (userValid)
                    {

                        FormsAuthentication.SetAuthCookie(username, false);
                        if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/") && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
                        {
                            return Redirect(returnUrl);
                        }
                        else
                        {
                            return RedirectToAction("Index", "Home");
                        }
                    }
                    else
                    {
                        ModelState.AddModelError("", "The user name or password provided is incorrect.");
                    }
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();
            return RedirectToAction("Index", "Login");
        }

Now in the above code when the user tries to login we will check if the user with the given user credentials exist in the user database or not. If it exist we set the authentication ticket and move forward. Now if our password was encrypted or hashed we would have done the same operation on the user entered password before checking in database, but since the password is in plain text lets just authenticate directly.

Now before moving ahead let us look at the view that will take the user credentials and perform the authentication.



Let us now create a simple Controller which will contain actions which can be accessed by specified Roles and Users.

Facilitating Roles extraction using the authentication ticket

Now to use the roles specified in our database, 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's Role details in this principle object. 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

 Now we have all the steps required to implement the custom forms authentication in place. We have successfully implemented custom forms authentication in an ASP.NET MVC application.
   Now run your Application . without login click on About or Click on Contact Link , it will redirect you to the Login Page. Login with admin, you can not access About and Contact Page because I have'nt Provided Access to Admin for these Pages. now login with user or guest you can access About Page but not Contact Page. Login with hr you can not access About and Contact Page. now Login with hr2 now you can access Contact Page because I have Provided Access to only hr2 with HR Role.

Note: This article contains code snippets that are only for demonstration of concepts of the article and it does not follow any best practices .

Click here to Download Project
 This article has been written from a beginner's perspective. hope this has been informative. leave a comment if you liked it.

0 comments:

Post a Comment

Topics

ADO .Net (2) Ajax (1) Angular Js (17) Angular2 (24) ASP .Net (14) Azure (1) Breeze.js (1) C# (49) CloudComputing (1) CMS (1) CSS (2) Design_Pattern (3) DI (3) Dotnet (21) Entity Framework (3) ExpressJS (4) Html (3) IIS (1) Javascript (6) Jquery (9) Lamda (3) Linq (11) Mongodb (1) MVC (48) NodeJS (7) RDLC (1) Report (1) Sql Server (29) SSIS (3) SSRS (2) UI (1) WCF (12) Web Api (9) Web Service (1) XMl (1)