I have implemented a custom membership provider and now I wish to provide my users with the option to let the website remember them, so they do not have to log in every single time they visit.
I've set my cookie to persistent, but it is not acting very persistent. When I close the browser and open it again after logging in the website requires that I log in again.
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
1,
model.UserName,
DateTime.Now,
(model.RememberMe == true ? DateTime.Now.AddDays(7) : DateTime.Now.AddMinutes(60)),
model.RememberMe,
userData);
string encTicket = FormsAuthentication.Encrypt(authTicket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
Response.Cookies.Add(cookie);
return RedirectToAction("Index", "Home");
model.RememberMe is in this case a boolean with the value of true, proven by the Visual Studio debugger.
Am I doing something blatantly wrong here?
In the end it turned out that I had to specify the expiration for the cookie aswell, as pointed out by Slicksim's comment from Feb 7 '13.
Related
I have to set FormsAuthentication cookie value (FormsAuthentication.SetAuthCookie(UserDesignation, false)) at the time of login.
Now I need to provide designation change option. So when user change their designation, I need to update the FormsAuthentication cookie value from old designation to new designation.
Is it possible to do that?
If yes means, how can I do this?
You can modify the cookie data as shown below, but it is preferable as per me to keep the role in a separate cookie and authenticate it using the FormsAuthentication cookie
HttpCookie cookie = FormsAuthentication.GetAuthCookie(Username, true);
var ticket = FormsAuthentication.Decrypt(cookie.Value);
var newticket = new FormsAuthenticationTicket(ticket.Version,
ticket.Name,
ticket.IssueDate,
ticket.Expiration,
true, //persistent
"user data,designation",
ticket.CookiePath);
cookie.Value = FormsAuthentication.Encrypt(newticket);
cookie.Expires = newticket.Expiration.AddHours(2);
HttpContext.Current.Response.Cookies.Set(cookie);
An external company has done some penetration tests on the ASP.NET MVC 5 application i'm working on.
An issue that they raised is described below
A cookie linked with session Management is called AspNet.ApplicationCookie. When entered manually,the application authenticates the user. Even though the user logs out from the Application,the cookie is still valid. This means,the old session cookie can be used for a valid authentication within unlimited timeframe. In the moment the old value is inserted, the application accepts it and replaces it with a newly generated cookie. Therefore, if the attacker gains access to one of the existing cookies, the valid session will be created,with the same access as in the past.
We're using ASP.NEt Identity 2.2
Here's our logout action on the account controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut();
return RedirectToAction("Login", "Account");
}
in startup.auth.cs
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
ExpireTimeSpan = TimeSpan.FromHours(24.0),
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, int>(
validateInterval: TimeSpan.FromMinutes(1.0),
regenerateIdentityCallback: (manager, user) =>
user.GenerateUserIdentityAsync(manager),
getUserIdCallback: (id) => (Int32.Parse(id.GetUserId())))
}
});
I would have thought that the framework would have taken care of invalidating an old session cookie but browsing through the Owin.Security source code it appears not.
How do i invalidate the session cookie on logout?
edit on Jamie Dunstan's Advice i've added AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie); but then has made no difference. I can still still log out of the application, clone a previously authenticated request in Fiddler, and have it accepted by the application.
Edit : My updated Logoff method
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> LogOff()
{
var user = await UserManager.FindByNameAsync(User.Identity.Name);
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
await UserManager.UpdateSecurityStampAsync(user.Id);
return RedirectToAction("Login", "Account");
}
Make sure you use AuthenticationManager.Signout(DefaultAuthenticationTypes.ApplicationCookie); as correctly suggested by Jamie.
Being able to login with the same cookie again is by design. Identity does not create internal sessions to track all logged-in users and if OWIN gets cookie that hits all the boxes (i.e. copies from the previous session), it'll let you login.
If you still can login after the security stamp is updated, most likely OWIN can't get a hold of ApplicationUserManager. Make sure you have this line just above the app.UseCookieAuthentication
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
Or if you are using DI take ApplicationUserManager from DI:
app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationUserManager>());
Also reduce the validateInterval: TimeSpan.FromMinutes(30) to lower value - I usually settle for couple minutes. This is how often Identity compares values in auth-cookie to the values in the database. And when the comparison is done, Identity regenerates the cookie to update timestamps.
Trailmax's answer is spot on, I thought I would add that if someone is trying to do this while also using ASP.NET Boilerplate, the following is what I used to make this work:
app.CreatePerOwinContext(() => IocManager.Instance.Resolve<UserManager>());
I originally had:
app.CreatePerOwinContext(() => IocManager.Instance.ResolveAsDisposable<UserManager>());
and is wasn't working.
you were on the right way. Indeed the easiest way is to update user SecurityStamp but
normally executing it doesn't lead to success because actualy credentials aren't changed and it remains the same in db.
Solution, try this:
private string NewSecurityStamp()
{
return Guid.NewGuid().ToString();
}
private async Task RegenerateSecurityStamp(string userId)
{
var user = await _userManager.FindByIdAsync(userId);
if (user != null)
{
user.SecurityStamp = NewSecurityStamp();
await _userStore.UpdateAsync(user);
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> LogOff()
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
await RegenerateSecurityStamp(User.Identity.GetUserId());
return RedirectToAction("Login", "Account");
}
I have an asp.net MVC application that was exhibiting some strange authentication timeout behavior. We had the timeout on the forms authentication tag in the web.config set to two days.
<forms loginUrl="~/Login/Index" timeout="2880" />
What we noticed though was the application was timing out around the 20 min mark. So, after researching the issue and trying various settings we decided to add code to the global.asax in the Applicatoni_AuthenticateRequest method that reads the ticket from the cookie, and updates the expiration .
Sub Application_AuthenticateRequest(sender As Object, e As EventArgs)
Dim authCookie As HttpCookie = Context.Request.Cookies(FormsAuthentication.FormsCookieName)
If (IsNothing(authCookie) OrElse authCookie.Value = "") Then
Return
End If
Dim authTicket As FormsAuthenticationTicket
Try
authTicket = RefreshLoginCookie()
Catch
Return
End Try
'string[] roles = authTicket.UserData.Split(';');
'if (Context.User != null) Context.User = new GenericPrincipal(Context.User.Identity, roles);
End Sub
Function RefreshLoginCookie() As FormsAuthenticationTicket
Dim authCookie As HttpCookie = HttpContext.Current.Request.Cookies(FormsAuthentication.FormsCookieName)
If (IsNothing(authCookie) OrElse authCookie.Value = "") Then
Return Nothing
End If
Dim oldTicket As FormsAuthenticationTicket = FormsAuthentication.Decrypt(authCookie.Value)
Dim expiryDate As Date = DateTime.Now.Add(FormsAuthentication.Timeout)
HttpContext.Current.Response.Cookies.Remove(FormsAuthentication.FormsCookieName)
Dim newTicket As FormsAuthenticationTicket = New FormsAuthenticationTicket(oldTicket.Version, oldTicket.Name, oldTicket.IssueDate, expiryDate, oldTicket.IsPersistent, oldTicket.UserData, oldTicket.CookiePath)
authCookie.Value = FormsAuthentication.Encrypt(newTicket)
authCookie.Expires = expiryDate
HttpContext.Current.Response.Cookies.Add(authCookie)
Return newTicket
End Function
Testing this in visual studio 2012 debugger everything worked beautifully, but once we deployed it to our IIS 7 environment the timeout issue returned.
To aid in my debugging I set the cookie expiration to be the same as the ticket's expiration so that I could easily tell what the ticket was being set to.
When the timeout occurs I see that the expiration should have occurred on 11/16, but that's not what's happening :
Here is the cookie information (at least the relevant information)
Name Expiration
.ASPXAUTH 11/16/2014, 9:00:47
putting logging in I see that the expiration update succeeds on the next request, but I still am getting sent to the login.
I'm stumped. I know it must be some setting in IIS 7 that is causing this because as I stated, it works fine in the debugger in visual studio (which is IIS 8 Express) so there must be something somewhere overriding this functionality.
I think it may be something specific to MVC though, because I'm using this same functionality on another site and am not seeing the same problem.
UPDATE
Forgot to mention, I'm also NOT seeing an authentication ticket timeout record in the event viewer.
Ok, so I finally figured out the answer to this problem. The Application Pool was hitting its Idle Timeout setting of 20 minutes. So, after 20 minutes of inactivity in the application, the app pool was recycled. Thus, when the user would eventually continue, the pool would reinitialize, and the current authentication cookie was no longer valid.
To modify this setting, go into the advanced settings on your application pool in IIS. Setting it to 0 will disable the idle timeout, or you can set it to whatever value in minutes that you determine appropriate.
I upload my first ASP.NET MVC application to App Harbor, that uses FormsAuthentication to simple authentication of the user.
As usual on local machine operation that required authentications performes well, at the moment I tun them on AppHarbor, they failed.
I checked that authentication cookie released on access is send with the request, as it visible from image:
and my server side authentication code looks like
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
1,
user.UserName,
DateTime.Now,
DateTime.Now.AddDays(1),
true,
user.UserName
);
string encTicket = FormsAuthentication.Encrypt(authTicket);
this.Response.Cookies.Add(
new HttpCookie(
FormsAuthentication.FormsCookieName,
encTicket)
{ Expires = authTicket.Expiration });
I read different posts from AppHarbor blogs like ASP.NET Forms Authentication Considered Broken, it says "harmflul" and not that it's not working.
So, what I do wrong here ?
Stackoverflow uses OpenId as many other websites. However, I rarely need to provide my OpenId to Stackoverflow while with other OpenId enabled websites, I have to do it once a day or week.
This suggests to me that the expiry of the session is with the website and not the OpenId provider.
Looking at the DotNetOpenId code in ASP MVC, I can see that after a successful authentication by the OpenId provider FormsAuthentication.SetAuthCookie is called with the identifier and a boolean parameter to determine if the cookie should be persisted.
How can I force this cookie to expire, say in 2020 instead of whatever the default value is.
From MSDN - Explained: Forms Authentication in ASP.NET 2.0:
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
"userName",
DateTime.Now,
new DateTime(2020, 01, 01), // value of time out property
false, // Value of IsPersistent property
String.Empty,
FormsAuthentication.FormsCookiePath);
string encryptedTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie authCookie = new HttpCookie(
FormsAuthentication.FormsCookieName,
encryptedTicket);
authCookie.Secure = true;
Response.Cookies.Add(authCookie);
You can use FormsAuthentication.RenewTicketIfOld() to update the ticket. This method would check if the current ticket has expired and if not, to extend its expiration. You can call this method on every request to your site, which will "slide" th expiration further in future as the user keeps using your site.