Reuse security code between WCF and MVC.NET

Posted by mrjoltcola on Stack Overflow See other posts from Stack Overflow or by mrjoltcola
Published on 2010-04-05T19:49:39Z Indexed on 2010/04/05 19:53 UTC
Read the original article Hit count: 816

First the background:

I jumped into MVC.NET from the Java MVC world, so my implementation below is possibly cheating, I don't know. I avoided fooling with a custom membership provider and I just implemented the base code needed to authenticate and load roles in my LogOn action. Typically I just need to check roles programatically, and have no use for all of the other membership features, so I didn't originally think I needed a full Membership provider.

I have a successful WCF project with a custom authentication and authorization layer that I did at least write per the proper API. I implemented it with custom IPrincipal, UserNamePasswordValidator and IAuthorizationPolicy classes to load from an Oracle database. In my WCF services, I use declarative security:

 [PrincipalPermission(SecurityAction.Demand, Role="ADMIN")].

The question (on the ASP.NET/MCV.NET side):

All my reading indicates I should implement a custom Membership/Roles provider, and use [Authorize(Roles="ADMIN")] on my controller actions. At this point, I don't have a true Membership provider, but I'm using the same User class that implements the IPrincipal interface that works with the WCF security. I plan to share common code between the WCF and ASP.NET modules.

So my LogOn action is not using the FormsService (and I assume this is bad). I had commented it out, and just used my "UserService" to access the Oracle db. Note my "TODO" comment below.

  public ActionResult LogOn(LogOnModel model, string returnUrl)
  {
     log.Info("Login attempt by " + model.UserName);
     if (ModelState.IsValid)
     {
        User user = userService.findByUserName(model.UserName);
        // Commented original MemberShipService code, this is probably bad
        // if (MembershipService.ValidateUser(model.UserName, model.Password))
        if (user != null && user.Authenticate(model.Password) == true)
        {
           log.Info("Login success by " + model.UserName);
           FormsService.SignIn(model.UserName, model.RememberMe);

           // TODO: Override with Custom identity / roles?

           user.AddRoles(userService.listRolesByUser(user)); // pull in roles from db

           if (!String.IsNullOrEmpty(returnUrl))
              return Redirect(returnUrl);
           else
              return RedirectToAction("Index", "Home");
        }
        else
        {
           log.Info("Login failure by " + model.UserName);
           ModelState.AddModelError("", "The user name or password provided is incorrect.");
        }
     }

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

So can I make the above work? Can I stick the IPrincipal (User) into the CurrentContext or HttpContext? Can I integrate the custom IPrincipal I've already created without writing a full Membership/Roles Provider?

I currently stick the User object into the session and access it from all MVC.NET controllers with "CurrentUser" property which grabs it from the session on demand. But this doesn't work with the [Authorize] attribute; I assume that is because it knows nothing about my custom Principal in the session, and is instead using whatever FormsService.SignIn() produces. I also found that session timeouts screw up the login redirect, the user doesn't get forwarded, instead we get a null exception accessing User from the session, and I assume it is related to my "skipping steps" to get a quick implementation.

Thanks.

© Stack Overflow or respective owner

Related posts about mvc.net

Related posts about asp.net-mvc