views:

1719

answers:

5

I am trying to create a custom authentication scheme in ASP.NET MVC using form authentication. The idea that I might have different areas on the site that will be managed - approver are and general user area, and these will use different login pages, and so forth. So this is what I want to happen.

  1. User access restricted page (right now I have it protected with a customer AuthorizeAttribute)
  2. User is redirected to a specific login page (not the one from Web.config).
  3. User credentials are verified (via custom databse scheme) and user logs in.

Would really appreciate any help with this!!!

This is what I what I have so far, and it doesn't work:

 public class AdministratorAccountController : Controller
{
    public ActionResult Login()
    {
        return View("Login");
    }

    [HttpPost]
    public ActionResult Login(AdministratorAccountModels.LoginModel model, string returnUrl)
    {
        if (ModelState.IsValid)
            if (model.UserName == "admin" && model.Password == "pass") // This will be pulled from DB etc
            {
                var ticket = new FormsAuthenticationTicket(1,               // version 
                                                           model.UserName,  // user name
                                                           DateTime.Now,    // create time
                                                           DateTime.Now.AddSeconds(30), // expire time
                                                           false,           // persistent
                                                           "");             // user data

                var strEncryptedTicket = FormsAuthentication.Encrypt(ticket);
                var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, strEncryptedTicket);
                Response.Cookies.Add(cookie);

                if (!String.IsNullOrEmpty(returnUrl))
                {
                    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);
    }

    [AdministratorAuthorize]
    public ActionResult MainMenu()
    {
        return View();
    }

    public class AdministratorAuthorizeAttribute : AuthorizeAttribute
    {
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            var authenCookie = httpContext.Request.Cookies.Get(FormsAuthentication.FormsCookieName);
            if (authenCookie == null) return false;

            var ticket = FormsAuthentication.Decrypt(authenCookie.Value);
            var id = new FormsIdentity(ticket);
            var astrRoles = ticket.UserData.Split(new[] { ',' });
            var principal = new GenericPrincipal(id, astrRoles);
            httpContext.User = principal;
            return true;
        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            var model = new AdministratorAccountModels.LoginModel();
            var viewData = new ViewDataDictionary(model);

            filterContext.Result = new ViewResult { ViewName = "Login", ViewData = viewData };

        }
    }
}
A: 

i tackled this one before i have a class i use for login

routines are login, read cookie, check cookie and they have a model that contains

name, email, id, userlevel

then you just have your own custom actionFilter

eg [CustomAuth(MinAllowedLevel=10)]

i use a baseclass for all my controllers so i can have an easier link to all my session content and can then get info like so

var model = pictures.all().where(x => x.userid == users.ReadCookie.userID)

i will bob up the code tommorow if you want for you when im back on UK daytime

say 10 hrs i will let you have the class for all the session stuff and the custom action filter that you can use, then all you need is a logins table with a userlevel field, best with levels of 10,20,30,40 incase you need a level between 1 and 2

minus4
Thanks, looking forward to seeing the code!
Greg R
+1  A: 

Isn't this what roles are for? Have a look at http://stackoverflow.com/questions/390930/asp-net-mvc-authorization-using-roles or have a look at roles in general

spaceman
Well, not exactly, if you have completely separate areas of logging in within your site that should not be mixed together and require a different login for each, you can't use roles.For example if you had administrator options for a website (that should never be visible to any visitors) as well as customer accounts for using the website. When you hit the secure pages without logging in, it should bounce users to different login screens based on the area the are accessing.
Greg R
Thanks for the clarification...
spaceman
+1  A: 

okay here you go The Code

in there you have ActionFilters folder ( AuthAccess.cs) Plugins Folder (security.cs (encrypt/decrypt cookie), SessionHandler.cs (all matters of login)) Controllers folder (BaseController.cs, and exampleController (show you how to use) and the loginTable SQL file.

i use mysql so you may need to amend, also i use subsonic so my model would come from there and would be in the empty models folder.

really simple to use will leave it up for a while for you, enjoy

nope cookie model is here sorry:

using System;

namespace TestApp.Models
{
    public class CookieModel
{
    public string CurrentGuid { get; set; }
    public DateTime LoginTime { get; set; }
    public Int32 UserLevel { get; set; }
    public Int32 LoginID { get; set; }
    public bool isValidLogin { get; set; }
    public string realUserName { get; set; }
    public string emailAddress { get; set; }
}
}
minus4
Thanks a lot, the code helped. I made some changes to simplify it and fit the original question.
Greg R
thats cool was meant as a base for a good start happy to help
minus4
+1  A: 

I used a combination of code suggested by minus4 and my own code above to create this simplified scenario that might help someone else. I added some comments about things that confused me at first.

 public class AdministratorAccountController : Controller
{
    public ActionResult Login()
    {
        return View("Login");
    }

    [HttpPost]
    public ActionResult Login(AdministratorAccountModels.LoginModel model, string returnUrl)
    {
        if (ModelState.IsValid)
            // Here you would call a service to process your authentication
            if (model.UserName == "admin" && model.Password == "pass")
            {
                // * !!! *
                // Creating a FromsAuthenticationTicket is what 
                // will set RequestContext.HttpContext.Request.IsAuthenticated to True
                // in the AdminAuthorize attribute code below
                // * !!! *
                var ticket = new FormsAuthenticationTicket(1, // version 
                                                           model.UserName, // user name
                                                           DateTime.Now, // create time
                                                           DateTime.Now.AddSeconds(30), // expire time
                                                           false, // persistent
                                                           ""); // user data, such as roles

                var strEncryptedTicket = FormsAuthentication.Encrypt(ticket);
                var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, strEncryptedTicket);
                Response.Cookies.Add(cookie);

                // Redirect back to the page you were trying to access
                if (!String.IsNullOrEmpty(returnUrl))
                {
                    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);
    }

    [AdminAuthorize]
    public ActionResult MainMenu()
    {
        return View();
    }

    public class AdminAuthorize : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (!filterContext.RequestContext.HttpContext.Request.IsAuthenticated)
            {
                // Redirect to the needed login page
                // This can be pulled from config file or anything else
                filterContext.HttpContext.Response.Redirect("/AdministratorAccount/Login?ReturnUrl=" 
                                        + HttpUtility.UrlEncode(filterContext.HttpContext.Request.RawUrl));               
            }

            base.OnActionExecuting(filterContext);
        }
    }
}
Greg R
A: 

Hey minus, I'm also interested in your sample code for the authorization. Could you please upload it again for me?

Or GregR, maybe you could send me your code for the AdministratorAccountModels so I can use it as a base for mine?

Thanks!

Erwin1441