diff --git a/web/config.py b/web/config.py
index f24373e4a..37adfb1e8 100644
--- a/web/config.py
+++ b/web/config.py
@@ -447,6 +447,18 @@ SESSION_EXPIRATION_TIME = 1
 # the session files for cleanup after specified number of *hours*.
 CHECK_SESSION_FILES_INTERVAL = 24
 
+# USER_INACTIVITY_TIMEOUT is interval in Seconds. If the pgAdmin screen is left
+# unattended for <USER_INACTIVITY_TIMEOUT> seconds then the user will
+# be logged out. When set to 0, the timeout will be disabled.
+USER_INACTIVITY_TIMEOUT = 0
+
+# OVERRIDE_USER_INACTIVITY_TIMEOUT when set to True, it will override
+# USER_INACTIVITY_TIMEOUT to prevent pgAdmin from timing out irrespective of
+# user activity.
+# Currently, it is used by query tool to prevent timeout of long running
+# queries.
+OVERRIDE_USER_INACTIVITY_TIMEOUT = True
+
 ##########################################################################
 # SSH Tunneling supports only for Python 2.7 and 3.4+
 ##########################################################################
diff --git a/web/pgadmin/browser/__init__.py b/web/pgadmin/browser/__init__.py
index 5aee341fd..3a54aafe4 100644
--- a/web/pgadmin/browser/__init__.py
+++ b/web/pgadmin/browser/__init__.py
@@ -520,6 +520,11 @@ class BrowserPluginModule(PgAdminModule):
         )
 
 
+def _get_logout_url():
+    return '{0}?next={1}'.format(
+        url_for('security.logout'), url_for('browser.index'))
+
+
 @blueprint.route("/")
 @pgCSRFProtect.exempt
 @login_required
@@ -579,6 +584,7 @@ def index():
         MODULE_NAME + "/index.html",
         username=current_user.email,
         is_admin=current_user.has_role("Administrator"),
+        logout_url=_get_logout_url(),
         _=gettext
     ))
 
@@ -666,7 +672,8 @@ def utils():
             editor_indent_with_tabs=editor_indent_with_tabs,
             app_name=config.APP_NAME,
             pg_libpq_version=pg_libpq_version,
-            support_ssh_tunnel=config.SUPPORT_SSH_TUNNEL
+            support_ssh_tunnel=config.SUPPORT_SSH_TUNNEL,
+            logout_url=_get_logout_url()
         ),
         200, {'Content-Type': 'application/javascript'})
 
diff --git a/web/pgadmin/browser/static/js/activity.js b/web/pgadmin/browser/static/js/activity.js
new file mode 100644
index 000000000..f2fcdbb95
--- /dev/null
+++ b/web/pgadmin/browser/static/js/activity.js
@@ -0,0 +1,114 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import $ from 'jquery';
+import _ from 'underscore';
+
+import pgAdmin from 'sources/pgadmin';
+import pgWindow from 'sources/window';
+import {getEpoch} from 'sources/utils';
+
+const pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {};
+const MIN_ACTIVITY_TIME_UNIT = 1000; /* in seconds */
+/*
+ * User UI activity related functions.
+ */
+_.extend(pgBrowser, {
+  inactivity_timeout_at: null,
+  logging_activity: false,
+  inactivity_timeout_daemon_running: false,
+
+  is_pgadmin_timedout: function() {
+    return !pgWindow.pgAdmin;
+  },
+
+  is_inactivity_timeout: function() {
+    return pgWindow.pgAdmin.Browser.inactivity_timeout_at < this.get_epoch_now();
+  },
+
+  get_epoch_now: function(){
+    return getEpoch();
+  },
+
+  log_activity: function() {
+    if(!this.logging_activity) {
+      this.logging_activity = true;
+      this.inactivity_timeout_at = this.get_epoch_now() + pgAdmin.user_inactivity_timeout;
+
+      /* No need to log events till next MIN_ACTIVITY_TIME_UNIT second as the
+       * value of inactivity_timeout_at won't change
+       */
+      setTimeout(()=>{
+        this.logging_activity = false;
+      }, MIN_ACTIVITY_TIME_UNIT);
+    }
+  },
+
+  /* Call this to register element for acitivity monitoring
+   * Generally, document is passed to cover all.
+   */
+  register_to_activity_listener: function(target, timeout_callback) {
+    let inactivity_events = ['mousemove', 'mousedown', 'keydown'];
+    let self = this;
+    inactivity_events.forEach((event)=>{
+      /* Bind events in the event capture phase, the bubble phase might stop propagation */
+      let eventHandler = function() {
+        if(self.is_pgadmin_timedout()) {
+          /* If the main page has logged out then remove the listener and call the timeout callback */
+          inactivity_events.forEach((event)=>{
+            target.removeEventListener(event, eventHandler, true);
+          });
+          timeout_callback();
+        } else {
+          pgWindow.pgAdmin.Browser.log_activity();
+        }
+      };
+
+      target.addEventListener(event, eventHandler, true);
+    });
+  },
+
+  /*
+   * This function can be used by tools like sqleditor where
+   * poll call is as good as user activity. Decorate such functions
+   * with this to consider them as events. Note that, this is controlled
+   * by override_user_inactivity_timeout.
+   */
+  override_activity_event_decorator: function(input_func) {
+    return function() {
+      /* Log only if override_user_inactivity_timeout true */
+      if(pgAdmin.override_user_inactivity_timeout) {
+        pgWindow.pgAdmin.Browser.log_activity();
+      }
+      return input_func.apply(this, arguments);
+    };
+  },
+
+  logout_inactivity_user: function() {
+    window.location.href = pgBrowser.utils.logout_url;
+  },
+
+  /* The daemon will track and logout when timeout occurs */
+  start_inactivity_timeout_daemon: function() {
+    let self = this;
+    if(pgAdmin.user_inactivity_timeout > 0 && !self.inactivity_timeout_daemon_running) {
+      let timeout_daemon_id = setInterval(()=>{
+        self.inactivity_timeout_daemon_running = true;
+        if(self.is_inactivity_timeout()) {
+          clearInterval(timeout_daemon_id);
+          self.inactivity_timeout_daemon_running = false;
+          $(window).off('beforeunload');
+          self.logout_inactivity_user();
+        }
+      }, MIN_ACTIVITY_TIME_UNIT);
+    }
+  },
+});
+
+export {pgBrowser};
diff --git a/web/pgadmin/browser/static/js/browser.js b/web/pgadmin/browser/static/js/browser.js
index 2ef881b9a..1941ef9a4 100644
--- a/web/pgadmin/browser/static/js/browser.js
+++ b/web/pgadmin/browser/static/js/browser.js
@@ -17,7 +17,7 @@ define('pgadmin.browser', [
   'pgadmin.browser.preferences', 'pgadmin.browser.messages',
   'pgadmin.browser.menu', 'pgadmin.browser.panel', 'pgadmin.browser.layout',
   'pgadmin.browser.error', 'pgadmin.browser.frame',
-  'pgadmin.browser.node', 'pgadmin.browser.collection',
+  'pgadmin.browser.node', 'pgadmin.browser.collection', 'pgadmin.browser.activity',
   'sources/codemirror/addon/fold/pgadmin-sqlfoldcode',
   'pgadmin.browser.keyboard', 'sources/tree/pgadmin_tree_save_state',
 ], function(
@@ -547,6 +547,11 @@ define('pgadmin.browser', [
       obj.Events.on('pgadmin-browser:tree:loadfail', obj.onLoadFailNode, obj);
 
       obj.bind_beforeunload();
+
+      /* User UI activity */
+      obj.log_activity(); /* The starting point */
+      obj.register_to_activity_listener(document);
+      obj.start_inactivity_timeout_daemon();
     },
 
     init_master_password: function() {
diff --git a/web/pgadmin/browser/templates/browser/index.html b/web/pgadmin/browser/templates/browser/index.html
index b2e0a8151..682c23d65 100644
--- a/web/pgadmin/browser/templates/browser/index.html
+++ b/web/pgadmin/browser/templates/browser/index.html
@@ -154,7 +154,7 @@ window.onload = function(e){
                     <li><a class="dropdown-item" href="#" onclick="pgAdmin.Browser.UserManagement.show_users()">{{ _('Users') }}</a></li>
                     <li class="dropdown-divider"></li>
                     {% endif %}
-                    <li><a class="dropdown-item" href="{{ url_for('security.logout') }}?next={{url_for('browser.index')}}">{{ _('Logout') }}</a></li>
+                    <li><a class="dropdown-item" href="{{ logout_url }}">{{ _('Logout') }}</a></li>
                 </ul>
             </li>
         </ul>
diff --git a/web/pgadmin/browser/templates/browser/js/utils.js b/web/pgadmin/browser/templates/browser/js/utils.js
index 79e812033..33dd6809e 100644
--- a/web/pgadmin/browser/templates/browser/js/utils.js
+++ b/web/pgadmin/browser/templates/browser/js/utils.js
@@ -45,6 +45,10 @@ define('pgadmin.browser.utils',
   pgAdmin['csrf_token_header'] = '{{ current_app.config.get('WTF_CSRF_HEADERS')[0] }}';
   pgAdmin['csrf_token'] = '{{ csrf_token() }}';
 
+  /* Get the inactivity related config */
+  pgAdmin['user_inactivity_timeout'] = {{ current_app.config.get('USER_INACTIVITY_TIMEOUT') }};
+  pgAdmin['override_user_inactivity_timeout'] = '{{ current_app.config.get('OVERRIDE_USER_INACTIVITY_TIMEOUT') }}' == 'True';
+
   // Define list of nodes on which Query tool option doesn't appears
   var unsupported_nodes = pgAdmin.unsupported_nodes = [
      'server_group', 'server', 'coll-tablespace', 'tablespace',
@@ -65,6 +69,7 @@ define('pgadmin.browser.utils',
     app_name: '{{ app_name }}',
     pg_libpq_version: {{pg_libpq_version|e}},
     support_ssh_tunnel: '{{ support_ssh_tunnel }}' == 'True',
+    logout_url: '{{logout_url}}',
 
     counter: {total: 0, loaded: 0},
     registerScripts: function (ctx) {
diff --git a/web/pgadmin/tools/debugger/static/js/direct.js b/web/pgadmin/tools/debugger/static/js/direct.js
index 8e00091a5..9f1693501 100644
--- a/web/pgadmin/tools/debugger/static/js/direct.js
+++ b/web/pgadmin/tools/debugger/static/js/direct.js
@@ -1891,6 +1891,14 @@ define([
       pgWindow.default.pgAdmin.Browser.onPreferencesChange('debugger', function() {
         self.reflectPreferences();
       });
+
+      /* Register to log the activity */
+      pgBrowser.register_to_activity_listener(document, ()=>{
+        Alertify.alert(gettext('Timeout'), gettext('Your session has timedout due to inactivity !!'));
+      });
+
+      controller.poll_result = pgBrowser.override_activity_event_decorator(controller.poll_result).bind(controller);
+      controller.poll_end_execution_result = pgBrowser.override_activity_event_decorator(controller.poll_end_execution_result).bind(controller);
     },
     reflectPreferences: function() {
       let self = this,
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index 488f6282b..458533cdf 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -659,6 +659,11 @@ define('tools.querytool', [
           }
         }, 1000);
       }
+
+      /* Register to log the activity */
+      pgBrowser.register_to_activity_listener(document, ()=>{
+        alertify.alert(gettext('Timeout'), gettext('Your session has timedout due to inactivity !!'));
+      });
     },
 
     /* Regarding SlickGrid usage in render_grid function.
@@ -2508,6 +2513,7 @@ define('tools.querytool', [
         }
 
         const executeQuery = new ExecuteQuery.ExecuteQuery(this, pgAdmin.Browser.UserManagement);
+        executeQuery.poll = pgBrowser.override_activity_event_decorator(executeQuery.poll).bind(executeQuery);
         executeQuery.execute(sql, explain_prefix, shouldReconnect);
       },
 
diff --git a/web/regression/javascript/browser/activity_spec.js b/web/regression/javascript/browser/activity_spec.js
new file mode 100644
index 000000000..f971899b3
--- /dev/null
+++ b/web/regression/javascript/browser/activity_spec.js
@@ -0,0 +1,174 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2020, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import {pgBrowser} from 'pgadmin.browser.activity';
+import { getEpoch } from 'sources/utils';
+import pgAdmin from 'sources/pgadmin';
+
+describe('For Activity', function(){
+  beforeEach(function(){
+    pgAdmin.user_inactivity_timeout = 60;
+    pgAdmin.override_user_inactivity_timeout = true;
+
+    /* pgBrowser here is same as main window Browser */
+    window.pgAdmin = {
+      Browser: pgBrowser,
+    };
+  });
+
+  describe('is_pgadmin_timedout', function(){
+    it('when not timedout', function(){
+      expect(pgBrowser.is_pgadmin_timedout()).toEqual(false);
+    });
+
+    it('when timedout', function(){
+      window.pgAdmin = undefined;
+      expect(pgBrowser.is_pgadmin_timedout()).toEqual(true);
+    });
+  });
+
+  describe('is_inactivity_timeout', function(){
+    it('when there is activity', function(){
+      window.pgAdmin.Browser.inactivity_timeout_at = getEpoch() + 30;
+      expect(pgBrowser.is_inactivity_timeout()).toEqual(false);
+    });
+
+    it('when there is no activity', function(){
+      window.pgAdmin.Browser.inactivity_timeout_at = getEpoch() - 30;
+      expect(pgBrowser.is_inactivity_timeout()).toEqual(true);
+    });
+  });
+
+  describe('log_activity', function(){
+    beforeEach(function(){
+      spyOn(pgBrowser, 'get_epoch_now').and.callThrough();
+      spyOn(pgBrowser, 'log_activity').and.callThrough();
+      pgBrowser.logging_activity = false;
+    });
+
+    it('initial log activity', function(){
+      pgBrowser.log_activity();
+      expect(window.pgAdmin.Browser.inactivity_timeout_at).not.toBe(null);
+      expect(pgBrowser.get_epoch_now).toHaveBeenCalled();
+    });
+
+    it('multiple log activity within a second', function(){
+      /* First call */
+      pgBrowser.log_activity();
+      expect(pgBrowser.get_epoch_now).toHaveBeenCalled();
+      expect(pgBrowser.logging_activity).toEqual(true);
+
+      /* Second call */
+      pgBrowser.get_epoch_now.calls.reset();
+      pgBrowser.log_activity();
+      expect(pgBrowser.get_epoch_now).not.toHaveBeenCalled();
+    });
+
+    it('set loggin to false after timeout', function(done){
+      pgBrowser.log_activity();
+      expect(pgBrowser.logging_activity).toEqual(true);
+      setTimeout(()=>{
+        expect(pgBrowser.logging_activity).toEqual(false);
+        done();
+      }, 1001);      
+    });
+  });
+
+  describe('register_to_activity_listener', function(){
+    let target = document;
+    let timeout_callback = jasmine.createSpy();
+    let event = new MouseEvent('mousedown', {
+      bubbles: true,
+      cancelable: true,
+      view: window,
+    });
+    
+    beforeEach(function(){
+      spyOn(pgBrowser, 'log_activity');
+      spyOn(target, 'addEventListener').and.callThrough();
+      spyOn(target, 'removeEventListener').and.callThrough();
+      pgBrowser.register_to_activity_listener(target, timeout_callback);
+    });
+
+    it('function called', function(){
+      expect(target.addEventListener).toHaveBeenCalled();
+    });
+
+    it('event triggered', function(done){
+      target.dispatchEvent(event);
+
+      setTimeout(()=>{
+        expect(pgBrowser.log_activity).toHaveBeenCalled();
+        done();
+      }, 250);
+    });
+
+    it('is timed out', function(done){
+      spyOn(pgBrowser, 'is_pgadmin_timedout').and.returnValue(true);
+      target.dispatchEvent(event);
+
+      setTimeout(()=>{
+        expect(timeout_callback).toHaveBeenCalled();
+        expect(target.removeEventListener).toHaveBeenCalled();
+        done();
+      }, 250);
+    });
+  });
+
+  describe('override_activity_event_decorator', function(){
+    let input_func = jasmine.createSpy('input_func');
+    let decorate_func = pgBrowser.override_activity_event_decorator(input_func);
+    beforeEach(function(){
+      spyOn(pgBrowser, 'log_activity').and.callThrough();
+    });
+
+    it('call the input_func', function(){
+      decorate_func();
+      expect(input_func).toHaveBeenCalled();
+    });
+
+    it('log activity when override_user_inactivity_timeout true', function(){
+      decorate_func();
+      expect(pgBrowser.log_activity).toHaveBeenCalled();
+    });
+
+    it('do not log activity when override_user_inactivity_timeout true', function(){
+      pgAdmin.override_user_inactivity_timeout = false;
+      decorate_func();
+      expect(pgBrowser.log_activity).not.toHaveBeenCalled();
+    });
+  });
+
+  describe('start_inactivity_timeout_daemon', function(){
+    beforeEach(function(){
+      spyOn(pgBrowser, 'logout_inactivity_user');
+    });
+
+    it('start the daemon', function(done){
+      spyOn(pgBrowser, 'is_inactivity_timeout').and.returnValue(false);
+      pgBrowser.inactivity_timeout_daemon_running = false;
+      pgBrowser.start_inactivity_timeout_daemon();
+      setTimeout(()=>{
+        expect(pgBrowser.inactivity_timeout_daemon_running).toEqual(true);
+        done();
+      }, 1001);
+    });
+
+    it('stop the daemon', function(done){
+      spyOn(pgBrowser, 'is_inactivity_timeout').and.returnValue(true);
+      pgBrowser.inactivity_timeout_daemon_running = false;
+      pgBrowser.start_inactivity_timeout_daemon();
+      setTimeout(()=>{
+        expect(pgBrowser.inactivity_timeout_daemon_running).toEqual(false);
+        expect(pgBrowser.logout_inactivity_user).toHaveBeenCalled();
+        done();
+      }, 1001);
+    });
+  });
+});
\ No newline at end of file
diff --git a/web/webpack.shim.js b/web/webpack.shim.js
index 77e86afdf..cf457ff60 100644
--- a/web/webpack.shim.js
+++ b/web/webpack.shim.js
@@ -189,6 +189,7 @@ var webpackShimConfig = {
     'pgadmin.browser.layout': path.join(__dirname, './pgadmin/browser/static/js/layout'),
     'pgadmin.browser.preferences': path.join(__dirname, './pgadmin/browser/static/js/preferences'),
     'pgadmin.browser.menu': path.join(__dirname, './pgadmin/browser/static/js/menu'),
+    'pgadmin.browser.activity': path.join(__dirname, './pgadmin/browser/static/js/activity'),
     'pgadmin.browser.messages': '/browser/js/messages',
     'pgadmin.browser.node': path.join(__dirname, './pgadmin/browser/static/js/node'),
     'pgadmin.browser.node.ui': path.join(__dirname, './pgadmin/browser/static/js/node.ui'),
diff --git a/web/webpack.test.config.js b/web/webpack.test.config.js
index c8a5dc311..70db29125 100644
--- a/web/webpack.test.config.js
+++ b/web/webpack.test.config.js
@@ -16,6 +16,7 @@ const nodeModulesDir = path.resolve(__dirname, 'node_modules');
 const regressionDir = path.resolve(__dirname, 'regression');
 
 module.exports = {
+  mode: 'development',
   devtool: 'inline-source-map',
   plugins: [
     new webpack.ProvidePlugin({
@@ -102,6 +103,7 @@ module.exports = {
       'pgadmin.schema.dir': path.resolve(__dirname, 'pgadmin/browser/server_groups/servers/databases/schemas/static/js'),
       'pgadmin.browser.layout': path.join(__dirname, './pgadmin/browser/static/js/layout'),
       'pgadmin.browser.preferences': path.join(__dirname, './pgadmin/browser/static/js/preferences'),
+      'pgadmin.browser.activity': path.join(__dirname, './pgadmin/browser/static/js/activity'),
       'bundled_codemirror': path.join(__dirname, './pgadmin/static/bundle/codemirror'),
     },
   },
