Auto shutdown using Angularjs based on user inactivity

Is it possible to determine if a user is inactive and automatically log them out, say, after 10 minutes of inactivity using angularjs?

I tried to avoid using jQuery, but I can not find any tutorials or articles on how to do this in angularjs. Any help would be appreciated.

+69
angularjs
03 Oct '13 at 20:08 on
source share
10 answers

I wrote a module called Ng-Idle , which may be useful to you in this situation. Here is a page containing instructions and a demo .

Basically, it has a service that starts a timer for your downtime, which can be interrupted by user action (events such as clicking, scrolling, text input). You can also manually abort the timeout by calling a method in the service. If the timeout is not interrupted, it counts a warning in which you can warn the user that they will be logged out. If they do not respond after the warning countdown reaches 0, an event will be sent that your application can respond to. In your case, he can issue a request for his session and redirect to the login page.

In addition, it has a keep-alive service that can forward some URL at intervals. This can be used by your application to keep the user session active. A simple service by default integrates with the keep-alive service, pinging ping if it goes into standby mode, and resumes it when it returns.

All the information you need to start is on the site with more details in the wiki . However, a configuration snippet is shown here showing how to sign them when they lose time.

 angular.module('demo', ['ngIdle']) // omitted for brevity .config(function(IdleProvider, KeepaliveProvider) { IdleProvider.idle(10*60); // 10 minutes idle IdleProvider.timeout(30); // after 30 seconds idle, time the user out KeepaliveProvider.interval(5*60); // 5 minute keep-alive ping }) .run(function($rootScope) { $rootScope.$on('IdleTimeout', function() { // end their session and redirect to login }); }); 
+103
Jan 30 '14 at 13:23
source share

There must be different ways to do this, and each approach should be better for the particular application than the other. For most applications, you can simply simply control keyboard or mouse events and enable / disable the exit timer accordingly. However, on top of my head, the β€œbizarre” AngularJS-y solution controls the digest cycle if none of them were running for the last [specified duration] and then logged out. Something like that.

 app.run(function($rootScope) { var lastDigestRun = new Date(); $rootScope.$watch(function detectIdle() { var now = new Date(); if (now - lastDigestRun > 10*60*60) { // logout here, like delete cookie, navigate to login ... } lastDigestRun = now; }); }); 
+17
Oct 03 '13 at 21:24
source share

angularjs demo that uses angularjs and see your browser history

 <!DOCTYPE html> <html ng-app="Application_TimeOut"> <head> <script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.min.js"></script> </head> <body> </body> <script> var app = angular.module('Application_TimeOut', []); app.run(function($rootScope, $timeout, $document) { console.log('starting run'); // Timeout timer value var TimeOutTimerValue = 5000; // Start a timeout var TimeOut_Thread = $timeout(function(){ LogoutByTimer() } , TimeOutTimerValue); var bodyElement = angular.element($document); /// Keyboard Events bodyElement.bind('keydown', function (e) { TimeOut_Resetter(e) }); bodyElement.bind('keyup', function (e) { TimeOut_Resetter(e) }); /// Mouse Events bodyElement.bind('click', function (e) { TimeOut_Resetter(e) }); bodyElement.bind('mousemove', function (e) { TimeOut_Resetter(e) }); bodyElement.bind('DOMMouseScroll', function (e) { TimeOut_Resetter(e) }); bodyElement.bind('mousewheel', function (e) { TimeOut_Resetter(e) }); bodyElement.bind('mousedown', function (e) { TimeOut_Resetter(e) }); /// Touch Events bodyElement.bind('touchstart', function (e) { TimeOut_Resetter(e) }); bodyElement.bind('touchmove', function (e) { TimeOut_Resetter(e) }); /// Common Events bodyElement.bind('scroll', function (e) { TimeOut_Resetter(e) }); bodyElement.bind('focus', function (e) { TimeOut_Resetter(e) }); function LogoutByTimer() { console.log('Logout'); /////////////////////////////////////////////////// /// redirect to another page(eg. Login.html) here /////////////////////////////////////////////////// } function TimeOut_Resetter(e) { console.log('' + e); /// Stop the pending timeout $timeout.cancel(TimeOut_Thread); /// Reset the timeout TimeOut_Thread = $timeout(function(){ LogoutByTimer() } , TimeOutTimerValue); } }) </script> </html> 

Below code is pure javascript version

 <html> <head> <script type="text/javascript"> function logout(){ console.log('Logout'); } function onInactive(millisecond, callback){ var wait = setTimeout(callback, millisecond); document.onmousemove = document.mousedown = document.mouseup = document.onkeydown = document.onkeyup = document.focus = function(){ clearTimeout(wait); wait = setTimeout(callback, millisecond); }; } </script> </head> <body onload="onInactive(5000, logout);"></body> </html> 

UPDATE

I updated my solution as a @Tom suggestion.

 <!DOCTYPE html> <html ng-app="Application_TimeOut"> <head> <script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.min.js"></script> </head> <body> </body> <script> var app = angular.module('Application_TimeOut', []); app.run(function($rootScope, $timeout, $document) { console.log('starting run'); // Timeout timer value var TimeOutTimerValue = 5000; // Start a timeout var TimeOut_Thread = $timeout(function(){ LogoutByTimer() } , TimeOutTimerValue); var bodyElement = angular.element($document); angular.forEach(['keydown', 'keyup', 'click', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mousedown', 'touchstart', 'touchmove', 'scroll', 'focus'], function(EventName) { bodyElement.bind(EventName, function (e) { TimeOut_Resetter(e) }); }); function LogoutByTimer(){ console.log('Logout'); /////////////////////////////////////////////////// /// redirect to another page(eg. Login.html) here /////////////////////////////////////////////////// } function TimeOut_Resetter(e){ console.log(' ' + e); /// Stop the pending timeout $timeout.cancel(TimeOut_Thread); /// Reset the timeout TimeOut_Thread = $timeout(function(){ LogoutByTimer() } , TimeOutTimerValue); } }) </script> </html> 

Click here to see the updated version in Plunker.

+15
Sep 18 '15 at 3:11
source share

It is played with the Boo approach, however, it does not like the fact that the user was launched only once, when another digest is launched, which means that the user remains in the system until he tries to do something on the page, and then immediately let go.

I am trying to force the logout to use an interval that is checked every minute if the last action was more than 30 minutes ago. I connected it on $ routeChangeStart, but could also be connected to $ rootScope. $ Watch, as in the Boo example.

 app.run(function($rootScope, $location, $interval) { var lastDigestRun = Date.now(); var idleCheck = $interval(function() { var now = Date.now(); if (now - lastDigestRun > 30*60*1000) { // logout } }, 60*1000); $rootScope.$on('$routeChangeStart', function(evt) { lastDigestRun = Date.now(); }); }); 
+10
Nov 26 '14 at 20:32
source share

You can also execute angular-activity-monitor more straightforward way than using multiple providers, and uses setInterval() (versus angular $interval ) to avoid manually starting the digest cycle (which is important to prevent inadvertent storage of items).

Ultimately, you simply subscribe to several events that determine when a user is inactive or becomes closed. Therefore, if you want to log out after 10 minutes of inactivity, you can use the following snippet:

 angular.module('myModule', ['ActivityMonitor']); MyController.$inject = ['ActivityMonitor']; function MyController(ActivityMonitor) { // how long (in seconds) until user is considered inactive ActivityMonitor.options.inactive = 600; ActivityMonitor.on('inactive', function() { // user is considered inactive, logout etc. }); ActivityMonitor.on('keepAlive', function() { // items to keep alive in the background while user is active }); ActivityMonitor.on('warning', function() { // alert user when they're nearing inactivity }); } 
+4
Jan 12 '16 at 4:13
source share

I tried the Buu approach and could not understand it due to the large number of events that trigger the digester, including the execution of the interval and $ timeout functions. This leaves the application in a state where it will never be inactive, regardless of user input.

If you really need to keep track of user downtime, I'm not sure there is a good angular approach. I would suggest that the best approach presented by Witoldz is here https://github.com/witoldsz/angular-http-auth . This approach will force the user to re-authenticate when an action is taken that requires their credentials. After the user has authenticated, the previous failed request will be processed and the application will continue to work as if nothing had happened.

This fixes the problem that the user session expires while they are active, because even if their authentication expires, they can still save the application state and not lose their work.

If you have any session on your client (cookies, tokens, etc.), you can also look at them and start the logout process if they expire.

 app.run(['$interval', function($interval) { $interval(function() { if (/* session still exists */) { } else { // log out of client } }, 1000); }]); 

UPDATE: here is a plunger that shows concern. http://plnkr.co/edit/ELotD8W8VAeQfbYFin1W . This demonstrates that the operating time of the digester is updated only when the interval is ticking. As soon as the interval reaches a maximum, then the digester will no longer work.

+2
Jan 17 '14 at 15:44
source share

ng-Idle looks like a way, but I could not understand the modifications of Brian F and wanted a timeout for a sleeping session, too, I also had a pretty simple use case. I took care of this below. It intercepts events until the timeout flag is reset (lazily placed in $ rootScope). It discovers that a timeout occurred when the user returns (and raises an event), but this is good enough for me. I could not get angular $ location to work here, but again, using document.location.href does the job.

I am stuck in my app.js after running .config.

 app.run(function($rootScope,$document) { var d = new Date(); var n = d.getTime(); //n in ms $rootScope.idleEndTime = n+(20*60*1000); //set end time to 20 min from now $document.find('body').on('mousemove keydown DOMMouseScroll mousewheel mousedown touchstart', checkAndResetIdle); //monitor events function checkAndResetIdle() //user did something { var d = new Date(); var n = d.getTime(); //n in ms if (n>$rootScope.idleEndTime) { $document.find('body').off('mousemove keydown DOMMouseScroll mousewheel mousedown touchstart'); //un-monitor events //$location.search('IntendedURL',$location.absUrl()).path('/login'); //terminate by sending to login page document.location.href = 'https://whatever.com/myapp/#/login'; alert('Session ended due to inactivity'); } else { $rootScope.idleEndTime = n+(20*60*1000); //reset end time } } }); 
+2
Apr 05 '14 at 15:57
source share

I think the Buu digest cycle watches are brilliant. Thanks for sharing. As others have noted, $ interval also triggers a digest cycle. For the purpose of automatic user registration, we could use setInterval, which will not cause a digest loop.

 app.run(function($rootScope) { var lastDigestRun = new Date(); setInterval(function () { var now = Date.now(); if (now - lastDigestRun > 10 * 60 * 1000) { //logout } }, 60 * 1000); $rootScope.$watch(function() { lastDigestRun = new Date(); }); }); 
0
Sep 24 '15 at 7:03
source share

I used ng-idle for this and added a small logout and token null code, and it works fine, you can try this. Thanks @HackedByChinese for creating such a nice module.

In IdleTimeout, I just deleted the session data and token.

Here is my code

 $scope.$on('IdleTimeout', function () { closeModals(); delete $window.sessionStorage.token; $state.go("login"); $scope.timedout = $uibModal.open({ templateUrl: 'timedout-dialog.html', windowClass: 'modal-danger' }); }); 
0
Mar 03 '17 at 4:34 on
source share

I would like to expand the answers to those who can use this in a larger project, you can accidentally link several event handlers, and the program will behave strangely.

To get rid of this, I used a single function opened by the factory, from which you would inactivityTimeoutFactory.switchTimeoutOn() and inactivityTimeoutFactory.switchTimeoutOff() in your angular application to activate and deactivate the output accordingly due to inactivity.

This way you will make sure that you use only one instance of event handlers, no matter how many times you try to activate the timeout procedure, which simplifies its use in applications where the user can connect from different routes.

Here is my code:

 'use strict'; angular.module('YOURMODULENAME') .factory('inactivityTimeoutFactory', inactivityTimeoutFactory); inactivityTimeoutFactory.$inject = ['$document', '$timeout', '$state']; function inactivityTimeoutFactory($document, $timeout, $state) { function InactivityTimeout () { // singleton if (InactivityTimeout.prototype._singletonInstance) { return InactivityTimeout.prototype._singletonInstance; } InactivityTimeout.prototype._singletonInstance = this; // Timeout timer value const timeToLogoutMs = 15*1000*60; //15 minutes const timeToWarnMs = 13*1000*60; //13 minutes // variables let warningTimer; let timeoutTimer; let isRunning; function switchOn () { if (!isRunning) { switchEventHandlers("on"); startTimeout(); isRunning = true; } } function switchOff() { switchEventHandlers("off"); cancelTimersAndCloseMessages(); isRunning = false; } function resetTimeout() { cancelTimersAndCloseMessages(); // reset timeout threads startTimeout(); } function cancelTimersAndCloseMessages () { // stop any pending timeout $timeout.cancel(timeoutTimer); $timeout.cancel(warningTimer); // remember to close any messages } function startTimeout () { warningTimer = $timeout(processWarning, timeToWarnMs); timeoutTimer = $timeout(processLogout, timeToLogoutMs); } function processWarning() { // show warning using popup modules, toasters etc... } function processLogout() { // go to logout page. The state might differ from project to project $state.go('authentication.logout'); } function switchEventHandlers(toNewStatus) { const body = angular.element($document); const trackedEventsList = [ 'keydown', 'keyup', 'click', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mousedown', 'touchstart', 'touchmove', 'scroll', 'focus' ]; trackedEventsList.forEach((eventName) => { if (toNewStatus === 'off') { body.off(eventName, resetTimeout); } else if (toNewStatus === 'on') { body.on(eventName, resetTimeout); } }); } // expose switch methods this.switchOff = switchOff; this.switchOn = switchOn; } return { switchTimeoutOn () { (new InactivityTimeout()).switchOn(); }, switchTimeoutOff () { (new InactivityTimeout()).switchOff(); } }; } 
0
Dec 21 '17 at 14:48
source share



All Articles