I am doing a group project with 4 other people. We are designing a job kiosk in ASP.NET in MVC4 with embedded c#.
I am working on having the system log the user out if they are idle for 10 minutes. I need some help on how to start coding a way for the system to log the user out.
If you don't use "Windows Authentication", this at least depends on the session timeout you can control via web.config:
<configuration>
<system.web>
<sessionState timeout="10" />
</system.web>
</configuration>
As most techniques somehow rely on sessions, this will work in most scenarios.
the answer you are looking for is the one AliK suggested. You want to set an automatic timeout in the web.config so that it will automatically logout the user and redirect them to the login page after a certain amount of idle time.
<authentication mode="Forms">
<forms loginUrl="Login.aspx" protection="All" timeout="1" slidingExpiration="true">
</forms>
</authentication>
If I remember right, the timeout value is in minutes, not seconds or milliseconds. Also the sliding Expiration means that the timeout will reset each time you perform an action on the website. So if you have a timeout of 5 minutes and you sit idle for 4 before clicking a button on the site then after the button click you get a new 5 minute timeout.
Here is how I do it if you are using FormsAuthentication:
Controller Action:
public ActionResult CheckLogin()
{
if (Request.Cookies["CookieName"] == null) return Json(0, JsonRequestBehavior.AllowGet);
var cookie = Request.Cookies["CookieName"].Value;
var ticket = FormsAuthentication.Decrypt(cookie);
var secondsRemaining = Math.Round((ticket.Expiration - DateTime.Now).TotalSeconds, 0);
return Json(secondsRemaining, JsonRequestBehavior.AllowGet);
}
Jquery on each page or on layout page:
<script>
$(function () {
setTimeout(doStuff, 1000);
});
function doStuff() {
$.ajax("/CheckLogin").done(function (data) {
if (data <= 60) {
startLogout(data);
} else {
setTimeout(doStuff, 1000);
}
});
}
function startLogout(seconds) {
var countdown = setInterval(function () {
//Show something here
if (count == 0) {
clearInterval(countdown);
//Do something here
}
seconds--;
}, 1000);
}
</script>
If you need to have them automatically logged out, start with Linus Caldwell's suggestion of setting the web.config session timeout. His example shows 30 minutes, so you would just change it to 10. The user won't know that they're logged out, though, until they actually try to request some server resource. To have that happen automatically, you can go a couple of ways. Both of these ways involve automatically refreshing the page after the timeout period has expired. One way is to use a javascript timer. The other is to add a refresh header to each page.
<script type="text/javascript">
var seconds = 60 * 11;// set timer for 11 minutes (1 minutes after session expires)
countdown();
function countdown(){
seconds--;
if (seconds <= 0){
window.location.reload(); // force a refresh.
}else{
setTimeout('countdown()', 1000);
}
}
</script>
The other way would be in your global.asax:
protected void Application_BeginRequest()
{
Response.Headers.Add("Refresh", Convert.ToString(Session.Timeout * 11));
}
If by Idle you mean absence of mouse and keyboard events from user, and i understand your requirement correctly, you should check the jquery-idleTimeout plugin.
Another good one is jQuery idleTimer Plugin
hope this helps.
Related
I have a simple question but not able to find solution to it. I have set session timeout of the application in the web.config as :
<sessionState timeout="30" mode="InProc"/>
and its working fine but now I got the requirement that if the user is idle that is, he is not performing any action on the page for one minute his session should get expired. I tried to do it using form authentication as :
<authentication mode="Forms">
<forms loginUrl="~/Login.aspx" timeout="1" slidingExpiration ="false" defaultUrl="login.aspx"/>
</authentication>
But its now working. Any help would be appreciated.
If I have understood the question correctly (see comments by OP) then the problem is that OP wants both slidingExpiration and absoluteExpiration to be active, but with separate timeouts.
This would enable the system require a user to log back in after a certain time of idling, and to require a user to log back in after a different time even if the user was not idling.
Unfortunately this is not supported out of the box using forms authentication. You have to choose either sliding or absolute expiration. Or you have to build a workaround yourself.
You can use a very simple work around by:
Setting the timeout of the session longer than the corresponding forms authentication timeout, and also longer than the desired absolute timeout:
<sessionState timeout="35" mode="InProc"/>
Set forms authentication to use slidingExpiration = true
Create a user logged in timestamp in the session whenever a user logs in:
Session["userLoggedInAt"] = DateTime.UtcNow;
Add an Application_PostAcquireRequestState method to Global.asax:
void Application_PostAcquireRequestState(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
if (context.Session != null && context.User.Identity.IsAuthenticated)
{
bool forceLogout = false;
if (context.Session["userLoggedInAt"] == null)
forceLogout = true;
else if (!(context.Session["userLoggedInAt"] is DateTime))
forceLogout = true;
else if (DateTime.UtcNow > ((DateTime)context.Session["userLoggedInAt"]).AddMinutes(30))
forceLogout = true;
if (forceLogout)
{
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
}
}
}
Disclaimer: Code above was hacked together quickly, may not be fool proof...
Notes:
Setting sliding expiration to timeout after 1 minute seems excessively paranoid. Even a fast user will not be able to finish any significant work in the application during that time. Even my web bank has a longer idle timeout that that. I would recommend a minimum of 5-10 minutes.
Sliding expiration in forms authentication has an interesting feature: The sliding happens by updating the authentication cookie, moving the expiration date forward when the user is active. But this only happens when at least half the expiration time has passed. If you want to guarantee that a user can be idle for 10 minutes without getting logged out, you must therefore set the timeout to be 20 minutes.
I am a new ASP.NET Webforms developer and I am struggling right now with how to have and manage session timeout in my simple test application. Generally speaking, the test application is listing a number of items. The user can add whatever he wants to a shopping cart and when he clicks on Checkout, he will be asked to enter his information. There is no login or authentication. However, I am using session variables to pass the user information between different pages.
I need to have a timeout here in such a case that the user leaves the page for a long time. In this case, he should get a message and gets redirected to the home page or any other page.
How to do that?
I tried to do that by adding the following to the web.config file:
<sessionState
mode="InProc"
stateConnectionString="tcpip=127.0.0.1:42424"
stateNetworkTimeout="60"
sqlConnectionString="data source=127.0.0.1;Integrated Security=SSPI"
cookieless="false"
timeout="60"
/>
I was trying to retrieve thousands of records from a web service according to the input parameters and naturally it was trowing timeout exceptions. I decided to put the following code in action to display a client-side message, so the user would change the input parameters to make the results more specific.
You can find this example in here.
Executes any code block:
public static bool ExecuteWithTimeLimit(TimeSpan timeSpan, Action codeBlock)
{
try
{
Task task = Task.Factory.StartNew(() => codeBlock());
task.Wait(timeSpan);
return task.IsCompleted;
}
catch (AggregateException ae)
{
throw ae.InnerExceptions[0];
}
}
For a specific amount of time:
bool Completed = ExecuteWithTimeLimit(TimeSpan.FromHours(1), () =>
{
/*your code block*/
});
After stopping the execution, you can redirect to the desired page.
First off I'd like to point you to this page here as it seems that:
You are using InProc mode, though providing state server information and timeout which does not make much sense.
There are different modes in this so most probably you only need
mode, timeout and cookieless items for InProc (which is the best choice for test applications)
Have you made sure that this is under system.web in web.config?
<configuration>
<system.web>
<sessionState mode="InProc"
cookieless="false"
timeout="20"/>
</sessionState>
</system.web>
</configuration>
As an extra, your timeout is 60 minutes. Have you waited long enough? If the session is not set anymore you need to handle the redirect yourself.
Here is another SO page that might help you
By the way, if you are to use the in proc mode you can handle session expiry in global.asx
void Session_End(object sender, EventArgs e)
{
// Code that runs when a session ends.
// Note: The Session_End event is raised only when the sessionstate mode
// is set to InProc in the Web.config file. If session mode is set to StateServer
// or SQLServer, the event is not raised.
Response.Redirect("Add url here");
}
My sessions timeouts are very short on my hosting environment, some times even 2 seconds and they timeout.
The sessions are reset if the user continues to use the website, unless the session = null and the count is 0.
The sessions should time out after 20min and then redirect the user to the log in page
The code for this is below:
protected override void OnInit(EventArgs e)
{
if (this.Session != null && this.Session.Count > 0)
{
string email = (string)this.Session["Email"];
int practiceId = (int)this.Session["PracticeId"];
int practitionerId = (int)this.Session["PractitionerId"];
this.ClientScript.RegisterHiddenField("loggedInUserName", email);
this.ClientScript.RegisterHiddenField("practiceId", practiceId.ToString());
this.ClientScript.RegisterHiddenField("practitionerId", practitionerId.ToString());
}
else
{
this.Session.Abandon();
Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", ""));
Response.Redirect("~/Default.aspx");
}
base.OnInit(e);
}
Does anyone know why my session timeout could be so short?
When using my site sometime i can move around for 2-5 minutes with no timeout and other time 10s in i get a time out. What could cause session being lost, are they any ways to avoid or test for the loss of sessions?
Thanks in advance.
I assume you are overriding the init function for the page, but potentially abandoning the session on every page load could cause more issues than it solves. I would check for the existence of the session inside the master page:
protected void Page_Init(object sender, EventArgs e)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
// user is not logged in
string ReturnUrl = HttpContext.Current.Request.Url.PathAndQuery;
string RedirectUrl = "/Login.aspx";
if (!String.IsNullOrEmpty(ReturnUrl))
{
RedirectUrl += "?ReturnUrl=" + Server.UrlEncode(ReturnUrl);
}
Response.Redirect(RedirectUrl);
}
}
If this is in the master page it will check on each request (made to an aspx page that inherits from the master) that will redirect the user to the login page.
If your app is sharing an application pool, you might be sharing the cookie id with another application:
<authentication mode="Forms">
<forms loginUrl="~/Login.aspx" timeout="60" name="MY_COOOKIE_NAME" slidingExpiration="true" />
</authentication>
<sessionState timeout="60" />
The MY COOKIE NAME will identify the cookies your app uses, other apps might have the default cookie name, so your sessions although apparently authenticated don't belongto the app as they are getting overwritten by a different app. The sliding expiration means that your session time will be extended everytime you visit a page.
Also, check that the machineKey config element is present, this made my sessions more stable:
<machineKey
validationKey="random_validation_key"
decryptionKey="random_decryption_key"
validation="SHA1" decryption="AES" />
I have created a heartbeat mechanism to keep the user's session alive. It will run every 60 seconds. Question is, why not set this to like 15 minutes? Also, why use a heartbeat at all; can't I just set my session expiration time in IIS?
I just want to make it so if a user leaves the page for a half hour or so and goes and gets lunch, that they can come back and their session will still be there when they click submit, so they won't lose any data they might have entered before they left.
$(function () {
// every 60 seconds...
setInterval(KeepSessionAlive, 60000);
});
function KeepSessionAlive() {
$.post("/FACTS/_code/Heartbeat.ashx", null, function () {
//console.log('Session is alive and kicking');
});
}
This can be configured if you are using Session within the .NET framework.
A snippet from http://msdn.microsoft.com/en-us/library/h6bb9cz9%28v=vs.100%29.aspx describes how to set a timeout parameter in your web.config if you are using SessionState
<configuration>
<system.web>
<sessionState
mode="[Off|InProc|StateServer|SQLServer|Custom]"
timeout="number of minutes">
<providers>...</providers>
</sessionState>
</system.web>
</configuration
I don't know what is going on but this is really evident in IE 8. It loads up my page and my javascript files.
I then have debug lines in my server side code that should get activated when an ajax request from my jquery comes through. Or some other contact with the server such a refreshing the page.
So I have this
var timeout;
var wait = 300000;
$(function()
{
timeout = null;
timeout = setTimeout("SessionTimeOut()", wait);
$.get('SessionTimeOut', null, function(response)
{
timeout = clearTimeout(timeout);
wait = parseInt(response);
timeout = setTimeout("SessionTimeOut()", wait);
});
});
This should run every time the page is loaded and start a timer to monitor if they should be logged out or not. I reset the timer every time they make a request to the server.
Some times when a user logs in they all of a sudden get timed out. So I put a default timer of 5 mins in to see if that would fix the problem.
So now say the user times out since they are inactive for like 30mins. They come back and get sent to my login page. So they are going to a new page. I would hope all this javascript stuff would be destroyed and all the timeout objects would be destroyed.
They then try to log in(Using IE8). Guess what they will get the message that they where timed out since IE8 seems to not want to run this script anymore.
I tried refreshing the page but that does nothing. It seems like once the script has ran it has ran for the entire time the browser is opened.
I have many pages with that uses the code above. Yet once it ran once in one page and you go to another page it won't run again.
The only way to get it to run again is close the browser down or clear all your cache.
So how can I make it run again?
Edit
Here is my server code(well portion of it)
public ContentResult SessionTimeOut()
{
var time = signOut.FigureSessionTime();
// minues 2 mins to make up for any lost time.
double total = time.TotalMilliseconds - 120000;
return Content(total.ToString());
}
So basically the FigureSessionTime out looking inside the users cookie and checks to see if the cookie is by end of session or if they choose to be kept logged in for 2 weeks.
If they choose 2 weeks then 2 weeks worth of milliseconds will be returned back. If it is by session then 30mins of milliseconds will be returned back.
I just take 2 mins of to be on the save side so the client side should always timeout before serverside.
return the time in millseconds
// this is what it returns
1670778.4725000001
Edit 2
Ok so this is what I just did to test some things out.
IE 8
Launched a page that a user does not need to log into. This means no cookie to check there session meaning -120000 will come back. This was verified by an alert box. Since this was like an instant timeout the second that page loaded up I was timed out.
Next I tried to log into a page that was secure and that would take the cookie out check it and return a timeout time back. I had an alert box and checked what was stored in the wait variable.
What the alert box came was -120000 so this should not be. It should be some high number. I was instantly logged out and sent to the login page.
I then went into the "safety tab" in IE 8 and choose "Delete browsing history" a popup came up and I checked all the avaiable options and hit"Delete".
I did this all while being on my site and never left it. So I just typed in my login credentials and logged in.
Now the wait variable has this in it "1677207". So this is like 27mins or something like that.
So why did the first time come back negative? Is it because it first timed out on some other page and cached this or did it just not feel like to work?
I now just tired something else. I cleared the browsing history and closed down IE 8. I then launched my website through VS2008 and loaded it up on my login page.
I then logged in and out 5 times. Each time I logged in I noted the wait time.
This is the results
1678237
1678237
1678237
1678237
1678237
So for 5 times the time was exactly the same not even a millisecond off.
Now lets check firefox.
I did the same thing started my site through VS2008 to launch firefox and go to my signin page.
I logged in 5 times in and out. Each time the alert box came up with the wait time in it I noted it.
This is the results
1677176
1677800
1678003
1677956
1677800
Every single time I logged in it was a different time. So for some reason firefox brought back different results each time but IE8 magically could always do it in the same time not even a millisecond more or less?
I after did what I did before in IE8 I went to a page that would time the user out and return -12000. I went to this page and it did just that and returned me to the sign in page.
I then logged in and the wait time that showed up was "1677940". So it actually went and ran my code and got a different time back. Where IE8 just used the previous -12000 over and over again. I could probably log in a million times and it would always -12000. Once it gets a value it seems to keep that.
Edit 3
You all can try at home now.
Here is what you need
// Javascript file
var timeout;
var wait = 300000;
$(function()
{
timeout = null;
timeout = setTimeout("SessionTimeOut()", wait);
$.get('SessionTimeOut', null, function(response)
{
timeout = clearTimeout(timeout);
wait = parseInt(response);
alert(wait);
timeout = setTimeout("SessionTimeOut()", wait);
});
/* starts every time a ajax request is made */
$().ajaxStart(function(e)
{
timeout = clearTimeout(timeout);
$('body').css('cursor', 'progress');
});
$().ajaxStop(function(e)
{
$('body').css('cursor', null);
});
$().ajaxComplete(function(event, XMLHttpRequest, ajaxOptions)
{
if (timeout == null)
{
timeout = setTimeout("SessionTimeOut()", wait);
}
});
});
// need to have dialog ui javascript for this.
function SessionTimeOut()
{
// $('#content').append('<div id="SessionTimeout" title="Session Time Out">Your session has timed out. You must sigin again.</div>');
// $('#SessionTimeout').dialog(
// {
// height: 140,
// resizable: false,
// modal: true,
// closeOnEscape: false,
// buttons:
// {
// 'Return To Sign In Page': function()
// {
// window.location.href = "/account/signin";
// }
// }
// });
// $('#ui-dialog-title-SessionTimeout').siblings('a').remove();
}
// Index View
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Index</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script src="../../JScript1.js" type="text/javascript"></script>
</head>
<body>
<div>
</div>
</body>
</html>
//TestController
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
namespace MvcApplication1.Controllers
{
public class TestController : Controller
{
//
// GET: /Test/
public ActionResult Index()
{
return View();
}
public ContentResult SessionTimeOut()
{
Random random = new Random();
return Content(random.Next(100).ToString());
}
}
}
If you run this code in IE8 you will get the same random number each time. Try it in firefox and you wont.
I had a similar problem recently. Disabling caching for may ajax call cleared it out. Try this guy:
var timeout;
var wait = 300000;
$(function()
{
timeout = null;
timeout = setTimeout("SessionTimeOut()", wait);
$.ajax({
url:'SessionTimeOut',
success:function(response) {
timeout = clearTimeout(timeout);
wait = parseInt(response);
timeout = setTimeout("SessionTimeOut()", wait);
},
type:'get',
cache:false
});
});
notice the cache:false option
Seeing as there is no JQuery and ASP.NET experts speaking up, I'll chip in my €0.02: I doubt this is solely a caching issue. I don't speak JQuery fluently enough to understand what your Ajax call is doing but I have a feeling the problem is there rather than in caching. But I can be wrong there.
Anyway, the easiest way to prevent caching of a Javascript file is to append a random timestamp to the URL:
<script language='script.js?timestamp=1020349302930'>
this will make the script load anew on every instance. If you can use only JS, you can use Math.rand to create the random value and then create or document.write the <script> tags. Or of course, use the server side language of your choice. ( I just saw in your tags that that is ASP.NET)
The cleaner way would be setting the right caching headers either in your server configuraiton, or by sending out headers dynamically inside the JS files (which would have to be parsed by a language like PHP or ASP to do that).
Maybe this gives fresh input in hunting down what's wrong. Good luck!
I agree with Pekka that the problem is more likely not to be in the browser caching.
It looks like you're using the $get call to fetch the timeout value from the server. This seems... odd. If that's not what you're trying to do, then why do you parse the response as the timeout value? If that is what you're trying to do, then are you sure that the response that's returned contains the value that you expect it to contain?