My scenario is probably the opposite of most, I want to ALLOW multiple simultaneous logins but only for different types of users.
- User — Has their own area
- Admin — Has their own area
The problem occurs as an Admin can be a User as well (they have two accounts, this is mainly so they can check how the system is working from a user PoV) and want to be logged into both at the same time.
With Forms authentication, this doesn't seem possible. So I've had to "hack" around it slightly and am worried I might have overlooked something.
Plan:
- Two action filters for each type of user: UserAuthorise & AdminAuthorise
- Have two session cookies for each type of user
- Decorate controllers which the correct action filter based on what user can access it.
Code might need some tidying up.
I'll make the cookie names more unique as well.
Excluded stuff like Views/Routes as they don't seem relevant.
Left password salting/hashing out of samples and 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 this all seem sensible and secure?
Looking in FireFox's Page Info window at cookies I see each user type has its own cookie and you can't access a user type area without logging in.