Logout User From all Browser When Password is changed Logout User From all Browser When Password is changed asp.net asp.net

Logout User From all Browser When Password is changed


I saw you are using ASP.NET Identity 2. What you are trying to do is already built in. All you need to do is change the SecurityStamp and all previous authentication cookies are no longer valid.

After you change the password you also need to change the SecurityStamp:

await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);await UserManager.UpdateSecurityStampAsync(User.Identity.GetUserId());

If you want the user to remain logged in, you have to reissue a new authentication cookie (signin):

    await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);

Otherwise the user/session who initated the password change will also be logged out.

And to log out all other sessions immediately you need to lower the check interval in the config:

app.UseCookieAuthentication(new CookieAuthenticationOptions{    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,    LoginPath = new PathString("/Account/Login"),    Provider = new CookieAuthenticationProvider    {        // Enables the application to validate the security stamp when the user logs in.        // This is a security feature which is used when you change a password or add an external login to your account.          OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(            validateInterval: TimeSpan.FromSeconds(1),            regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))    }});

Steps to reproduce:

  1. Created a new Asp.Net Web App in VS2015.
  2. Choose MVC template.
  3. Edit App_Stat/Startup.Auth.cs, line 34: change validateInterval: TimeSpan.FromMinutes(30) to validateInterval: TimeSpan.FromSeconds(1)
  4. Edit Controllers/ManageController.cs, line 236: add the UserManager.UpdateSecurityStampAsync method call.
  5. Run project, create a user, login, open a different browser and also login.
  6. Change password, refresh the page in the other browser : you should be logged out.


So I got home and decided to put together some code. Show me the code !!!

I would use a handler so the verification is always done when the user first access the application and it is done at one place for every action method access.

The idea is when the user reset their password, the application records the user has reset their password and have not logged in for the first time and sign out the user.

user.HasResetPassword = true;user.IsFirstLoginAfterPasswordReset = false;

When the user signs in, the application verifies if the user had previously reset their password and is now signing in for the first time. If these statements are valid the application updates its records to say you have not reset your password and you are not signing in for the first time.

Step 1

Add two properties to ApplicationUser model

enter image description here

Step 2

Add a class AuthHandler.cs in Models folder with the implementation below.At this stage you verify if the user has reset their password and has not logged in for the first time since the password was reset. If this is true, redirect the user to the login.

enter image description here

Step 3

In RouteConfig.cs call the AuthHandler so that it is invoked for each incoming http request to your application.enter image description here

Step 4

In ResetPassword method add implementation as below. At this step when a user has reset their password update the properties to say , they have reset their password and have not logged in for the first time. Notice the user is also signed out explicitly when they reset their password.

enter image description here

Step 5

In Login method add the implementation below. At this step if a user logins in successfully, verify their password was reset and they has logged for the first time is false. If all the conditions are true, update the properties in the database, so the properties are in a state ready for when the user resets the password in the future. So kind of a loop determining and updating the state of the password reset and first logins after resetting the password.

enter image description here

Lastly

Your AspnetUsers table should look as below

enter image description here

Comments

This is how I would approach it. I have not tested it so you may have modify it if you encounter exception. It is all also hard coded to show the approach to solved the problem.


Even ASP.NET Authentication says clearly that you have to have a secondary check to confirm if user is still an active logged in user (for example, we could block the user, user may have changed his password), Forms Authentication ticket does not offer any security against these things.

UserSession has nothing to do with ASP.NET MVC Session, it is just a name here

The solution I have implemented is,

  1. Create a UserSessions table in the database with UserSessionID (PK, Identity) UserID (FK) DateCreated, DateUpdated
  2. FormsAuthenticationTicket has a field called UserData, you can save UserSessionID in it.

When User Logs in

public void DoLogin(){     // do not call this ...     // FormsAuthentication.SetAuthCookie(....     DateTime dateIssued = DateTime.UtcNow;     var sessionID = db.CreateSession(UserID);     var ticket = new FormsAuthenticationTicket(            userName,            dateIssued,            dateIssued.Add(FormsAuthentication.Timeout),            iSpersistent,            // userData            sessionID.ToString());     HttpCookie cookie = new HttpCookie(         FormsAuthentication.CookieName,         FormsAuthentication.Encrypt(ticket));     cookie.Expires = ticket.Expires;     if(FormsAuthentication.CookieDomain!=null)         cookie.Domain = FormsAuthentication.CookieDomain;     cookie.Path = FormsAuthentication.CookiePath;     Response.Cookies.Add(cookie);}

To Authorize User

Global.asax class enables to hook into Authorize

public void Application_Authorize(object sender, EventArgs e){     var user = Context.User;     if(user == null)            return;     FormsIdentity formsIdentity = user.Identity as FormsIdentity;     long userSessionID = long.Parse(formsIdentity.UserData);     string cacheKey = "US-" + userSessionID;     // caching to improve performance     object result = HttpRuntime.Cache[cacheKey];     if(result!=null){         // if we had cached that user is alright, we return..         return;     }     // hit the database and check if session is alright     // If user has logged out, then all UserSessions should have been     // deleted for this user     UserSession session = db.UserSessions           .FirstOrDefault(x=>x.UserSessionID == userSessionID);     if(session != null){          // update session and mark last date          // this helps you in tracking and you          // can also delete sessions which were not          // updated since long time...          session.DateUpdated = DateTime.UtcNow;          db.SaveChanges();          // ok user is good to login          HttpRuntime.Cache.Add(cacheKey, "OK",                // set expiration for 5 mins               DateTime.UtcNow.AddMinutes(5)..)         // I am setting cache for 5 mins to avoid         // hitting database for all session validation         return;     }     // ok validation is wrong....     throw new UnauthorizedException("Access denied");}

When User Logs out

public void Logout(){    // get the ticket..    FormsIdentity f = Context.User.Identity as FormsIdentity;    long sessionID = long.Parse(f.UserData);    // this will prevent cookie hijacking    var session = db.UserSessions.First(x=>x.UserSessionID = sessionID);    db.UserSession.Remove(session);    db.SaveChanges();    FormsAuthentication.Signout();}

When user changes password or user is blocked or user is deleted...

public void ChangePassword(){    // get the ticket..    FormsIdentity f = Context.User.Identity as FormsIdentity;    long sessionID = long.Parse(f.UserData);    // deleting Session will prevent all saved tickets from    // logging in    db.Database.ExecuteSql(        "DELETE FROM UerSessions WHERE UserSessionID=@SID",        new SqlParameter("@SID", sessionID));}