I want to prevent multiple login for user with same username and password . Example :
User A login with account "A", Session["AccountA"] is create ( about 30 minutes ) . After user B login with account "A" ,Session["AccountA"] is create and Session["AccountA"] of user A timeout .
Yes, it is possible (although may not be as great an idea as you are thinking). Here's one way to do it:
When the user signs on, store a value in Application state that binds his session ID to his user ID.
var context = HttpContext.Current;
var lookup = String.Format("Session_{0}", userID);
Application.Lock();
Application[lookup] = context.Session.SessionID;
Application.Unlock();
When a user requests a page (and is not signing on) check to see if the binding is correct. If not, kill the session.
var context = HttpContext.Current;
var lookup = String.Format("Session_{0}", userID);
var sessionID = Application[lookup] as string;
if (sessionID != context.Session.SessionID)
{
context.Session.Abandon();
var c = new HttpCookie(FormsAuthentication.FormsCookieName,"DELETED");
c.Expires = System.DateTime.Now.AddDays(-2);
context.Response.AppendCookie(c);
context.Response.Redirect("~/logout.aspx");
}
When a user attempts to access a page, but has the wrong session ID, his session will be killed.
The trick here is... your Application state will keep growing as users sign on. You will need to devise some means of detecting when users have signed off and cleaning it up, or you will need to recycle your AppPool on a regular basis.
Related
I have previously worked with Membership through "System.Web.Security.Membership"
Here, you can do the following:
var currentUser = Membership.GetUser();
var otherUser = Membership.GetUser(username);
...giving you a MembershipUser.
Now, with Identity, I can find a load of ways to get the current logged in user.
But no way to get another user.
I can use:
var userStore = new UserStore<IdentityUser>();
var userManager = new UserManager<IdentityUser>(userStore);
var user = userManager.Find(username, password);
But that takes both username and password, with no overload for just username.
How do i get the IdentityUser from only a username?
Almost every answer I find is connected to MVC.
This is for a WCF service, where authorization is made using Identity. And in some cases the user is getting to the site from an other site with a generated "token" - an encrypted string, containing the username. From here, user is logged in and a session-cookie is set, depending on users settings.
Also, is there a shorter way to get UserInformation?
"var currentUser = Membership.GetUser(username);"
is much more convenient than
"var user2 = (new UserManager((new UserStore()))).Find(username, password);"
UserManager has UserManager<TUser>.FindByNameAsync method. You can try using it to find user by name.
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.
The system I am working on does not use standard ASP.NET Auth/ Membership facilities for logging users in/ out. Therefore after logging the user in I want to issue a new Session ID to the user in order to prevent Session trapping/ Hijacking. The problem i have is that although I have been able to successfully create a new session with a new ID and copy the various components to the newly created session eg. session["value"]. By the end of the code excerpt below the newly created session is the current HTTPContext's session, and has the session values that were copied accross. However after performing a Response.Redirect the new session is in action, but none of the session["values"] have persisted across the two requests. As you can see from the code below i've tried adding the values to a number of collections to avail.
Any help would be amazing!! Thanks in advance
bool IsAdded = false;
bool IsRedirect = false;
HttpSessionState state = HttpContext.Current.Session;
SessionIDManager manager = new SessionIDManager();
HttpStaticObjectsCollection staticObjects = SessionStateUtility.GetSessionStaticObjects(HttpContext.Current);
SessionStateItemCollection items = new SessionStateItemCollection();
foreach (string item in HttpContext.Current.Session.Contents)
{
var a = HttpContext.Current.Session.Contents[item];
items[item] = a;
}
HttpSessionStateContainer newSession = new HttpSessionStateContainer(
manager.CreateSessionID(HttpContext.Current),
items,
staticObjects,
state.Timeout,
true,
state.CookieMode,
state.Mode,
state.IsReadOnly);
foreach (string item in HttpContext.Current.Session.Contents)
{
var a = HttpContext.Current.Session.Contents[item];
newSession.Add(item,a);
}
SessionStateUtility.RemoveHttpSessionStateFromContext(HttpContext.Current);
SessionStateUtility.AddHttpSessionStateToContext(HttpContext.Current, newSession);
manager.RemoveSessionID(HttpContext.Current);
manager.SaveSessionID(HttpContext.Current, newSession.SessionID, out IsRedirect, out IsAdded);
return newSession.SessionID;
Maybe I'm missing something here but won't this work:
Session["mysession"] = mySessionObject;
Basically it appears it's not possible since you can only add session variables once there has been one round trip to the client to create the corresponding session cookie. Therefore I had to create the new new session (with new ID) so that by the time I came to adding session variables, the client cookie had the appropriate session id: annoying since this in reality is issuing the new session ID before the user is authenticated.
Interestingly, it seems a little strange that issuing a new Session ID is exactly what the standard asp.net authentication/ membership functionality does but is able to maintain session variables, and yet doing it manually it doesn't....are there some methods for this that are not being exposed to us mere developers maybe....
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!!
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.