ASP.NET MVC Forms Authentication of Multiple Concurrent Logins

My scenario is probably the opposite of most, I want ALLOW to have multiple simultaneous logins, but only for different types of users.

  • User - has its own area.
  • Administrator - has its own area.

The problem arises because the administrator can also be a user (they have two accounts, this basically means that they can check how the system works from the PoV user) and want them to log in both at the same time.

Using forms authentication is not possible. So I had to “crack” it a bit, and I worry that maybe I missed something.

Plan:

  • Two action filters for each type of user: UserAuthorise & AdminAuthorise
  • You have two session cookies for each type of user.
  • Decorate controllers for which the correct action filter is based on the fact that the user can access it.

The code may require some disposal.

I will make cookie names more unique.

Excluded things, such as Views / Routes, as they do not seem relevant.

Left salt / hash password from samples and get stuck with test values.

UserAuthorise:

public class UserAuthorize : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { var authCookie = filterContext.RequestContext.HttpContext.Request.Cookies["User"]; if (authCookie == null || authCookie.Value == "") { filterContext.HttpContext.Response.Redirect("/login"); base.OnActionExecuting(filterContext); return; } FormsAuthenticationTicket authTicket; try { authTicket = FormsAuthentication.Decrypt(authCookie.Value); } catch { filterContext.HttpContext.Response.Redirect("/login"); base.OnActionExecuting(filterContext); return; } if (authTicket.Expired || authTicket.Expiration <= DateTime.Now) { filterContext.HttpContext.Response.Redirect("/login"); } base.OnActionExecuting(filterContext); } } 

AdminAuthorise:

 public class AdminAuthorise : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { var authCookie = filterContext.RequestContext.HttpContext.Request.Cookies["Admin"]; if (authCookie == null || authCookie.Value == "") { filterContext.HttpContext.Response.Redirect("/admin/login"); base.OnActionExecuting(filterContext); return; } FormsAuthenticationTicket authTicket; try { authTicket = FormsAuthentication.Decrypt(authCookie.Value); } catch { filterContext.HttpContext.Response.Redirect("/admin/login"); base.OnActionExecuting(filterContext); return; } if (authTicket.Expired || authTicket.Expiration <= DateTime.Now) { filterContext.HttpContext.Response.Redirect("/admin/login"); } base.OnActionExecuting(filterContext); } } 

User Login Controller Action:

 [HttpPost] public virtual ActionResult Login(FormCollection form) { if (form["username"] == "admin" && form["password"] == "pass") { var authTicket = new FormsAuthenticationTicket( 1, // version form["username"], // user name DateTime.Now, // created DateTime.Now.AddMinutes(20), // expires false, // persistent? "" // can be used to store roles ); string encryptedTicket = FormsAuthentication.Encrypt(authTicket); var authCookie = new HttpCookie("User", encryptedTicket); Response.Cookies.Add(authCookie); // Redirect back to the page you were trying to access return RedirectToAction(MVC.Home.Index()); } else { ModelState.AddModelError("", "Bad info mate"); } return View(); } 

Admin Login Controller Action:

 [HttpPost] public virtual ActionResult Login(FormCollection form) { if (form["username"] == "admin" && form["password"] == "pass") { var authTicket = new FormsAuthenticationTicket( 1, // version form["username"], // user name DateTime.Now, // created DateTime.Now.AddMinutes(20), // expires false, // persistent? "" // can be used to store roles ); string encryptedTicket = FormsAuthentication.Encrypt(authTicket); var authCookie = new HttpCookie("Admin", encryptedTicket); Response.Cookies.Add(authCookie); // Redirect back to the page you were trying to access return RedirectToAction(MVC.Admin.Home.Index()); } else { ModelState.AddModelError("", "Bad info mate"); } return View(); } 

Does all this seem reasonable and safe?

In the "FireFox Page Information" window in cookies, I see that each user type has its own cookie, and you cannot access the user type area without logging in.

+4
source share
3 answers

First, you should probably be associated with an AuthorizeAttribute, not an ActionFilterAttribute. AutorizationFilters go before ActionFilters and allow a short circuit (that is, if the authorization filter fails, action filters will never be executed). In addition, ActionFilters join together and can be executed in any order.

Secondly, it is not a good idea for the administrator username and password to be hardcoded in the attribute. Passwords really need to be one-way hashed.

+3
source

What you need for this scenario is called impersonation , basically all you have to do is set up a fake cookie with impersonated user data (so that the administrator can see what the client sees).

You probably also want to track this, so you can place an administrator on the user interface, impersonating user information about the application’s status, and also provide a link to end the impersonation session (you would restore the previous cookie at this point), instead of allowing him to "enter twice."

You can check this out because it may contain some useful information for you (a bit old, but always valid).

+1
source

As for your database model, I would assign a user several roles (simple user, administrator, supervisor, etc.). Therefore, you must only log in once using the default role (admin) and be able to switch to another role (simple user PoV) and save session permissions.

0
source

Source: https://habr.com/ru/post/1403218/


All Articles