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

I have a Reset Password page:
When the user fills the details and clicks the Reset Password button. The following controller is called:
public ActionResult ResetPassword(ResetPassword model)
{
...
return RedirectToAction("Logout");
}
When the user changes their password, they get Logged Out from the browser. However, if they are logged into another browser at the same time they remain logged in on the other browser.
I want to log out the user from all browsers they are logged into when they change their password.

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:
Created a new Asp.Net Web App in VS2015.
Choose MVC template.
Edit App_Stat/Startup.Auth.cs, line 34: change validateInterval: TimeSpan.FromMinutes(30) to validateInterval: TimeSpan.FromSeconds(1)
Edit Controllers/ManageController.cs, line 236: add the UserManager.UpdateSecurityStampAsync method call.
Run project, create a user, login, open a different browser and also login.
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
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.
Step 3
In RouteConfig.cs call the AuthHandler so that it is invoked for each incoming http request to your application.
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.
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.
Lastly
Your AspnetUsers table should look as below
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,
Create a UserSessions table in the database with UserSessionID (PK, Identity) UserID (FK) DateCreated, DateUpdated
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));
}

The ASP.NET Identity authentication is dependent on cookies on the user's browser. Because you use two different browsers to test it. You will have two different authentication cookies.Until the cookies expire the user is still authenticated That is why you are getting that results.
So you will have to come with some custom implementation.
For instance, always check if the user's has reset the password and has not yet logged in for the first time with the new password. If they haven't, logout them out and redirect to login. When they login a new auth cookie will be created.

I modeled my approach around this article from Github's Blogs
Modeling your App's User Session
They use a Hybrid Cookie Store / DB approach using ruby but I ported it to My ASP .Net MVC project and works fine.
Users can see all other sessions and revoke them if needed. When a user resets password, any active sessions are revoked.
I use an ActionFilterAttribute on a base controller to check active sessions cookies. If session cookie is found to be stale the user is logged out and redirected to sign in.

Based on CodeRealm's answer...
For anyone who experiences a situation where https access to your application on the browser throws a null pointer exception (i.e Object reference not set to an instance of an object.), it is because there might be existing records in your database where HasResetPassWord and/or IsFirstLoginAfterPasswordReset is null. Http requests will work, but https requests will fail, not sure why.
Solution: Just update the database manually and give both fields values. Preferably, false on both columns.

Related

MVC 5 - change password on demo accounts

I have an MVC 5 demo application that uses asp.net security. Within that application I have 75+ user accounts.
The person who gives the demos left, so I'd like to be able to reset all of the passwords for all of the accounts without having to change the email on each account to my personal email and do them individually where a link would be sent to my personal email.
Is there a way I can type in the user name and new password and use built in IdentityUser functionality to reset the password?
Assuming your app is in standard MVC5 format, put this ViewResult into the Account controller:
[AllowAnonymous]
public async Task<ViewResult> ResetAllPasswords()
{
// Get a list of all Users
List<ApplicationUser> allUsers = await db.Users.ToListAsync();
// NOTE: make sure this password complies with the password requirements set up in Identity.Config
string newPassword = "YourNewPassword!";
int passwordChangeSuccess = 0;
int countUsers = 0;
// Loop through the list of Users
foreach (var user in allUsers)
{
// Get the User
ApplicationUser thisUser = await UserManager.FindByNameAsync(user.UserName);
// Generate a password reset token
string token = await UserManager.GeneratePasswordResetTokenAsync(thisUser.Id);
// Change the password, using the reset token
IdentityResult result = await UserManager.ResetPasswordAsync(thisUser.Id, token, newPassword);
// Record results (extend to taste)
if (result.Succeeded)
{
passwordChangeSuccess++;
}
countUsers++;
}
ViewBag.CountUsers = countUsers;
ViewBag.PasswordSuccess = passwordChangeSuccess;
return View();
}
and set up a new View with ViewBag.CountUsers and ViewBag.PasswordSuccess to check the results.
Then set up an ActionLink pointing to ResetAllPasswords in Account controller and press to go.
Obviously the formatting can be changed (maybe a form with a confirm instead, maybe with an input field to specify the password .. ), but the basic controller code should hopefully be good. And note the [AllowAnonymous] attribute is there just for one-off access - not a good idea to leave it there for anything more than testing!
This should reset all Users to the same password specified in the code.
yes in Account Controller just go to the forget Password function and change that code a little where first of all user search the email id and after that system send a mail to that user .
There just write down a code where user send mail to your specific email id then you can get that link in your account click that link and reset the Password

How can i keep users logged in when they return? [duplicate]

HI
I am using asp.net mvc with asp.net membership.
I want to have a checkbox that if clicked keeps the users signed in for 2 weeks(unless they clear their cookies).
So I know their is
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie)
but I don't know how to set it up for 2week retention.
I rewrote most of the membership stuff. So I don't use stuff like Create() and VerifyUser().
Add a hash key or a random string to both the cookie and the database (both the same key). If the cookie and database value are the same, when the user starts a new session, sign him/her in again. When the user reaches the two weeks, remove the secret key from the database using a cronjob (Unix) or scheduled task (Windows).
Warning: Do not rely on the cookie expire date, since people can hack their browser.
Rule: NEVER, EVER trust ANY of your users!
You can set the global session timeout (the value is in minutes) in web.config eg.
<system.web>
<authentication mode="Forms">
<forms timeout="20160"/>
</authentication>
</system.web>
This will be for all authenticated users. If you want to use the 'Remember Me' functionality then you will need to write your own code to set the cookie/ticket. Something like this (taken from here):
protected void Page_Load()
{
if (Request.Cookies["username"] == null || Request.Cookies["username"].Value.ToString().Trim() == "")
{
Login1.RememberMeSet = true;
}
else
{
Login1.UserName = Request.Cookies["username"].Value.ToString().Trim();
Login1.RememberMeSet = true;
}
}
protected void RememberUserLogin()
{
// Check the remember option for login
if (Login1.RememberMeSet == true)
{
HttpCookie cookie = new HttpCookie("username");
cookie.Value = Login1.UserName.Trim();
cookie.Expires = DateTime.Now.AddHours(2);
HttpContext.Current.Response.AppendCookie(cookie);
Login1.RememberMeSet = true;
}
else if (Login1.RememberMeSet == false)
{
HttpContext.Current.Response.Cookies.Remove("username");
Response.Cookies["username"].Expires = DateTime.Now;
Login1.RememberMeSet = false;
}
}
Just use a simple cookie with 2 weeks expiration date.
Have you seen this?
http://forums.asp.net/t/1440824.aspx
Along similar lines to what Koning has suggested.
You can not use a session method to keep your users logged in, since browsers delete the session cookies when the browser is closed.
Do what user142019 offered and set the session's IdleTimeout parameter very short, up to 15 min. When the server receives any request from the browser, first check the session if it's alive. if not, try to get the cookie. If the cookie and database value are the same and not expired, assign it to the (new) session and return the response.
You can use onBeforeUnload listener to send a logout request when the user leaves your site. If logged out, delete the cookie and the db record, if not - assign a new hash for the next auto login and refresh that hash again when the user retunes to your website. You can also keep track of IP and the browser and link them to the hash in your db.
So, in case if the cookie is used with another browser or IP, and the hash code is valid, you can force them to login again.

asp.net make custom role provider (authorize) behave like the custom membership provider (authenticate) in expiration

I have the form authentication work fine with expiry 3 months cookie setting:
FormsAuthentication.Initialize();
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1, this.txtUsername.Text, DateTime.Now
, DateTime.Now.AddMonths(3), true, string.Empty);
so even if IIS restarted or project rebuild occur the user still authenitaced until he choose to log out our 3 months passed.
as for the custom role provider [authorizing part] when the user login isValid() i add session variable:
HttpContext.Current.Session.Add("userinfo", userInfo);
but as we know the session expire after web.config change, project build, IIS reboot or 20 mins passed by default.
all what I want is to make the system save Session["userinfo"] same as authentication [cookie] do but ofcourse without setting userinfo in cookie because that's not secure even the userId is considered security breach to be stored in cookie!
so how to accomplish that? i thought to store the user id in cookie but encrypted then if i found session expired but user still authenticated I'll reload the userInfo from DB but is that good enough or better approach available? and what about storing userInfo in authTicked in (string.Empty) in above code segment, is at accessible later and how to use it?
ok, sounds no body responded! so i choose to store the userId in the user-data section of the auth ticket:
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1, this.txtUsername.Text, DateTime.Now
, DateTime.Now.AddMonths(3), true, UserInfo.UserId.ToString());
then when I need to check the userInfo I use the following property:
public UserInformation UserInfo
{
get
{
if (Session["userinfo"] == null)
{
FormsIdentity id = (FormsIdentity)User.Identity;
FormsAuthenticationTicket ticket = id.Ticket;
long userId = Convert.ToInt64(ticket.UserData);
Session["userinfo"]=new MySqlMembershipProvider().LoadUserInfo(userId);
}
return (UserInformation)Session["userinfo"];
}
}
that's all. i thought of profile provider but i didn't like the idea of fetching the user permissions from db [9 tables structure] then re-store them under on record in sessions tables [its like circulating around yourself] beside if user perms or prefs updated more db hits required!!

How do I Keep a user logged in for 2 weeks?

HI
I am using asp.net mvc with asp.net membership.
I want to have a checkbox that if clicked keeps the users signed in for 2 weeks(unless they clear their cookies).
So I know their is
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie)
but I don't know how to set it up for 2week retention.
I rewrote most of the membership stuff. So I don't use stuff like Create() and VerifyUser().
Add a hash key or a random string to both the cookie and the database (both the same key). If the cookie and database value are the same, when the user starts a new session, sign him/her in again. When the user reaches the two weeks, remove the secret key from the database using a cronjob (Unix) or scheduled task (Windows).
Warning: Do not rely on the cookie expire date, since people can hack their browser.
Rule: NEVER, EVER trust ANY of your users!
You can set the global session timeout (the value is in minutes) in web.config eg.
<system.web>
<authentication mode="Forms">
<forms timeout="20160"/>
</authentication>
</system.web>
This will be for all authenticated users. If you want to use the 'Remember Me' functionality then you will need to write your own code to set the cookie/ticket. Something like this (taken from here):
protected void Page_Load()
{
if (Request.Cookies["username"] == null || Request.Cookies["username"].Value.ToString().Trim() == "")
{
Login1.RememberMeSet = true;
}
else
{
Login1.UserName = Request.Cookies["username"].Value.ToString().Trim();
Login1.RememberMeSet = true;
}
}
protected void RememberUserLogin()
{
// Check the remember option for login
if (Login1.RememberMeSet == true)
{
HttpCookie cookie = new HttpCookie("username");
cookie.Value = Login1.UserName.Trim();
cookie.Expires = DateTime.Now.AddHours(2);
HttpContext.Current.Response.AppendCookie(cookie);
Login1.RememberMeSet = true;
}
else if (Login1.RememberMeSet == false)
{
HttpContext.Current.Response.Cookies.Remove("username");
Response.Cookies["username"].Expires = DateTime.Now;
Login1.RememberMeSet = false;
}
}
Just use a simple cookie with 2 weeks expiration date.
Have you seen this?
http://forums.asp.net/t/1440824.aspx
Along similar lines to what Koning has suggested.
You can not use a session method to keep your users logged in, since browsers delete the session cookies when the browser is closed.
Do what user142019 offered and set the session's IdleTimeout parameter very short, up to 15 min. When the server receives any request from the browser, first check the session if it's alive. if not, try to get the cookie. If the cookie and database value are the same and not expired, assign it to the (new) session and return the response.
You can use onBeforeUnload listener to send a logout request when the user leaves your site. If logged out, delete the cookie and the db record, if not - assign a new hash for the next auto login and refresh that hash again when the user retunes to your website. You can also keep track of IP and the browser and link them to the hash in your db.
So, in case if the cookie is used with another browser or IP, and the hash code is valid, you can force them to login again.

Using Custom MembershipProvider without a Login control in ASP.NET

We have got a custom MembershipProvider in ASP.NET. Now there are 2 possible scenario the user can be validated:
User login via login.aspx page by entering his username/password. I have used Login control and linked it with the MyMembershipProvider. This is working perfectly fine.
An authentication token is passed via some URL in query string form a different web sites. For this I have one overload in MembershipProvider.Validate(string authenticationToken), which is actually validating the user. In this case we cannot use the Login control. Now how can I use the same MembershipProvider to validate the user without actually using the Login control? I tried to call Validate manually, but this is not signing the user in.
Here is the code snippet I am using
if (!string.IsNullOrEmpty(Request.QueryString["authenticationToken"])) {
string ticket = Request.QueryString["authenticationToken"];
MyMembershipProvider provider = Membership.Provider as MyMembershipProvider;
if (provider != null) {
if (provider.ValidateUser(ticket))
// Login Success
else
// Login Fail
}
}
After validation is successful, you need to sign in the user, by calling FormsAuthentication.Authenticate: http://msdn.microsoft.com/en-us/library/system.web.security.formsauthentication.authenticate.aspx
EDIT: It is FormsAuthentication.SetAuthCookie:
http://msdn.microsoft.com/en-us/library/twk5762b.aspx
Also, to redirect the user back where he wanted to go, call: FormsAuthentication.RedirectFromLoginPage: http://msdn.microsoft.com/en-us/library/system.web.security.formsauthentication.redirectfromloginpage.aspx
link text
You can set your own FormsAuthenticationTicket if the validation is successful.
Something like this;
if (provider != null) {
if (provider.ValidateUser(ticket)) {
// Login Success
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
1, //version
someUserName, //name
DateTime.Now, //issue date
DateTime.Now.AddMinutes(lengthOfSession), //expiration
false, // persistence of login
FormsAuthentication.FormsCookiePath
);
//encrypt the ticket
string hash = FormsAuthentication.Encrypt(authTicket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, hash);
Response.Cookies.Add(cookie);
Response.Redirect(url where you want the user to land);
} else {
// Login Fail
}
}
You are right in the case of storing the auth information as a cookie directly. But using a strong hash function (e.g. MD5 + SHA1) is great and secure.
By the way, if you use sessions (which is also just a hash cookie) you could attach auth information to it.

Resources