Help! Why do my IE users get randomly logged off? - c#

ASP.NET MVC 2.0, here's my auth code:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Login(string username, string password, string returnUrl) {
if (ModelState.IsValid) {
// Attempt to login
var loginSuccessful = provider.ValidateUser(username, password);
if (loginSuccessful) {
FormsAuthentication.SetAuthCookie(username, true);
if (!String.IsNullOrEmpty(returnUrl))
return Redirect(returnUrl);
return RedirectToAction("Index", "Home");
}
}
return View(Language + "/Login", Vd);
}
Pretty much straight default authentication. Works fine for logging in. However, IE users get auto logged off randomly, even while they're active on the site. Other browsers work fine. Here's the forms auth from web.config:
<authentication mode="Forms">
<forms loginUrl="~/en/Account/Login" timeout="2880"/>
</authentication>
Where do I begin to look in this case? Have I found a bug?

As far as I can see everything seems fine, however, could your issue be something to do with your use of a persistent cookie? I think persistent cookies are not meant to timeout, which is why you might be using them.
Try using a non-persistent one instead, and see if that works:
FormsAuthentication.SetAuthCookie(username, false);
Also, a few others notes of interest:
I think that the timeout attribute in a web.config is specified in minutes. You've specified more than 2000 minutes.
By default, sliding expiration is disabled, so after n minutes it will timeout anyway. If this isn't what you want, then add a slidingExpiration="true" entry onto your <forms/> element in the web.config.

What kind of session mode are you using-in process or out of process? If you are using in process with non-persistent cookie and the application pool recycles, then session is lost.

Related

Preventing malicious user from accessing a page after logout [duplicate]

Smashed my head against this a bit too long. How do I prevent a user from browsing a site's pages after they have been logged out using FormsAuthentication.SignOut? I would expect this to do it:
FormsAuthentication.SignOut();
Session.Abandon();
FormsAuthentication.RedirectToLoginPage();
But it doesn't. If I type in a URL directly, I can still browse to the page. I haven't used roll-your-own security in a while so I forget why this doesn't work.
Users can still browse your website because cookies are not cleared when you call FormsAuthentication.SignOut() and they are authenticated on every new request. In MS documentation is says that cookie will be cleared but they don't, bug?
Its exactly the same with Session.Abandon(), cookie is still there.
You should change your code to this:
FormsAuthentication.SignOut();
Session.Abandon();
// clear authentication cookie
HttpCookie cookie1 = new HttpCookie(FormsAuthentication.FormsCookieName, "");
cookie1.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie1);
// clear session cookie (not necessary for your current problem but i would recommend you do it anyway)
SessionStateSection sessionStateSection = (SessionStateSection)WebConfigurationManager.GetSection("system.web/sessionState");
HttpCookie cookie2 = new HttpCookie(sessionStateSection.CookieName, "");
cookie2.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie2);
FormsAuthentication.RedirectToLoginPage();
HttpCookie is in the System.Web namespace. MSDN Reference.
Using two of the above postings by x64igor and Phil Haselden solved this:
1. x64igor gave the example to do the Logout:
You first need to Clear the Authentication Cookie and Session Cookie by passing back empty cookies in the Response to the Logout.
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
Session.Clear(); // This may not be needed -- but can't hurt
Session.Abandon();
// Clear authentication cookie
HttpCookie rFormsCookie = new HttpCookie( FormsAuthentication.FormsCookieName, "" );
rFormsCookie.Expires = DateTime.Now.AddYears( -1 );
Response.Cookies.Add( rFormsCookie );
// Clear session cookie
HttpCookie rSessionCookie = new HttpCookie( "ASP.NET_SessionId", "" );
rSessionCookie.Expires = DateTime.Now.AddYears( -1 );
Response.Cookies.Add( rSessionCookie );
2. Phil Haselden gave the example above of how to prevent caching after logout:
You need to Invalidate the Cache on the Client Side via the Response.
// Invalidate the Cache on the Client Side
Response.Cache.SetCacheability( HttpCacheability.NoCache );
Response.Cache.SetNoStore();
// Redirect to the Home Page (that should be intercepted and redirected to the Login Page first)
return RedirectToAction( "Index", "Home" );
}
Sounds to me like you don't have your web.config authorization section set up properly within . See below for an example.
<authentication mode="Forms">
<forms name="MyCookie" loginUrl="Login.aspx" protection="All" timeout="90" slidingExpiration="true"></forms>
</authentication>
<authorization>
<deny users="?" />
</authorization>
The key here is that you say "If I type in a URL directly...".
By default under forms authentication the browser caches pages for the user. So, selecting a URL directly from the browsers address box dropdown, or typing it in, MAY get the page from the browser's cache, and never go back to the server to check authentication/authorization. The solution to this is to prevent client-side caching in the Page_Load event of each page, or in the OnLoad() of your base page:
Response.Cache.SetCacheability(HttpCacheability.NoCache);
You might also like to call:
Response.Cache.SetNoStore();
I've struggled with this before too.
Here's an analogy for what seems to be going on... A new visitor, Joe, comes to the site and logs in via the login page using FormsAuthentication. ASP.NET generates a new identity for Joe, and gives him a cookie. That cookie is like the key to the house, and as long as Joe returns with that key, he can open the lock. Each visitor is given a new key and a new lock to use.
When FormsAuthentication.SignOut() is called, the system tells Joe to lose the key. Normally, this works, since Joe no longer has the key, he cannot get in.
However, if Joe ever comes back, and does have that lost key, he is let back in!
From what I can tell, there is no way to tell ASP.NET to change the lock on the door!
The way I can live with this is to remember Joe's name in a Session variable. When he logs out, I abandon the Session so I don't have his name anymore. Later, to check if he is allowed in, I simply compare his Identity.Name to what the current session has, and if they don't match, he is not a valid visitor.
In short, for a web site, do NOT rely on User.Identity.IsAuthenticated without also checking your Session variables!
After lots of search finally this worked for me . I hope it helps.
public ActionResult LogOff()
{
AuthenticationManager.SignOut();
HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
return RedirectToAction("Index", "Home");
}
<li class="page-scroll">#Html.ActionLink("Log off", "LogOff", "Account")</li>
This works for me
public virtual ActionResult LogOff()
{
FormsAuthentication.SignOut();
foreach (var cookie in Request.Cookies.AllKeys)
{
Request.Cookies.Remove(cookie);
}
foreach (var cookie in Response.Cookies.AllKeys)
{
Response.Cookies.Remove(cookie);
}
return RedirectToAction(MVC.Home.Index());
}
This Answer is technically identical to Khosro.Pakmanesh. I'm posting it to clarify how his answer differs from other answers on this thread, and in which use case it can be used.
In general to clear a user-session, doing
HttpContext.Session.Abandon();
FormsAuthentication.SignOut();
will effectively log out the user. However, if in the same Request you need to check Request.isAuthenticated (as may often happen in an Authorization Filter, for example), then you will find that
Request.isAuthenticated == true
even _after you did HttpContext.Session.Abandon() and FormsAuthentication.SignOut().
The only thing that worked was doing
AuthenticationManager.SignOut();
HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
That effectively sets Request.isAuthenticated = false.
The code you posted looks like it should correctly remove the forms authentication token, so it is possible that the folders/pages in question are not actually protected.
Have you confirmed that the pages cannot be accessed before a login has occured?
Can you post the web.config settings and login code that you are using?
I have been writing a base class for all of my Pages and I came to the same issue.
I had code like the following and It didn't work. By tracing, control passes from RedirectToLoginPage() statement to the next line without to be redirected.
if (_requiresAuthentication)
{
if (!User.Identity.IsAuthenticated)
FormsAuthentication.RedirectToLoginPage();
// check authorization for restricted pages only
if (_isRestrictedPage) AuthorizePageAndButtons();
}
I found out that there are two solutions.
Either to modify FormsAuthentication.RedirectToLoginPage(); to be
if (!User.Identity.IsAuthenticated)
Response.Redirect(FormsAuthentication.LoginUrl);
OR to modify the web.config by adding
<authorization>
<deny users="?" />
</authorization>
In the second case, while tracing, control didn't reach the requested page. It has been redirected immediately to the login url before hitting the break point.
Hence, The SignOut() method isn't the issue, the redirect method is the one.
I hope that may help someone
Regards
I just tried some of the suggestions here and while I was able to use the browser back button, when I clicked on a menu selection the [Authorize] token for that [ActionResult] sent me right back to the login screen.
Here is my logout code:
FormsAuthentication.SignOut();
Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
Response.Cache.SetExpires(DateTime.Now.AddSeconds(-1));
HttpCookie cookie = HttpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
if (cookie != null)
{
cookie.Expires = DateTime.Now.AddDays(-1);
Response.Cookies.Add(cookie);
}
Although the back function on the browser took me back and displayed the secured menu (I am still working on that) I was not able to do anything that was secured in the app.
Hope this helps
I've tried most answers in this thread, no luck. Ended up with this:
protected void btnLogout_Click(object sender, EventArgs e)
{
FormsAuthentication.Initialize();
var fat = new FormsAuthenticationTicket(1, "", DateTime.Now, DateTime.Now.AddMinutes(-30), false, string.Empty, FormsAuthentication.FormsCookiePath);
Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(fat)));
FormsAuthentication.RedirectToLoginPage();
}
Found it here: http://forums.asp.net/t/1306526.aspx/1
This started happening to me when I set the authentication > forms > Path property in Web.config. Removing that fixed the problem, and a simple FormsAuthentication.SignOut(); again removed the cookie.
It could be that you are logging in from one subdomain (sub1.domain.com) and then trying to logout from a different subdomain (www.domain.com).
I just had the same problem, where SignOut() seemingly failed to properly remove the ticket. But only in a specific case, where some other logic caused a redirect. After I removed this second redirect (replaced it with an error message), the problem went away.
The problem must have been that the page redirected at the wrong time, hence not triggering authentication.
I am having a similar issue now and I believe the problem in my case as well as the original poster is because of the redirect. By default a Response.Redirect causes an exception which immediately bubbles up until it is caught and the redirect is immediately executed, I am guessing that this is preventing the modified cookie collection from being passed down to the client. If you modify your code to use:
Response.Redirect("url", false);
This prevents the exception and seems to allow the cookie to be properly sent back to the client.
Just try to send a session variable when you press log in.
And on the welcome page, first check whether that session is empty like this in the page load or in the Init Event:
if(Session["UserID"] == null || Session["UserID"] == "")
{
Response.Redirect("Login.aspx");
}
I wanted to add some information to help understand the problem. Forms Authentication allows for storing user data either in a cookie, or in the query string of the URL. The method your site supports can be configured in the web.config file.
According to Microsoft:
The SignOut method removes the forms-authentication ticket information
from the cookie or the URL if CookiesSupported is false.
At the same time, they say:
One of the HttpCookieMode values that indicates whether the
application is configured for cookieless forms authentication. The
default is UseDeviceProfile.
Lastly, regarding UseDeviceProfile, they say:
If the CookieMode property is set to UseDeviceProfile, the
CookiesSupported property will return true if the Browser for the
current Request supports both cookies and redirecting with cookies;
otherwise, the CookiesSupported property will return false.
Piecing this all together, depending on the user's browser, the default configuration may result in CookiesSupported being true, which means the SignOut method doesn't clear the ticket from the cookie. This seems counter-intuitive and I don't know why it works this way -- I would expect SignOut to actually sign the user out under any circumstances.
One way to make the SignOut work by itself is to change the cookie mode to "UseCookies" (i.e. cookies are required) in the web.config file:
<authentication mode="Forms">
<forms loginUrl="~/Account/SignIn" cookieless="UseCookies"/>
</authentication>
According to my tests, doing this makes SignOut work by itself at the cost of your site now requiring cookies to function properly.
For me, the following approach works. I think if there is any error after the "FormsAuthentication.SignOut()" statement, SingOut doesn't work.
public ActionResult SignOut()
{
if (Request.IsAuthenticated)
{
FormsAuthentication.SignOut();
return Redirect("~/");
}
return View();
}
Are you testing/seeing this behaviour using IE? It's possible that IE is serving up those pages from the cache. It is notoriously hard to get IE to flush it's cache, and so on many occasions, even after you log out, typing the url of one of the "secured" pages would show the cached content from before.
(I've seen this behaviour even when you log as a different user, and IE shows the "Welcome " bar at the top of your page, with the old user's username. Nowadays, usually a reload will update it, but if it's persistant, it could still be a caching issue.)
Doing Session.abandon() and destroying the cookie works pretty good. I'm using mvc3 and it looks like the problem occurs if you go to a protected page, log out, and go via your browser history. Not a big deal but still kinda of annoying.
Trying to go through links on my web app works the right way though.
Setting it to not do browser caching may be the way to go.
For MVC this works for me:
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
return Redirect(FormsAuthentication.GetRedirectUrl(User.Identity.Name, true));
}
Be aware that WIF refuses to tell the browser to cleanup the cookies if the wsignoutcleanup message from STS doesn't match the url with the name of the application from IIS, and I mean CASE SENSITIVE. WIF responds with the green OK check, but will not send the command to delete cookies to browser.
So, you need to pay attention to the case sensitivity of your url's.
For example, ThinkTecture Identity Server saves the urls of the visiting RPs in one cookie, but it makes all of them lower case. WIF will receive the wsignoutcleanup message in lower case and will compare it with the application name in IIS. If it doesn't match, it deletes no cookies, but will report OK to the browser. So, for this Identity Server I needed to write all urls in web.config and all application names in IIS in lower case, in order to avoid such problems.
Also don't forget to allow third party cookies in the browser if you have the applications outside of the subdomain of STS, otherwise the browser will not delete the cookies even if WIF tells him so.

ASP.NET website not allowing IE to log in with "Keep me Logged In" enabled

I've googled this issue and found very little help relevant to my issue, although a lot of information about fixing a similar issue on IE10. This is occurring on IE11, both on desktop & on Windows Phone.
If I log in on IE, it works correctly; however if I tick "Keep me logged in", as soon as it redirects me to my user homepage, I'm no longer logged in. This issue does not occur in Chrome.
I'm using .NET 4.5 with C# as the backend language. (ASP.NET, as mentioned)
Here's my relevant login code:
else if (ModelState.IsValid)
{
try
{
if (WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
return RedirectToAction("Landing", "Home", null);
}
else
{
ModelState.AddModelError("Password", "This password does not match the account you are trying to log in to.");
}
}
catch (System.InvalidOperationException e)
{
ModelState.AddModelError("", "Invalid operation exception occured: " + e.Message);
}
}
I can confirm:
My user details were correct
It successfully redirects me to /Landing/Home/, implying the server believes I have successfully logged in
I'm definitely running IE11 (See image above)
Is anyone familiar with this issue or have any idea how to correct it?
Add cookieless="UseCookies" to the <forms> element of your web.config. It should look something like this:
<authentication mode="Forms">
<forms loginUrl="your login url" cookieless="UseCookies" ... />
</authentication>

Redirect to page after Forms Authentication Timeout

In my asp.net web application, I'm using asp.net forms authentication with following configuration.
<authentication mode="Forms">
<forms name=".ASPNETAUTH" loginUrl="Login.aspx" protection="None" timeout="20" />
</authentication>
After form authentication time out, I would like to redirect to a different page. For example to 'SessionTimedOut.aspx' page.
I've found other questions on here, here is one, Forms Authentication Timeout vs Session Timeout
The answer given makes sense but the first line of code has me confused.
var cookie = Retrieve AuthenticationCookie();
if (cookie == null) return;
FormsAuthenticationTicket ticket = null;
try {
ticket = FormsAuthentication.Decrypt(cookie.Value);
} catch (Exceptoin decryptError) {
// Handle properly
}
if (ticket == null) return; // Not authorised
if (ticket.Expiration > DateTime.Now) {
Response.Redirect("SessionExpiredPage.aspx"); // Or do other stuff here
}
Now there is a
FormsAuthentication.GetAuthCookie()
which takes a username and bool to persist the cookie, but this is for creating an auth cookie not getting it. So, what would the var cookie, first line of code look like.
At the moment, I am using " in web config and then when user logins in settings a session and then on every post back in a page init in my base page am checking if that session is null, if so, redirecting to a session timed out page. This is not really what I want.
May have found out how to get cookie,
HttpCookie cookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
This doesn't work because when the authentication ticket expires, the cookie goes away and the the cookie var is null. Any other way to get this working? I would still like on post back check if authentication has expired and then take appropriate action. Any thoughts from anyone????
The thing to remember is that even though your session times out on the server end, the client end will not process anything until it's next request. At that time it will discover that it's session has expired and attempt to restart the session. A Response.Redirect or even Server.Redirect call won't help with this.
What you need to do is to synchronize your server timeout with your client timeout, and have some client script in place to redirect the user to a "Timed Out" type page. I've written up an article with some sample code on how to do that here.
Q: So, what would the var cookie, first line of code look like.?
var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName]

Why isn't .ASPAUX cookie being validated by FormsAuthentication?

I have a site that uses FormsAuthentication and yes, the name of the cookie is .ASPAUX :)
I can log in perfectly. The server creates a forms authentication ticket, packs it in a cookie, properly determines the expiration time (1 year ahead) and sends it to the client.
For some reason, after some time, even though the cookie is there yet (I can see it with FireCookies) HttpContext.Current.Request.IsAuthenticated becomes false at the server. It's as if the cookie couldn't be validated.
The problem is: Why would that happen? How can I debug why the cookie suddenly becomes invalid without expiring?
EDIT
Here's the login method:
public static bool Login(int id)
{
try
{
string securityToken = UserHelper.AuthenticateUser(id);
DateTime expiryDate = DateTime.Now.AddYears(1);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, id.ToString(), DateTime.Now, expiryDate, true,
securityToken, FormsAuthentication.FormsCookiePath);
string encryptedTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
cookie.Expires = expiryDate;
HttpContext.Current.Response.Cookies.Add(cookie);
return true;
}
catch
{
return false;
}
}
And the web.config:
<authentication mode="Forms">
<forms loginUrl="~/Login.aspx" timeout="2880" slidingExpiration="true"/>
</authentication>
Set static machine keys in your web.config to make sure that the encryption key used in generating your ticket survives an application pool being recycled (or your website being restarted in the ASP.NET web server)?
Also see the Forms Authentication Tickets section of this MSDN library article
A few things I can think of to check:
Do you have multiple domains (including www.domain.com vs domain.com)?
If so, either set the domain in the cookie as domain.com or ensure you always use the same domain
Are you using HTTPS?
If so, make sure you're always accessing the cookie via HTTPS or making sure that Secure is set to false on the HttpCookie (otherwise it's only accessible on HTTPS requests)
Are you writing the cookie from a virtual directory?
If so, the "path" on the cookie might be set and it won't be accessible from outside the path.
Do you have multiple web servers?
If so, make sure your machine key is set to the same value (though that should be throwing an exception)

Forms Authentication cookie seems to stop working

I want to have functionality on my application that lets a user check off that they wish to stay logged indefinitely (arbitrarily setting the cookie expiration at 3 months from NOW).
The code I have for dealing with this is
private static HttpCookie GetFormsAuthenticationCookie(string userNameResponse,
bool persistCookie)
{
var cookie = FormsAuthentication.GetAuthCookie(userNameResponse, persistCookie);
if (persistCookie)
cookie.Expires = DateTime.Now.AddMonths(3);
return cookie;
}
private void LoginUser(string userNameResponse, bool PersistCookie)
{
Response.Cookies.Add(GetFormsAuthenticationCookie(userNameResponse, PersistCookie));
string navigateAfterUrl = FormsAuthentication.GetRedirectUrl(userNameResponse,
PersistCookie);
Response.Redirect(navigateAfterUrl);
}
However at some point later when I return to the site I need to login again. I have verified that the cookie comes back with my expiration date and that it is not set as a session cookie (also tested with closing/reopening browser and cookie still exists). My one thought is that it has something to do with when ASP.NET expires the session.
I have a specific machine key setup in my web.config so shouldn't the same cookie work if IIS gets restarted etc? Does anyone have any suggestions on what could either be causing this or atleast on how to trace this further since I can't think of anything else to do.
When you call the GetAuthCookie method a FormsAuthenticationTicket is created with a timeout given by the Timeout property in web.config. So be sure to set it properly:
<authentication mode="Forms">
<forms
loginUrl="/someloginUrl"
requireSSL="true"
protection="All"
// This is the setting you are looking for! (it's in seconds)
timeout="120"
domain="example.com"
slidingExpiration="false"
name="cookieName" />
</authentication>
Once the ticket is encrypted it is used as a value for the cookie. When you set the Expires property of your cookie to a given value this indicates that it will be persisted on the client computer for the given period. Then on every request ASP.NET runtime will check the presence of the cookie, will try to decrypt the value and obtain the ticket. Next it will check if the ticket is still valid by using the Timeout property, so if you have a small timeout, no matter that your cookie is still transmitted, the ticket is no longer valid and the authentication will fail.

Categories

Resources