Untitled
// POST: /Account/Login [HttpPost] [AllowAnonymous] //public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) public ActionResult Login(LoginViewModel model, string returnUrl) { //We need to be sure basic session values are there for site operation (such as company logo, Adobe key, KBItems, etc.) if (Session["appPath"] == null) { setSiteBasics();//session variables required for site operation even before user logs in } //We post to this ActionResult multiple times. The first time, it's for intial authentication and this will be null. //Subsequent posts are related to MFA and the user will be in cache. We can use the data in cache to save on the database hit. var cachedUser = MotorBase_Biz.Account.GetCurrentUserFromCache(HttpContext.User.Identity.Name); if (cachedUser == null && model.portalUser != null) { //The user logged in from spsuite.com and we haven't redirected yet, so no HttpContext.User.Identity.Name. We can look in cache this way. cachedUser = MotorBase_Biz.Account.GetCurrentUserFromCache(model.portalUser.strUserName); } //If the user hasn't entered a username and a password, throw a validation error. if (!ModelState.IsValid && cachedUser == null) { return View(model); } //This will contain everything about the current user, needed to process MFA or send them on their merry way after MFA is satisfied spReturntblEmployee portalUser = new spReturntblEmployee(); //If the user is not logged in yet, we need to try their username and password. if (cachedUser == null) { //returns a non null object if they are authenticated properly portalUser = MotorBase_Biz.Account.AuthenticateUser(model.Username, model.Password); if (!String.IsNullOrEmpty(portalUser.strEmployeeID)) { //successfully authenticated, set our session variable for Employee ID Session["EmpID"] = portalUser.strEmployeeID; //Clean up our user table, used for licensing products in the suite and IP security. List<SqlParameter> nvcParams = new List<SqlParameter>(); nvcParams.Add(new SqlParameter("strEmployeeID", portalUser.strEmployeeID)); var gUtils = new MotorBase_Biz.generalUtils(); // Old records can go, as can ours from a previous login. This table only ever contains active users. bool result = gUtils.ExecuteNonQuery("Delete from tblzWebUser Where dtmLastActivity < DATEADD(HOUR, -1, GETDATE()) Or strEmpID = @strEmployeeID", nvcParams, CommandType.Text); //Set our authentication cookie so the site considers the user authenticated. FormsAuthentication.SetAuthCookie(portalUser.strUserName, false); gUtils.logger("** authenticated username: " + portalUser.strUserName); //assign the web roles to the user. These roles determine access to parts of the menu/site. var UserRoles = MotorBase_Biz.generalUtils.GetRolesForUser(portalUser.strUserName); portalUser.userRoles = UserRoles; portalUser.strWebStartPage = "/About/MyProfile"; //We use the session id to detect concurrent logins from different sessions. portalUser.strSessionId = Session.SessionID; //add this user to cache. We can retrieve it from there, if we need any of these properties for site operations MotorBase_Biz.Account.AddUserToCache(portalUser); } } else if (cachedUser != null) { portalUser = cachedUser;//grab the user from cache. They are already authenticated. We use this variable for MFA below } if (!String.IsNullOrEmpty(portalUser.strEmployeeID)) { //If we already triggered MFA, we are waiting on a code and don't need to trigger again. (MFAStates.WaitOnCode) //Has this user opted in? If so, is it time to trigger? If they previously indicated to be remembered for 60 days we want to account for that. int CheckTriggerMFA = MotorBase_Biz.Account.CheckTriggerMFA(portalUser.strEmployeeID, portalUser.strUserName); if (CheckTriggerMFA == 0 || (String.IsNullOrEmpty(portalUser.strMFAMethod1) && String.IsNullOrEmpty(portalUser.strMFAMethod2))) { //If user opted out, or has no method stored we can forgo MFA model.mfaState = MFAStates.NoMFA; } else if (CheckTriggerMFA == 1 && model.mfaState != MFAStates.WaitOnCode) { //If the portal user has only one MFA method, no need to stop and ask. Just send the code. if ((!String.IsNullOrEmpty(portalUser.strMFAMethod1) && String.IsNullOrEmpty(portalUser.strMFAMethod2)) || (String.IsNullOrEmpty(portalUser.strMFAMethod1) && !String.IsNullOrEmpty(portalUser.strMFAMethod2))) { var strCurrentMFAMethod = (String.IsNullOrEmpty(portalUser.strMFAMethod1) ? portalUser.strMFAMethod2 : portalUser.strMFAMethod1); if (!String.IsNullOrEmpty(strCurrentMFAMethod)) { model.strCurrentMFA = strCurrentMFAMethod; //We need to generate our code and send it to the method specified string MFACode = GetMFACode(); model.portalUser = portalUser; if (MFACode != "") { model.MFACode = MFACode; portalUser.strCurrentMFA = model.strCurrentMFA; portalUser.strSessionId = Session.SessionID; MotorBase_Biz.Account.AddSecurityAuth(model.portalUser, MFACode); bool codeSendResult = SendMFACode(MFACode, model.strCurrentMFA); if (!codeSendResult) { ViewBag.MFAResult = "We are sorry, there has been an error attempting to send your code. Please try another method, or again later."; model.mfaState = MFAStates.TriggerMFA; } else model.mfaState = MFAStates.WaitOnCode; //The UI lets the user know the code has been sent and presents a place to enter this code. return View("Login",model); } } } //If the user has opted in and we aren't yet awaiting a code, we need to trigger the code send model.mfaState = MFAStates.TriggerMFA; } //Waiting on a code but having a value in strCurrentMFA but no value in the MFA Code yet means the user has chosen a method to send the code else if (model.mfaState == MFAStates.WaitOnCode && !String.IsNullOrEmpty(model.strCurrentMFA) && String.IsNullOrEmpty(model.MFACode)) { //We need to generate our code and send it to the method specified string MFACode = GetMFACode(); model.portalUser = portalUser; if (MFACode != "") { model.MFACode = MFACode; portalUser.strCurrentMFA = model.strCurrentMFA; portalUser.strSessionId = Session.SessionID; MotorBase_Biz.Account.AddSecurityAuth(model.portalUser, MFACode); bool codeSendResult = SendMFACode(MFACode, model.strCurrentMFA); if (!codeSendResult) { ViewBag.MFAResult = "We are sorry, there has been an error attempting to send your code. Please try again later."; model.mfaState = MFAStates.TriggerMFA; } else model.mfaState = MFAStates.WaitOnCode; //The UI lets the user know the code has been sent and presents a place to enter this code. //The UI lets the user know the code has been sent and presents a place to enter this code. return View("Login", model); } } //If we are waiting on a code, the code exists, as does the current MFA method, the user had entered a code and we need to validate it else if (model.mfaState == MFAStates.WaitOnCode && !String.IsNullOrEmpty(model.strCurrentMFA) && !String.IsNullOrEmpty(model.MFACode)) { //Checks tblSecurityAuth to validate the code. Can't be older than 15 minutes and has to match exactly. If more than one was triggered, only the last one works int authID = MotorBase_Biz.Account.CheckMFACode(model.strCurrentMFA, model.MFACode, model.Username); if (authID == 0) { ViewBag.MFAResult = "Incorrect or expired code. Please send and use another code."; model.mfaState = MFAStates.TriggerMFA; model.portalUser = portalUser; return View(model); } else { //Update tblSecurityAuth with our login date and time. MotorBase_Biz.Account.UpdateMFACode(authID, portalUser.denyRoleID); model.mfaState = MFAStates.MFASatisfied; //If the user indicated we to remember for 60 days, we need to set that value. if (model.ysnRememberMFA) { portalUser.ysnRememberMFA = true; MotorBase_Biz.Account.UpdateMFARemember(portalUser); } } } //If this user has MFA turned on, now that they have authenticated let's require the code. if (model.mfaState != MFAStates.MFASatisfied && model.mfaState != MFAStates.NoMFA && portalUser.ysnMFAOptIn) { model.mfaState = MFAStates.TriggerMFA; model.portalUser = portalUser; return View("Login", model); } else { //Authentication and MFA is satisfied! //These are items we will handle as part of our cached object when we can carry out clean up string redirectNow = SetSessionValuesForUser(portalUser); if (String.IsNullOrEmpty(redirectNow)) redirectNow = "/Report/Dashboard"; if (Session["ReturnUrl"] == null) Session["ReturnUrl"] = ""; if (String.IsNullOrEmpty(Session["ReturnUrl"].ToString())) { if (portalUser.strPassword == "123") { //The user still has the default password. Need to request that they change it. Session["ChangePasswordNow"] = "1"; } portalUser.strWebStartPage = redirectNow; return Redirect(portalUser.strWebStartPage); } else { return Redirect(Session["ReturnUrl"].ToString());//They could be using a bookmark or a QR code } } } else { ModelState.AddModelError("", "Invalid username or password. Please try again"); return View(model); } }
Leave a Comment