views:

4555

answers:

9

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.

+2  A: 

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?

AJ
+3  A: 

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>
jwalkerjr
A: 

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.)

Stobor
A: 

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).

jorsh1
A: 

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

Waheed Sayed
Also you could call Response.End() just after calling FormsAuthentication.RedirectToLoginPage()
murki
+6  A: 

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)
HttpCookie cookie2 = new HttpCookie("ASP.NET_SessionId", "");
cookie2.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie2);

FormsAuthentication.RedirectToLoginPage();
x64igor
+1  A: 

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.

Peder Skou
+2  A: 

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();
Phil Haselden
A: 

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!

Glen Little