Introduction
There are several sites there that prevent multiple logins, It only allows a single device for the same id so to do the same I have created a simple solution to solve this situation in C#. For example, we have a site that provides some kind of service based on subscription, if we allow multiple logins for multiple devices, in that case, we have lost because a single user shares his/her id with others so all can use the same service without payment. To prevent that case below solution works.
Solution
Here I implement one helper class, ActionFilterAttribute, and LoginHistory table to maintain the logins history of users.
LoginHistory.cs (SQL Table)
public class LoginHistory
{
[Key]
public int ID { get; set; }
public string UserId { get; set; }
public string SessionId { get; set; }
public bool LoggedIn { get; set; }
}
HelperExtention.cs
public class HelperExtention
{
public static bool IsUserLoginStillTrue(string userId, string sessionId)
{
ApplicationDbContext context = new ApplicationDbContext();
IEnumerable<LoginHistory> logins = (from i in context.LoginHistory
where i.LoggedIn == true &&
i.UserId == userId && i.SessionId == sessionId
select i).AsEnumerable();
return logins.Any();
}
public static bool IsUserLoggedOnElsewhere(string userId, string sessionId)
{
ApplicationDbContext context = new ApplicationDbContext();
IEnumerable<LoginHistory> logins = (from i in context.LoginHistory
where i.LoggedIn == true &&
i.UserId == userId && i.SessionId != sessionId
select i).AsEnumerable();
return logins.Any();
}
public static void LogOutRestUsersForSameUserId(string userId, string sessionId)
{
ApplicationDbContext context = new ApplicationDbContext();
var logins = (from i in context.LoginHistory
where i.LoggedIn == true &&
i.UserId == userId &&
i.SessionId != sessionId
select i).ToList();
foreach (LoginHistory item in logins)
{
item.LoggedIn = false;
}
context.SaveChanges();
}
}
GlobalActionFilterAttribute.cs
This method will be called the byASP.NET MVC framework before any action method executes.
public class GlobalActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext) { }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var userID = HttpContext.Current.User.Identity.GetUserId();
if (filterContext.HttpContext.Session["sessionid"] != null)
{
if (HelperExtention.IsUserLoginStillTrue
(userID, filterContext.HttpContext.Session["sessionid"].ToString()))
{
if (!HelperExtention.IsUserLoggedOnElsewhere
(userID, filterContext.HttpContext.Session["sessionid"].ToString()))
{
}
else
{
HelperExtention.LogOutRestUsersForSameUserId
(userID, filterContext.HttpContext.Session["sessionid"].ToString());
}
}
else
{
filterContext.HttpContext.Session.Abandon();
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
filterContext.Result = new RedirectResult("/Account/Login");
}
}
else
{
filterContext.HttpContext.Session.Abandon();
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
filterContext.Result = new RedirectResult("/Account/Login");
}
}
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.Current.GetOwinContext().Authentication;
}
}
}
Below is the controller code you can use as per requirements. I used an Identity server to authenticate but you can use a simple controller and logic also. The below method will just check the user is valid or not and if it’s valid it will insert login history in the LoginHistory table.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
{
var sessionID = System.Web.HttpContext.Current.Session.SessionID;
var userID = SignInManager.AuthenticationManager.AuthenticationResponseGrant.Identity.GetUserId();
Session["sessionid"] = sessionID;
LoginHistory history = new LoginHistory
{
UserId = userID,
SessionId = sessionID,
LoggedIn = true
};
if (!_db.LoginHistory.Any(x => x.SessionId.Equals(sessionID) && x.UserId.Equals(userID)))
{
_db.LoginHistory.Add(history);
await _db.SaveChangesAsync();
}
else
{
var oldHistory = _db.LoginHistory.FirstOrDefault(x => x.SessionId.Equals(sessionID) && x.UserId.Equals(userID));
if (oldHistory != null)
{
oldHistory.LoggedIn = true;
_db.SaveChanges();
}
}
return RedirectToLocal(returnUrl);
}
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
You can also write the below lines on registration if you want.
var sessionID = System.Web.HttpContext.Current.Session.SessionID; Session["sessionid"] = sessionID;
That’s it this works for me, you can use a piece of code or modify code as per your requirements.
hope you guys found something useful. Please give your valuable feedback/comments/questions about this article. Please let me know how you like and understand this article and how I could improve it. If you like this blog you can buy me Pizza.
The post Prevent The Same User ID For Logging From Multiple Devices In C# appeared first on The Code Hubs.