Thursday, March 20, 2014

ASP.NET MVC 4 Built-in Form Based Authentication

Given that I have some background knowledge of ASP.NET Web Forms Authentication, I decided to try experimenting on MVC 4 authentication/authorization framework. Most of the articles in the internet regarding forms authentication in ASP.NET MVC are custom based approach and less on the built-in technology which is WebData.WebSecurity usage. Not until I found these articles:
a. Forms Authentication Customized
b. Introduction to forms based authentication in MVC 4
c. Authenticating Users In Asp.net MVC 4
So, to cut the story short, I made an application which utilized the built-in WebMatrix authentication in MVC 4. The first thing to do, is to familiarize those articles to get a good grasp on the topic. The setup of my application are as follows:
1. Web.config
     1.1 Added authentication and custom errors
  <authentication mode="Forms">  
    <forms loginUrl="~/Account/Login" timeout="2880"/>    
   </authentication>  
   <authorization>  
     <allow users="*"/>     
   </authorization>  
   <customErrors mode="On" defaultRedirect="~/Error/Error">  
    <error statusCode="404" redirect="~/Errors/Http404" />  
   </customErrors>  
     1.2 Replaced the connection string to point it with an existing database. In my case it's northwind database.
  <add name="DefaultConnection" connectionString="Data Source=testServer;Initial Catalog=NORTHWIND;User ID=sa;Password=testsql2012;" providerName="System.Data.SqlClient" />  
Here's a sample image that Northwind has added the built in authentication/authorization tables.
2. In the built-in account controller Register() action, I added a code to map a default role which is a "Customer" to a new user. The other role is "Administrator" which will be set explicity through the database or admin page.
Code:
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                // Attempt to register the user
                try
                {                    
                    WebSecurity.CreateUserAndAccount(model.UserName, model.Password);     
                    //assign default role to new user.     
                    Roles.AddUserToRole(model.UserName, "Customer");                   
                    WebSecurity.Login(model.UserName, model.Password);
                    return RedirectToAction("Index", "Customer");
                }
                catch (MembershipCreateUserException e)
                {
                    ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
                }
            }

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

3. I added an Errors Controller(ErrorsController.cs) with the following methods that handles 404 and unauthorized exceptions.
Code:
protected override void HandleUnknownAction(string actionName)
        {
            if (this.GetType() != typeof(ErrorsController))
            {
                var errorRoute = new System.Web.RoutingRouteData(); //ako gi change added assembly
                errorRoute.Values.Add("controller", "Errors");
                errorRoute.Values.Add("action", "Http404");
                errorRoute.Values.Add("url", HttpContext.Request.Url.OriginalString);
                View("Http404").ExecuteResult(this.ControllerContext);
            }
        }

        public ActionResult Http404()
        {
            return View();
        }

        public ActionResult Error()
        {
            return View();
        }

        public ActionResult InvalidUser()
        {
            return PartialView("_InvalidUser");
        }

        public ActionResult UnAuthorizedUser()
        {
            return PartialView("_UNAuthorizedUser");
        }

4. I added the class AuthorizeUsersAttribute in my Models folder and decorated my controller actions in CustomerController.cs with attribute as presented in this post: Redirect Unauthorized Access to Custom View in MVC 4
Code:
 [AuthorizeUsers(Roles = "Administrator", NotifyUrl = "/Errors/UnAuthorizedUser")]
        [HttpPost]
        public ActionResult Create(Customer customer)
        {
            if (ModelState.IsValid)
            {
                db.Customers.Add(customer);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            return View(customer);
        }

5. I added partial view in shared folder for notifying unauthorized users.
_UNAuthorizedUser.cshtml
6. Add this in global.asax or RouteConfig.cs RegisterRoutes() method
Code:
WebMatrix.WebData.WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);

7. I didn't revise anything in AccountModels.cs
8. In my sample project, the administrator role can create, edit, delete and view customer information. The customer role is limited to viewing only. Here are some sample images:
     8.1 The default page automatically redirects to the login page for authorization and authentication.      8.2 The main view for users with admin or customer role.

     8.3 Page restriction on a view accessed by a customer user.

     8.4 Edit Page authorized to admin users.

:)

0 comments:

Post a Comment