ASP.MVC Remember me cookie not working after session time out - c#

On a login form I have an option to allow the user to click a remember me checkbox which creates a new FormsAuthenticationTicket which then gets added to a cookie.
if (_model.RememberMe)
{
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
_model.Username,
DateTime.Now,
DateTime.Now.AddDays(30),
true,
_model.Username,
FormsAuthentication.FormsCookiePath);
// Encrypt the ticket.
string encTicket = FormsAuthentication.Encrypt(ticket);
// Create the cookie.
Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
Which should hopefully be in the clients browser for 30 days as stated above.
Testing this, I've purposely left the current session timeout for only a minute
<sessionState timeout="1"></sessionState>
So after a minute, if the user has said "remember me" I expect the website should not be redirected back to the login page. However it does. This is the code that does it.
// [".ASPXAUTH"] is the cookie name that is created by the FormsAuthenticationTicket`
if (User.Identity.Name == "" && Request.Cookies[".ASPXAUTH"] == null)
{
return RedirectToAction("LogOut", "Login");
}
// the current session hasn't timed out or the remember me cookie is enabled
FormsIdentity id = (FormsIdentity)User.Identity;
FormsAuthenticationTicket ticket = id.Ticket;
But the cookie is NULL.
I am expecting it's a misunderstanding on my behalf so if anyone can give me a hand. I would be very grateful.
Thanks

What you are looking for is
string mySessionCookie = System.Web.HttpContext.Current.Request.Headers["Cookie"];
if (mySessionCookie.IndexOf(".ASPXAUTH", StringComparison.Ordinal) >= 0) {
// do something
}
EDIT
How about this, I haven't tested it but I remember doing something like this before
HttpCookie cookie = (HttpCookie)(Request.Cookies[FormsAuthentication.FormsCookieName]);
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);

Related

Custom MVC Authentication without SetAuthCookie()

Because of the requirements of my project, I'm wanting to provide custom authenticate for my MVC controller actions. Therefore, I will not be using SetAuthCookie().
Intially I set a cookie as follows;
string userData = EncDec.MakeString(user.Email + "|" + user.UserId);
//the Cookie and FormsAuthenticationTicket expiration date/time is the same
DateTime cookieExpiry = DateTime.Now.AddMinutes(AccountPage.MvcApplication.COOKIE_EXPIRY_MINUTES);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, // ticket version
user.UserName, // authenticated username
DateTime.Now, // issueDate
cookieExpiry, // expiryDate
false, // true to persist across browser sessions
userData, // can be used to store additional user data
FormsAuthentication.FormsCookiePath); // the path for the cookie
string encryptedTicket = FormsAuthentication.Encrypt(ticket);
//create the cookie
HttpCookie cookie = new HttpCookie("ADV_" + Extensions.ControllerExtensionMethods.GetGuid(this), encryptedTicket);
cookie.Secure = true;
cookie.HttpOnly = true;
cookie.Expires = cookieExpiry;
Response.Cookies.Add(cookie);
The HttpCookie is being saved in the client browser with a encrypted FormsAuthenticationTicket.
Then within my controller actions, whenever I need to check and verify that the user is authenticated I call this method;
public static FormsAuthenticationTicket IsAuthenticated(string guid)
{
HttpCookie cookie = HttpContext.Current.Request.Cookies["ADV_" + guid];
if (cookie != null)
{
string encryptedTicket = cookie.Value;
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(encryptedTicket);
if (!ticket.Expired)
{
//if the user is authenticated and the cookie hasn't expired we increase the expiry of the cookie - keep alive
DateTime cookieExpiry = DateTime.Now.AddMinutes(AccountPage.MvcApplication.COOKIE_EXPIRY_MINUTES);
//create a new ticket based on the existing one
FormsAuthenticationTicket newTicket = new FormsAuthenticationTicket(
ticket.Version, // ticket version
ticket.Name, // authenticated username
ticket.IssueDate, // issueDate
cookieExpiry, // expiryDate, changed to keep alive if user is navigating around site
false, // true to persist across browser sessions
ticket.UserData, // can be used to store additional user data
ticket.CookiePath); // the path for the cookie
string newEncryptedTicket = FormsAuthentication.Encrypt(newTicket);
//keep alive
HttpCookie newCookie = new HttpCookie("ADV_" + guid, newEncryptedTicket);
newCookie.Secure = true;
newCookie.HttpOnly = true;
newCookie.Expires = cookieExpiry;
HttpContext.Current.Response.Cookies.Set(newCookie);
return newTicket;
}
}
return null;
}
Every time the user is re-authenticated, I am increasing the time out of when the cookie will expire, so that the login is keep alive.
Everything seems to work fine, and the users are correctly authenticated, and if they aren't authenticated I redirect them to a login page, and they can't access methods if they aren't authenticated either.
My questions are:
Is this way of dealing with the authentication secure.
Is there anything I should be aware of, in terms of a security risk.
Thanks.
You basically need to look at creating a Custom Authentication Attribute.
I'm not going to provide the actual implementation here, but this will put you on the right path.
Here's the basic representation :-
public class GoogleAuthAttribute : FilterAttribute, IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
IIdentity ident = filterContext.Principal.Identity;
if (!ident.IsAuthenticated || !ident.Name.EndsWith("#google.com"))
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
{
if (filterContext.Result == null || filterContext.Result is HttpUnauthorizedResult)
{
filterContext.Result =
new RedirectToRouteResult(new RouteValueDictionary
{
{"controller", "GoogleAccount"},
{"action", "Login"},
{"returnUrl", filterContext.HttpContext.Request.RawUrl}
});
}
}
}
I've took this from the Apress Book i'm currently reading on MVC5. If OnAuthentification fails, you set the Result property of the AuthenticationContext, this is then passed to the AuthenticationChallengeContext, where you can add your challenge code.
In my example, the user is redirected to the login page.
All you need t do, is place this AuthentificationAttribute on the Action Methods you require.
You should be able to build in or work your custome security code in to this.
You should really ask yourself if its a good idea to be adding custom security measures, as it can lead to more problems that you want.

Login session never expires

I am creating a HttpCookie in my AccountController during the login process like so
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
When model.RememberMe is false I notice that I am never logged out, even if I restart the browser. Shouldn't the session expire after 20 minutes or on closing the browser?
I also, during the authentication process, set a cookie to store Role information using this method. This also sets a non persistent cookie. Is this affecting the login cookie?
What I notice is that, when I deserialize my cookie in Global.asax.cs the HttpCookie has an expiration of DateTime.MinValue and the authTicket has an expiration of up to 20minutes in the future. If I let this time elapse I do not notice the user logs out, in fact something has renewed the cookie as the expiration date jumps 20minutes or so in the future - but I can't see this happening within my code!
Can anyone help explain what is happening?
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
var authTicket = FormsAuthentication.Decrypt(authCookie.Value);
var serializer = new JavaScriptSerializer();
var serializeModel = serializer.Deserialize<EMPrincipalSerializeModel>(authTicket.UserData);
if (!authTicket.Expired && serializeModel != null && HttpContext.Current.User.Identity.IsAuthenticated)
{
var newUser = new EMPrincipal(authTicket.Name);
newUser.Id = serializeModel.Id;
newUser.RealName = serializeModel.RealName;
newUser.Username = serializeModel.UserName;
HttpContext.Current.User = newUser;
}
}
}
Your authentication expiration is changing because of sliding expiration, meaning that on each request the expiration is reset to to x minutes in the future. This is the default behavior for FormsAuthentication. Authentication will only expire after x minutes of inactivity, each time you are invoking a request to check the session, you are renewing it.
You can disable this if you wish: https://msdn.microsoft.com/en-us/library/system.web.security.formsauthentication.slidingexpiration%28v=vs.110%29.aspx
If disabled, authentication will expire x minutes after creation, and not x minutes after inactivity.

RememberMe Cookie set expirations time but on Firebug appears as Expiration as Session

I'm using C# and MVC5 to create my own cookie using this code:
// Prepare the ticket
HttpContext.Response.Cookies.Clear();
FormsAuthenticationTicket ticket =
new FormsAuthenticationTicket(1,
"MYNAME",
DateTime.Now,
DateTime.Now.AddDays(10), // <<- Expires 10 days
true,
null);
// Encrpt the ticket
string encryptedCookie = FormsAuthentication.Encrypt(ticket);
// Create new cookie
HttpCookie cookie = new HttpCookie("MYNAME", encryptedCookie);
cookie.Path = FormsAuthentication.FormsCookiePath;
// Send the Cookie back to the browser
HttpContext.Response.Cookies.Add(cookie);
On the Web.Config I set the name to be
<authentication mode="Forms">
<forms name="MYNAME" loginUrl="~/Account/Login"></forms>
</authentication>
But when I look the Firebug, the Cookie appears as "MYNAME" but the "expires" is set to Session.
And in fact, when I close the browser, the cookie disappears and when I go back to the site, I always have to login again. The same happens with all other browsers.
What am I doing wrong??
The problem was that I was setting the Expiration at the "Ticket" level but NOT at the "Cookie" level.
Adding
cookie.Expires = ticket.Expiration;
..solved the issue !!
So the entire code should look like this:
// Prepare the ticket
HttpContext.Response.Cookies.Clear();
FormsAuthenticationTicket ticket =
new FormsAuthenticationTicket(1,
"MYNAME",
DateTime.Now,
DateTime.Now.AddDays(10), // <<- Expires 10 days
true,
null);
// Encrpt the ticket
string encryptedCookie = FormsAuthentication.Encrypt(ticket);
// Create new cookie
HttpCookie cookie = new HttpCookie("MYNAME", encryptedCookie);
cookie.Path = FormsAuthentication.FormsCookiePath;
// THE MISSING LINE IS THIS ONE
cookie.Espires = ticket.Expiration; // <<- Uses current Ticket Expiration
// Send the Cookie back to the browser
HttpContext.Response.Cookies.Add(cookie);
How it goes with other browsers? Chrome, IE?
If it works fine there, then it should be working on FF as well.
If it doesn't work there then possibility is there is issue with code
Take a look on these articles
FormsAuthenticationTicket expires too soon
Basic one
http://www.codeproject.com/Articles/244904/Cookies-in-ASP-NET
http://msdn.microsoft.com/en-us/library/ms178194.ASPX
Thanks

Why my users get logged out when the session gets restarted?

I have this application that uses custom methods to register and loggin users using FormsAuthentication. The server where this is hosted has a policy of restarting the sessions every 15 minutes and when that happens all my users get logged out. The code to loggin a user is:
var user = this.accountRepo.GetUser(id);
// Create the forms authentication cookie
var cookieValue = user.name;
HttpCookie cookie = FormsAuthentication.GetAuthCookie(cookieValue, true);
// Dercrypt the cookie
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
// Create a new ticket with the desired data
FormsAuthenticationTicket newTicket = new FormsAuthenticationTicket
(
ticket.Version,
ticket.Name,
ticket.IssueDate,
DateTime.Now.AddYears(1),
true,
user.Authentication
);
// Update the cookies value
cookie.Value = FormsAuthentication.Encrypt(newTicket);
Response.Cookies.Set(cookie);
accountRepo.Login(user);
With the Forms cookie created and with my Authentication data, which is basically the users hashed password, I then use the following logic to display the Login button or the username:
#{
var accountRepo = new AccountRepository();
var user = accountRepo.GetCurrentUser();
}
#if(user != null && user.LoggedIn) {
<div>#Html.ActionLink(Context.User.Identity.Name + " - Logout", "LogOff", "Account", null, new { #class = "logout_link" })</div>
}
else
{
#Html.ActionLink("Login", "Login", "Account", new { returnUrl = Request.Url.AbsoluteUri }, new { #class = "login_link" })
}
And that "GetCurrentUser()" method is:
var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (cookie != null)
{
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
return db.Users.SingleOrDefault(u => u.Authentications.Equals(ticket.UserData, StringComparison.CurrentCultureIgnoreCase));
}
return null;
Am I missing something here? I believe that with this code It should matter if the session restarts, my users should stay logged in.
Thanks in advance.
It's just as Mystere Man said. The cookie name was getting re-generated every time the session rebooted, so the app was looking for the cookie with a different name than what it had before.
For the peace of mind of all of you that helped me, and for the developer that will support this app in the future, I refactored it so its not that "evil" anymore :P

Forms Authentication and All fine except blank datavalue field

I am trying to create and read a forms authentication cookie in a c# web app that I am developing.
I create the ticket
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1, "myData", DateTime.Now, DateTime.Now.AddMinutes(60), true, "Hello");
// Now encrypt the ticket.
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName,encryptedTicket);
// Add the cookie to the outgoing cookies collection
Response.Cookies.Add(authCookie);
Then when I retrieve the ticket using:
HttpCookie authCookie = Context.Request.Cookies[cookieName];
FormsAuthenticationTicket authTicket = null;
authTicket = FormsAuthentication.Decrypt(authCookie.Value)
I can see authTicket now has all of the data, cookie creation date, expiration date, name="mydata"... etc.
But there is nothing in the dataValue... I am expecting "Hello" to be there.
When I debug, I can see it is in the ticket right before encryption... it is getting lost in the decryption I suppose?
Any Help?
In order to set the cookie you use the FormsAuthentication.FormsCookieName property while when you read it you use the cookieName variable. Are you sure that both point to the same cookie? Also verify with FireBug that the value in the cookie is the same as the one you see when you debug.
Try using SetAuthCookie(authCookie) instead of REsponse.Cookies.Add(authCookie).
http://msdn.microsoft.com/en-us/library/aa480476.aspx

Categories