C#

Prevent The Same User ID For Logging From Multiple Devices In C#

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.

 

Faisal Pathan

Faisal Pathan is a founder of TheCodeHubs, .NET Project Manager/Team Leader, and C# Corner MVP. He has extensive experience with designing and developing enterprise-scale applications. He has good skills in ASP.NET C#, ASP.NET Core, ASP.NET MVC, AngularJS, Angular, React, NodeJS, Amazon S3, Web API, EPPlus, Amazon MWS, eBay Integration, SQL, Entity Framework, JavaScript, eCommerce Integration like Walmart, Tanga, Newegg, Group-on Store, etc. and Windows services.

Recent Posts

Testing hk

Testing

2 years ago

Create and Used PIPE in angular

In this article, we have to show Create and Used PIPE in angular

2 years ago

Operation

Testing

2 years ago

Create and Used PIPE in angular

In this article, we have to show Create and Used PIPE in angular

2 years ago

Create and Used PIPE in angular

In this article, we have to show Create and Used PIPE in angular

2 years ago

TETS NEW

test

3 years ago