How to stop basepage from recursivly detecting session timeout - asp.net

Alternate Title: How to redirect on session timeout
FINAL SOLUTION: Credit to: Robin Day (Although I tested Ben's solution and it also works and the other two solutions are also both good solutions)
I got rid of the basepage that I had originally.
Put this in the Session_Start of Global.asax
void Session_Start(object sender, EventArgs e)
{
string cookie = Request.Headers["Cookie"];
// Code that runs when a new session is started
if ((null != cookie) && (cookie.IndexOf("ASP.NET_SessionId") >= 0))//&& !Request.QueryString["timeout"].ToString().Equals("yes"))
{
if(Request.QueryString["timeout"] == null || !Request.QueryString["timeout"].ToString().Equals("yes"))
Response.Redirect("Default.aspx?timeout=yes");
}
}
Put this on the Defualt.aspx page:
if (!IsPostBack)
{
if (Request.QueryString["timeout"] != null && Request.QueryString["timeout"].ToString().Equals("yes"))
{
Response.Write("<script>" +
"alert('Your Session has Timedout due to Inactivity');" +
"location.href='Default.aspx';" +
"</script>");
}
}
This solution works even when the timeout occurs on the Default.aspx page
END SOLUTION
I have a base page that checks for session timeout. (thats all it does). I want to redirect to the home page if there is a session timeout. However, the home page also inherits from this base page.
I'm not sure if i'm explaining this well:
Step 1: One of my pages loads
Step 2: It sits for more than 20 minutes(this would cause session timeout).
Step 3: I click on something that causes poastback
Step 4: Basepage detects timeout and redirects to default.aspx
Step 5: As default.aspx loads, the basepage detects that there is still a timeout and once again tries to redirect to default.aspx.
Step 6: Repeat step 5
The bold is the undesired effect...
This is the basepage code.
using System;
using System.Web.UI;
public class SessionCheck : System.Web.UI.Page
{
public SessionCheck() {}
override protected void OnInit(EventArgs e)
{
base.OnInit(e);
if (Context.Session != null)
{
//check the IsNewSession value, this will tell us if the session has been reset.
//IsNewSession will also let us know if the users session has timed out
if (Session.IsNewSession)
{
//now we know it's a new session, so we check to see if a cookie is present
string cookie = Request.Headers["Cookie"];
//now we determine if there is a cookie does it contains what we're looking for
if ((null != cookie) && (cookie.IndexOf("ASP.NET_SessionId") >= 0))
{
//since it's a new session but a ASP.Net cookie exist we know
//the session has expired so we need to redirect them
Response.Redirect("Default.aspx?timeout=yes&success=no");
}
}
}
}
}
Thanks!!!
(If you need further clarification please ask)
Note: I know that if I redirect to a page that does not inherit from this basepage it would fix the problem. But i don't like this solution.

Can you use the event Session_OnStart within the Global.asax?
This will be fired once whenever a new session begins. You can do your redirect in there.
The key I guess though is that you only redirect to the home page if you're not already on it. You can do this by just checking the URL in the Request object.

We had similar situation. We first check if the there's Session Timeout and within that we only redirect if current page is not the Default Login page.

The below will do the trick with your existing code but robin day's sugestion of using global.asax is a better option.
if (Context.Session != null)
{
//check the IsNewSession value, this will tell us if the session has been reset.
//IsNewSession will also let us know if the users session has timed out
if (Session.IsNewSession)
{
//now we know it's a new session, so we check to see if a cookie is present
string cookie = Request.Headers["Cookie"];
//now we determine if there is a cookie does it contains what we're looking for
if ((null != cookie) && (cookie.IndexOf("ASP.NET_SessionId") >= 0))
{
//since it's a new session but a ASP.Net cookie exist we know
//the session has expired so we need to redirect them
//Only redirect if the current page is NOT Default.aspx
if (Request.Path.Substring(Request.Path.LastIndexOf("/")) != "/Default.aspx")
{
Response.Redirect("Default.aspx?timeout=yes&success=no");
}
else if (Request.QueryString["timeout"] == "yes")
{
ScriptManager.RegisterStartupScript(this, this.GetType(), "TimeOutScript", "alert('Your session timed out');", true);
}
}
}
}
Editing the answer to respond to comments below becuase it is easier than adding more comments:
I did not provide you with a solution to redirect when the session timed out, you simply cannot do that, the session has timed out, there is nothing to redirect. You said "i just wanna know if there was a timeout" and i gave you a solution for that. There is no real way to tell if a user is hitting page x becuase their session has timed out or because they have opened a browser and started a new session, all you know is there is no existing session. If they hit a page they need to be authenticated to view then you can assume the session has timed out because they should not be hitting the page from scratch. But you can't do this on the home page becuase you can't tell the difference between a user who's session timed out on this page and a user who is just hitting the page for the first time. I have updated the code so that for every page other than Default.aspx it will redirect, and if it has redirected to Default.aspx due to a session time out it will pop up a javascript alert.

Create a common base page that contains all things you want to have both in your home page and in other pages.
Than inherit from this your home page and another base page (say SessionCheck) in which to put your session expiration logic. All your pages but home have than to inherit from SessionCheck page.
public class BasePage: System.Web.UI.Page //(this contains all things shared between all pages)
public class SessionCheck : BasePage //(your session loginc in this class)
public class Home : BasePage

Put a property in your base page:
public bool IsDefault { get; set; }
In the PreInit event of your default page:
public void Default_PreInit(object sender, System.EventArgs e)
{
this.IsDefault = true;
}
Modify your session check:
if (Context.Session != null && !this.IsDefault)
{
// blibbitty blah blah
}

Related

User ID initialization on Master Page?

I have a site with multiple pages, not necessarily heirarchical. I want to query the user's identity (using AD...) whenever the user first enters the site, and create session state variables for the convenience of other pages as needed. A user could possibly enter the site without going through the default.aspx page, so I thought I'd put the code in the Master Page's code-behind.
On the assumption this is a good idea, versus some sort of static class that maintains this information, I started setting it up, but found the Master Page code-behind doesn't always seem to get fired when I enter the site. Is this a debugging phenomenon, or am I right, and the Master Page is the wrong place to put this code...?
I would recommend using the Global.asax class. You'll need to add it to your web app if it's not already there. Once you have it, you can then use the various events (session start and end, app start and end and error) to implement business logic particular to what you need exactly.
I tend to monkey around with the logged in user in the Application_PreRequestHandlerExecute event of the global.asax. This will allow you to look at the User Principle (eg - User.Identity.Name) to see who is logged in (or if they're not logged in) and do what you need to (such as set Session information for the user, etc.).
Here's a tidbit of code I've got on one .NET web app that uses the Global.asax for storing user data in the Session.
protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e) {
if (Context.Handler is IRequiresSessionState || Context.Handler is IReadOnlySessionState) {
SetUserItem();
}
}
private void SetUserItem() {
if (Session["UserItem"] == null)
Server.Execute("~/SetSessionUserObj.aspx", true);
}
... and then the SetSessionUserObj.aspx.cs
protected void Page_Load(object sender, EventArgs e) {
string ID = User.Identity.Name;
MyUser myUser = new MyUser();
UserItem userItem = myUser.GetUserItemByID(ID);
if (userItem != null) {
Session["UserItem"] = userItem;
}
}
This is just one manner that you can go about accessing a user's identity in the global.asax. You don't necessarily have to go about doing a Server.Execute to set user data (I just did it for other reasons that fall outside the scope of this question).
Good luck.

ASP.Net PreSendRequestHeaders unable to access session

The goal is to intercept when user code calls Response.Redirect and alter the URL the browser is being redirected to. To alter this URL, I need access to Session (stored in Session is information that tells me what I should put in this URL.) Mostly I'm just appending a query argument to the redirect location under a circumstance.
PreSendRequestHeaders does let me alter Response.RedirectLocation. That's fine. However, I'm unable to access Session state from here. It's apparently been released before this event is fired.
So, I need a way to get this information into PreSendRequestHeaders; or I need another way to accomplish this. Ultimately my goal is to just append an argument to the query string of wherever the browser is being redirected.
If you can modify the code that writes to Session then you can use Context.Items.
For example, before the Response.RedirectPermanent method existed, I used the following:
//in some library
public static void PermanentRedirect(this HttpContext context, string url)
{
context.Items["IsPermanentRedirect"] = true;
context.Response.Redirect(url);
}
//in global.asax
void Application_PreSendRequestHeaders(object sender, EventArgs e)
{
if (Response.IsRequestBeingRedirected && (bool) (Context.Items["IsPermanentRedirect"] ?? false))
{
Response.Status = "301 Moved Permanently";
}
}

Request.IsAuthenticated always true. Wham am I missing?

I'm trying to add username to cookie and clear it on logouts but Request.IsAuthenticated returns always true so I'm not able to know if it is a member or not on the next page.
Here is what I'm trying:
public void Logout()
{
FormsAuthentication.SignOut();
Session.Abandon(); // trying everything...
}
protected void signin_submit_Click(object sender, EventArgs e)
{
//checklogins...
HttpCookie authCookie = FormsAuthentication.GetAuthCookie(username_top.Text, true);
}
I'm clicking the Logout from javascript function, and it hits the debugger so I'm sure that its being called.
I'm checking the value on Page_Load:
protected void Page_Load(object sender, EventArgs e)
{
bool isAuth = Request.IsAuthenticated; // always true !!!
if (isAuth)
{
registeredDiv.Visible = true;
guestDiv.Visible = false;
}
}
What am I missing? How can I know if the user is logged in or not?
Edit: I found out that authenticationMode was set to Windows in web.config.
Edited it as:
<authentication mode="Forms">
But now it always returns false. I tried redirecting after login to same page yet still didn't work. Any ideas?
When you say that you are hitting the debugger and making the call through Javascript, are you doing this via ajax? I'm wondering that you are indeed signing out, but not redirecting the user, which means that the cookie the user has on there machine is not being overwritten/destroyed so that when they next request a page, it works again because the cookie still exists.
When the user hits your logout button on your site, you really need to have them redirect to a new page so that the cookies can be set (or unset as it were) properly...Here's the MSDN Article on Forms Authentication Signout and its example code calls RedirectToLoginPage() which will sort out the cookies as previously mentioned.
I've found FireCookie for Firebug really helpful in debugging cookie related issues.
Edit
According to the MSDN article I linked to:
You can use the SignOut method in
conjunction with the
RedirectToLoginPage method to log one
user out and allow a different user to
log in.
Your code does not have RedirectToLoginPage called, so just calling FormsAuthentication.SignOut() isn't enough (unless you are going to manually do the work of RedirectToLoginPage)
Edit 2
Perhaps change your Sign In Button code to use this function instead:
FormsAuthentication.RedirectFromLoginPage(username_top.Text, true)
As this will automatically take care of setting all the cookie stuff...
Edit 3
Glad you sorted it out, The above function (edit 2) would have just done what you said you had missed out on (adding the cookie to the response)...

Logoff User when browser tab page is closed, ASP.NET MVC

In one of the ASP.NET MVC apps we would like to logoff the user automatically if he closes the browser tab in which the app is opened.
We are using the following code when he authenticates.
FormsAuthentication.SetAuthCookie(userName, false)
As of now, if we closes the browser window and relaunch it, users are asked to authenticate again. But we want to ask users to authenticate again if they close the tab and try to access any of the website urls.
We decided to use cookie less authentication so that the authentication token is part of the url. When the tab is closed and they open the website again, they will be asked to authenticate again :)
I have not tried this myself, but I think the following approach should work:
On the client side, you can use the OnUnload event of your document to launch a javascript function that would call your server-side signout method via ajax.
On the server side, you should have the action method call FormsAuthentication.SignOut() and Session.Abandon();
A browser clears all Session scoped objects only when it is completely closed, and not when an individual tab is closed.
One way could be to use a very low Session timeout and have a server-side script poll every few seconds to hit an object on the page. This will extend Session time again. So if a tab is closed, the script can't find the object thereby allowing the Session to timeout. One problem here is if your app is on a pretty high load, your app could DoS itself!
Actually there is no way we can LogOff the user when the user closes the browser tab. The only way for this is to check if the the user is authenticated when we call the LogOn method in the Controller.
This code is an example of how I do it in ASP.Net MVC 3.
public ActionResult LogOn()
{
if (Request.IsAuthenticated)
{
FormsAuthentication.SignOut();
return RedirectToAction("Index","ProductManager");
}
return View();
}
You can simply use session variables to automatically log off anybody trying to return to the secured destination page. Create a single session variable (integer or boolean) and in the onclick event of your login button reset it to a known state after acknowledging that the user has a valid credential then set or increment that session variable in the page_load event of the page your trying to secure. Test these values and signout the user if he is trying to return to the page or do nothing if otherwise. The code may look similar to this.
protected void btnLogin_Click(object sender, EventArgs e)
{
if (IsAuthenticated == true)
Session["IsUserLoggedIn"] = (int)0;
}
protected void Page_Load(object sender, EventArgs e)
{
if (HttpContext.Current.User.Identity.IsAuthenticated == true)
{
if (Session["IsUserLoggedIn"] != null)
{
int IsUserLoggedIn = (int)Session["IsUserLoggedIn"];
if (IsUserLoggedIn <= 0)
{
Session["IsUserLoggedIn"] = (int)IsUserLoggedIn + 1;
}
else
{
Session["IsUserLoggedIn"] = (int)0;
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
}
}
}
else { Session["IsUserLoggedIn"] = (int)0; }
}

How do you stop a page_load event from running if the page is refreshed

Each time my webpage is loaded it runs a routine in the page_load event of the page behind which increments a viewcount by 1.
The problem I have is that the routine runs even if use refresh to reload the page.
How do I stop this routine from running if the page has been viewed by a particular user in their current session?
I thought I could use:
If Not Page.IsPostBack Then
(run the routine)
but it does not seem to work.
Do I need to use session state or cookies etc.?
The If Not Page.IsPostback will only help if the user clicks a button on that page. If the user just refreshes the page (like using F5) it won't work.
The problem is
do you want to add to the counter if the user comes back to the page after he's been to another page on your site (i.e. homepage -> productpage -> homepage) or not.
You could use a Dictionary and store it in the user's Session. The Dictionary would be a Dictionary of type Dictionary<string, int>. When the user visits the page, you retrieve the dictionary and see if there is already an entry for the current page + querystring (the string Key).
if not, add it. Then depending on whether you want to increment the count for that page if the user revisits the page after first going to another page increment the counter (or not).
you can check if the user's came from another url by using: Request.UrlReferrer
You could record whether the user has visited the page within the session. You could just place a bool within the session under the page path. This way it'll scope to the individual user and it'll work for the duration of their session.
To record that the user has visited the page you could do the following:
HttpContext.Current.Session[pagePath] = true;
and to get whether the user has visited the page you could do this:
bool hasUserVisitedPage = (bool)HttpContext.Current.Session[pagePath];
Here's how it would come together within your page load:
protected void Page_Load(object sender, EventArgs e)
{
//set the default for whether the user visited the page
bool hasUserVisitedPage = false;
//get the path of the page
string pagePath = HttpContext.Current.Request.Url.LocalPath;
//find out if the user visited the page by looking in the session
try { hasUserVisitedPage = (bool)HttpContext.Current.Session[pagePath]; }
//we don't care if the value wasn't present (and therefore didn't cast)
catch {}
//if the user hasn't visited the page before
if (!hasUserVisitedPage )
{
//record that the page has now been visited
HttpContext.Current.Session[pagePath] = true;
//put the rest of your load logic here...
}
}
If you want to incorporate this technique on multiple pages I'd encapsulate this functionality into a helper class so you don't keep repeating yourself.
public static class PageHelper
{
public static bool hasPageBeenViewed()
{
//set the default for whether the user visited the page
bool hasUserVisitedPage = false;
//get the path of the page
string pagePath = HttpContext.Current.Request.Url.LocalPath;
//find out if the user visited the page by looking in the session
try { hasUserVisitedPage = (bool)HttpContext.Current.Session[pagePath]; }
//we don't care if the value wasn't present (and therefore didn't cast)
catch {}
//if the user hasn't visited the page before
if (!hasUserVisitedPage )
{
//record that the page has now been visited
HttpContext.Current.Session[pagePath] = true;
}
return hasUserVisitedPage;
}
}
Then it'd greatly simplify the load logic to the following:
(It would give you the added benefit of the logic being in a central location, which would be very handy if you needed to change it)
protected void Page_Load(object sender, EventArgs e)
{
//if the user hasn't visited the page before
if (!PageHelper.hasPageBeenViewed())
{
//put the rest of your load logic here...
}
}

Resources