Autologout

Contact Us or call 1-877-932-8228
Autologout

Autologout

If a user is idle for a certain amount of time, it is often a good idea to force a logout, especially if there might be sensitive data on the screen. This is normally handled on the server side; however, if we want to hide the data on the screen and alert the user that the session has ended, we'll need to handle the session on the client as well. One way to handle this is described below:

  1. When the user logs in, create a JavaScript timer with the window.setTimeout() method. The timer will call a function that ends the session after n minutes.
  2. Whenever there is user activity, the timer must be restarted.
  3. When the user explicitly logs out (e.g, clicks on a logout button), the timer must be killed.

Start the Node.js server by typing npm start from the command line in the directory MoreAjaxApps/Demos/. Open http://localhost:8080/PresidentsLogin.html in a browser to view the working example.

To illustrate, we will be forcing the presidents to login to our inline editing application. The logins are all first initial - lastname (e.g, "gwashington") and the passwords are all the word "password". First, let's look at the application. It starts with a simple login form:Login Form

When the user logs in, the editable table appears. We also provide a Logout button, so the user can log himself out: Logout

When the session times out, the page goes blank and an alert appears informing the user that the session has timed out: Session Timed Out

After the user clicks on the OK button, the login form reappears. Let's look at the code. There are quite a few files involved. The main file is shown below:

Code Sample:

MoreAjaxApps/Demos/PresidentsLogin.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Authentication</title>
<link href="Presidents.css" type="text/css" rel="stylesheet">
<link href="Login.css" type="text/css" rel="stylesheet">
<script type="text/javascript" src="lib.js"></script>
<script type="text/javascript" src="inline-editing.js"></script>
<script type="text/javascript">
	var presConfig = {
		userName: null,
		autoLogoutTimer: null
	}

	function startApp() {
		var loggedInDiv = document.getElementById("LoggedInDiv");
		var logOutDiv = document.getElementById("LogoutDiv");
		loggedInDiv.innerHTML = "Logged in as " + presConfig.userName;
		loggedInDiv.style.display = "block";
		logOutDiv.style.display = "block";
		showPresidents();
		startAutoLogoutTimer();
		observeEvent(document.body, "click", startAutoLogoutTimer);
	}

	function endApp() {
		var loggedInDiv = document.getElementById("LoggedInDiv");
		var logOutDiv = document.getElementById("LogoutDiv");
		clearTimeout(presConfig.autoLogoutTimer);
		presConfig.userName = null;
		loggedInDiv.innerHTML = "";
		loggedInDiv.style.display = "none";
		logOutDiv.style.display = "none";
		loginForm();
		unObserveEvent(document.body, "click", startAutoLogoutTimer);
	}

	function showPresidents() {
		var xmlhttp = new XMLHttpRequest();
		xmlhttp.open("GET", "Controller?req=Table", true);
		xmlhttp.onreadystatechange = function() {
			if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
				results(xmlhttp);
			}
		}
		xmlhttp.send(null);

		function results(xmlhttp) {
			var output = document.getElementById("Output");
			output.innerHTML = xmlhttp.responseText;
			enableEditing();
		}
	}

	function loginForm() {
		var xmlhttp = new XMLHttpRequest();
		xmlhttp.open("GET", "Controller?req=LoginForm", true);
		xmlhttp.onreadystatechange = function() {
			if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
				results(xmlhttp);
			}
		}
		xmlhttp.send(null);

		function results(xmlhttp) {
			var output = document.getElementById("Output");
			var loginForm;
			output.innerHTML = xmlhttp.responseText;
			loginForm = document.getElementById("LoginForm");
			loginForm.onsubmit = function() {
				login(loginForm.Username.value, loginForm.Password.value);
				return false;
			}
		}
	}

	function login(un, pw) {
		var params = "username=" + un + "&password=" + pw;
		var xmlhttp = new XMLHttpRequest();
		xmlhttp.open("POST", "Controller?req=Login", true);
		xmlhttp.onreadystatechange = function() {
			if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
				results(xmlhttp);
			}
		}
		xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
		xmlhttp.send(params);

		function results(xmlhttp) {
			if (xmlhttp.responseText.indexOf("failed") == -1) {
				presConfig.userName = xmlhttp.responseText;
				startApp();
			} else {
				var badLogin = document.getElementById("BadLogin");
				var userName = document.getElementById("Username");
				badLogin.style.display = "block";
				userName.select();
				userName.className = "Highlighted";
				setTimeout(function() {
					document.getElementById('BadLogin').style.display = 'none';
				}, 5000);
			}
		}
	}

	function startAutoLogoutTimer() {
		var sessionTime = 15 * 1000; //15 seconds
		clearTimeout(presConfig.autoLogoutTimer);
		presConfig.autoLogoutTimer = setTimeout(function() {
			logout(true);
		}, sessionTime);
	}


	function logout(auto) {
		var xmlhttp = new XMLHttpRequest();
		document.getElementById("Output").innerHTML = "";
		xmlhttp.open("GET", "Controller?req=Logout", true);
		xmlhttp.onreadystatechange = function() {
			if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
				results(xmlhttp);
			}
		}
		xmlhttp.send(null);

		function results(xmlhttp) {
			endApp();
			if (auto) {
				alert("You have been logged out due to inactivity.");
			}
		}
	}

	function checkLogin() {
		var xmlhttp = new XMLHttpRequest();
		xmlhttp.open("GET", "Controller?req=LoggedIn", true);
		xmlhttp.onreadystatechange = function() {
			if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
				results(xmlhttp);
			}
		}
		xmlhttp.send(null);

		function results(xmlhttp) {
			if (xmlhttp.responseText.indexOf("failed") == -1) {
				presConfig.userName = xmlhttp.responseText;
				startApp();
			} else {
				loginForm();
			}
		}
	}

	observeEvent(window, "load", function() {
		var btnLogout = document.getElementById("logout");
		observeEvent(btnLogout, "click", function() {
			logout(false);
		});
		checkLogin();
	});
</script>
</head>
<body>
<div id="LogoutDiv">
	<button id="logout">Logout</button>
</div>
<div id="LoggedInDiv"></div>
<div id="Output">One moment please...</div>
</body>
</html>

This page contains the basic HTML elements that will hold data returned from the server:

<div id="LogoutDiv"> <button id="logout">Logout</button> </div> <div id="LoggedInDiv"></div> <div id="Output">One moment please...</div>

The "Output" div will hold the login form or the table data, depending on whether or not the user is logged in. This page also controls the flow of the application:

When the page loads, the checkLogin() function is called, which looks like this:

function checkLogin() { var xmlhttp = new XMLHttpRequest(); xmlhttp.open("GET","Controller?req=LoggedIn",true); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState==4 && xmlhttp.status==200) { results(xmlhttp); } } xmlhttp.send(null); function results(xmlhttp) { if (xmlhttp.responseText.indexOf("failed") == -1) { presConfig.userName = xmlhttp.responseText; startApp(); } else { loginForm(); } } }

It makes an Ajax request to Controller, which handles the assignment of tasks based on the req parameter. In this case, the server-side code checks to see if the user is logged in: if so, it returns the user's name; if not, it returns the text "failed" if s/he is not logged in. The results() callback function then calls startApp() (if the user is logged in) to start the application or loginForm() (if the user is NOT logged in) to show the login form. Let's first see what happens when the user is NOT logged in:

function loginForm() { var xmlhttp = new XMLHttpRequest(); xmlhttp.open("GET","Controller?req=LoginForm",true); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState==4 && xmlhttp.status==200) { results(xmlhttp); } } xmlhttp.send(null); function results(xmlhttp) { var output = document.getElementById("Output"); var loginForm; output.innerHTML = xmlhttp.responseText; loginForm = document.getElementById("LoginForm"); observeEvent(loginForm,"submit",function() { login(loginForm.Username.value,loginForm.Password.value); return false; }); } }

The loginForm() function uses Ajax to get an HTML login form and then, in the results() callback function displays the form and attaches the login() function to its submit event.

When the user logs in, the login() function is called:

function login(un,pw) { var params="username=" + un + "&password=" + pw; var xmlhttp = new XMLHttpRequest(); xmlhttp.open("POST","Controller?req=Login",true); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState==4 && xmlhttp.status==200) { results(xmlhttp); } } xmlhttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=UTF-8"); xmlhttp.send(params); function results(xmlhttp) { if (xmlhttp.responseText.indexOf("failed") == -1) { presConfig.userName = xmlhttp.responseText; startApp(); } else { var badLogin = document.getElementById("BadLogin"); var userName = document.getElementById("Username"); badLogin.style.display="block"; userName.select(); userName.className="Highlighted"; setTimeout(function() {document.getElementById('BadLogin').style.display='none'; },5000); } } }

The login() function sends the passed-in username and password to the Controller response route using Ajax to try to log the user in. If the login succeeds, the server returns the user's full name (e.g, George Washington). If the login fails, the server returns "failed". The results() callback function either starts the application or displays a bad-login message and leaves the form in place for the user to try again.

When the user does successfully log in, the startApp() function is called:

function startApp() { var loggedInDiv = document.getElementById("LoggedInDiv"); var logOutDiv = document.getElementById("LogoutDiv"); loggedInDiv.innerHTML = "Logged in as " + presConfig.userName; loggedInDiv.style.display="block"; logOutDiv.style.display="block"; showPresidents(); startAutoLogoutTimer(); observeEvent(document.body,"click",startAutoLogoutTimer); }

The startApp() function does the following:

  1. Shows the logged-in message and the Logout button.
  2. Calls showPresidents(), which gets the editable Presidents table we saw in earlier demos.
  3. Calls startAutoLogoutTimer(), which starts the inactivity timer. This function is recalled every time the user clicks the mouse. It is shown below:
function startAutoLogoutTimer() { var sessionTime = 15 * 1000; //15 seconds clearTimeout(presConfig.autoLogoutTimer); presConfig.autoLogoutTimer = setTimeout(function() { logout(true); }, sessionTime); }

The startAutoLogoutTimer() function simply sets a timer to call logout() after 15 seconds and passing in true to indicate that it is an auto logout vs. a user-initiated logout. The function is recalled (and the timer restarted) each time the user clicks the mouse. We set the timer at 15 seconds so that you wouldn't have to wait too long to see it work. Normally, it would be much longer. The logout() function is shown below:

function logout(auto) { var xmlhttp = new XMLHttpRequest(); var loggedInDiv = document.getElementById("LoggedInDiv"); var logOutDiv = document.getElementById("LogoutDiv"); var output = document.getElementById("Output"); xmlhttp.open("HEAD","Controller?req=Logout",true); xmlhttp.send(null); clearTimeout(presConfig.autoLogoutTimer); output.innerHTML = ""; loggedInDiv.innerHTML = ""; loggedInDiv.style.display="none"; logOutDiv.style.display="none"; loginForm(); unObserveEvent(document.body,"click",startAutoLogoutTimer); if (auto) { alert("You have been logged out due to inactivity."); } }

As we don't need a response from the server to log the user out, we use the "HEAD" method to send the request. The rest of the function sets the page back as it was when it first loaded.

Next