Sunday, May 15, 2016

Notes on Roles with ASP.NET Identity on MVC

How do you handle authentication and authorization (or “security”) on a small scale ASP.NET MVC site? The other day I talked to someone who was experiencing problems with setting up security. He set up a MVC project in Visual Studio and couldn’t get it do what he wanted it to do.

Membership Provider

Back in the day there was the Membership Provider, which was easy to use but isn’t hardened enough for these modern times. But was cool, you could set up users and roles with the ASP.NET Web Configuration Tool, you could control access to Controller methods with the Authorize attribute, so if I wanted Admins to access AdminOnly, I could do this:

        [Authorize(Roles = "Admin")]
        public ActionResult AdminOnly()
        {
            return View();
        }

The guy talked to above probably wanted something that behaves like the Membership Provider.

ASP.NET Identity

The Membership Provider has been replaced by ASP.NET Identity, which supports OAuth, Two-factor authentication and other coolness. My problem: there isn’t any obvious support for roles. All of the web examples deal with the new cool (send email to confirm, login using Facebook, etc.)

I created a MVC site on Visual Studio 2015 Community and poking around ASP.NET Identity, I noticed that many of the parts that support roles

AspNetRoles & AspNetUserRoles Tables

I created a new row in AspNetRoles named “Admin” (id = 1) and added a record in AspNetUserRoles to like 1 of my users in AspNetUsers to the role “Admin”.

Decorate Controller method with proper security

I added this method to my new AccessController (I don’t use ActionResult because I’m basically lazy.):

        [Authorize(Roles = "Admin")]
        public string AdminOnly()
        {
            // I don?t use ActionResult because I?m basically lazy. 
            //Otherwise I?d have to create a View.
            return "Welcome, you are allowed here because you are an Admin!";
        } 

1. Without logging on I go to http://localhost:58625/Access/AdminOnly and I get the Login page. Good.

2. Logged in as an administrator and I get to the page. Good.

3. Logged in as a non-administrator and I get the Login page. Weird.

Redirecting to Unauthorized page

OK, you don’t want to go to the Login page when you are logged in and are going to places where you don’t belong. ASP.NET Identity appears to redirect to Login whenever you go to somewhere you don’t belong. It makes the site feel incompetent. So I changed the Login() method in AccountController to:

        [AllowAnonymous]
        public ActionResult Login(string returnUrl)
        {
            if (User != null && User.Identity != null && User.Identity.IsAuthenticated)
            {
                return RedirectToAction("Unauthorized", "Account");
            }
            ViewBag.ReturnUrl = returnUrl;
            return View();
        } 

And added

        [AllowAnonymous]
        public ActionResult Unauthorized()
        {
            return View();
        } 

And added the appropriate view.

What about administering my users?

Right now I’m doing it in SQL Server. I’m looking into alternatives. More about that later.