6

I want to implement a feature (in Angular2) i.e. After login, if a user keeps browser idle for 30 minutes, he should be logged out when coming back after 30 minutes. This has to be done by the front end only.

I am using angular CLI of Angular version 2.4

How can I implement this feature in my Angular2 application?

angularrocks.com
  • 26,767
  • 13
  • 87
  • 104
Er Vipin Sharma
  • 2,519
  • 8
  • 22
  • 32

3 Answers3

11
import { Injectable } from "@angular/core";
import { Router } from '@angular/router'
const MINUTES_UNITL_AUTO_LOGOUT = 60 // in mins
const CHECK_INTERVAL = 15000 // in ms
const STORE_KEY =  'lastAction';
@Injectable()
export class AutoLogoutService {
 public getLastAction() {
    return parseInt(localStorage.getItem(STORE_KEY));
  }
 public setLastAction(lastAction: number) {
    localStorage.setItem(STORE_KEY, lastAction.toString());
  }

  constructor(private router: Router) {
    this.check();
    this.initListener();
    this.initInterval();
    localStorage.setItem(STORE_KEY,Date.now().toString());
  }

  initListener() {
    document.body.addEventListener('click', () => this.reset());
    document.body.addEventListener('mouseover',()=> this.reset());
    document.body.addEventListener('mouseout',() => this.reset());
    document.body.addEventListener('keydown',() => this.reset());
    document.body.addEventListener('keyup',() => this.reset());
    document.body.addEventListener('keypress',() => this.reset());
  }

  reset() {
    this.setLastAction(Date.now());
  }

  initInterval() {
    setInterval(() => {
      this.check();
    }, CHECK_INTERVAL);
  }

  check() {
    const now = Date.now();
    const timeleft = this.getLastAction() + MINUTES_UNITL_AUTO_LOGOUT * 60 * 1000;
    const diff = timeleft - now;
    const isTimeout = diff < 0;

    if (isTimeout)  {
      localStorage.clear();
      this.router.navigate(['./login']);
    }
  }
}
Nabin Kumar Khatiwada
  • 1,546
  • 18
  • 19
  • 1
    I thought the code was useful, thank you. I just added a couple of things to it. I am calling the init routines explicitly as I found they were not called in the constructor at a suitable time. (not quite sure why). I also call a "destroy" method which removes the event listeners too. Finally in the check method I also check to see if they are currently logged in or not. I found otherwise that I was being forwarded to the login page from areas of my app where the user did not need to be logged in. – zeiddev Aug 10 '18 at 15:08
  • Glad it could help.You can make changes according to your requirement. There are several ways to do. Check could be done after initialization as well. – Nabin Kumar Khatiwada Aug 13 '18 at 07:52
  • 1
    can you tell me, how to implement and use this? where should i add it in the module.ts and how does the the webpage react to it and auto logout? do i have to add it somehow in each controller or can i do this globally? thanks – Peter Oct 18 '18 at 08:54
  • Since its a service, provide it in your parent module like app-module. So,it will be available throughout your project. You have to update localstorage value on any page where you want to start the auto logout feature. In my case,it was after login page. – Nabin Kumar Khatiwada Oct 18 '18 at 11:10
  • that seems very useful.....but can you tell me from where should I call this service, from app.component's ngoninit(), OR from somewhere else? – ronit Apr 17 '20 at 19:18
  • It works perfectly in web but not on mobile. Does anyone face the same issue... @NabinKumarKhatiwada do you aware of this mobile. Please let me know if you are – Kartheek Sarabu May 20 '20 at 14:46
  • @Kartheeks It was working fine in mobile device as well when i was developing my project till angular 5. Currently, I am not working with angular projects so cannot help you. Very sorry. – Nabin Kumar Khatiwada May 21 '20 at 10:18
  • @ronit you can call this service from anywhere you need to. You just need to inject in your root module once if you are working in single page app. – Nabin Kumar Khatiwada May 21 '20 at 10:20
7

I needed to do similar thing and created this: https://github.com/harunurhan/idlejs

It is not specifically for angular, but it is written in typescript so you get official typings.

It is simple and configurable without any dependencies. Some examples:

import { Idle } from 'idlejs/dist';

// with predefined events on `document`
const idle = new Idle()
  .whenNotInteractive()
  .within(60)
  .do(() => console.log('IDLE'))
  .start();

You can also use custom event targets and events:

const idle = new Idle()
  .whenNot([{
    events: ['click', 'hover'],
    target: buttonEl,
  },
  {
    events: ['click', 'input'],
    target: inputEl,
  },
  ])
  .within(10)
  .do(() => called = true)
  .start();
harunurhan
  • 1,516
  • 1
  • 17
  • 21
  • I hope you're still maintaining your repo – PhillipJacobs Nov 14 '19 at 17:21
  • I am.. it seems inactive because it was simply working for me without any issue and I didn't need an extra feature... If you have a feature request or found a bug, feel free to create an issue :) – harunurhan Nov 14 '19 at 18:48
3

Basically what you need to do is to set a flag in case of any client activity and than after 30 minutes you have to check for that flag. If flag wasn't set which means user wasn't being active so you can perform a logout() action.

Here is some code sample(using ngrx) that you might find useful.

export class ClientActiveService {
  constructor(
    private store: Store<fromRoot.State>,
  ) { }

  run() {
    window.onload = () => { this.setActive(); };
    window.onmousemove = () => { this.setActive(); };
    window.onmousedown = () => { this.setActive(); }; 
    window.onclick = () => { this.setActive(); };
    window.onscroll = () => { this.setActive(); }; 
    window.onkeypress = () => { this.setActive(); };
  }

  setActive() {
     this.store.select(fromRoot.getClientActive)
     .take(1)
     .subscribe((active) => {
        if (!active) {
          this.store.dispatch(new layout.ClientActiveAction());
        }
      });
  }
}

ClientActiveService is a service that just emmiting an action if client was active. Somewhere like in app.component.ts you have to inject that service and call this.clientActiveService.run();

Then somewhere in your code you have to setup a 30 minutes timer where you subscribe for an ClientInactiveAction action

    setInterval(() => {
      this.store.select(fromRoot.getClientActive)
      .take(1)
      .subscribe((active) => {
        if (!active) {
          this.auth.logout();
        }
      });
    }, 30 * 60 * 1000);

If you are not using ngrx you can just set a variable/flag instead in ClientActiveService service. Then in setTimeout() just check for that variable and perform your logout() action

Otherwise you might wanna use ng2-idle library. In that case Angular 2 - Logout using ng2-idle might help.

angularrocks.com
  • 26,767
  • 13
  • 87
  • 104
  • i am creating ClientActiveService, but its showing an error in the constructor of this class: 'cannot find nameSpace fromRoot' – Er Vipin Sharma Aug 28 '17 at 08:40
  • @ErVipinSharma if you are not using `ngrx` just take out the `private store: Store` and all related code that belongs to it – angularrocks.com Aug 28 '17 at 08:42
  • `import * as fromRoot from '@ngrx/store';` not seams to be right. You should import from your `reducers` something like `import * as fromRoot from '../reducers';` just put the right path. – angularrocks.com Aug 28 '17 at 08:51
  • you are defining your own reducers in your app, check the `ngrx` guide https://gist.github.com/btroncone/a6e4347326749f938510 and https://github.com/ngrx/platform/tree/master/example-app form more details – angularrocks.com Aug 28 '17 at 22:51
  • window onclick and window onscroll did not work for me. document onclick is working better. – Stephen G Tuggy Nov 08 '17 at 23:30