diff --git a/jupyterhub/app.py b/jupyterhub/app.py index 78b1f4ab..be0ec62f 100644 --- a/jupyterhub/app.py +++ b/jupyterhub/app.py @@ -37,7 +37,7 @@ if V(IPython.__version__) < V('3.0'): from IPython.utils.traitlets import ( Unicode, Integer, Dict, TraitError, List, Bool, Any, - Type, Set, Instance, Bytes, + Type, Set, Instance, Bytes, Float, ) from IPython.config import Application, catch_config_error @@ -185,6 +185,11 @@ class JupyterHub(Application): Useful for daemonizing jupyterhub. """ ) + cookie_max_age_days = Float(14, config=True, + help="""Number of days for a login cookie to be valid. + Default is two weeks. + """ + ) last_activity_interval = Integer(300, config=True, help="Interval (in seconds) at which to update last-activity timestamps." ) @@ -805,6 +810,7 @@ class JupyterHub(Application): spawner_class=self.spawner_class, base_url=self.base_url, cookie_secret=self.cookie_secret, + cookie_max_age_days=self.cookie_max_age_days, login_url=login_url, logout_url=logout_url, static_path=os.path.join(self.data_files_path, 'static'), diff --git a/jupyterhub/handlers/base.py b/jupyterhub/handlers/base.py index 90abac0b..279b3b9d 100644 --- a/jupyterhub/handlers/base.py +++ b/jupyterhub/handlers/base.py @@ -71,6 +71,10 @@ class BaseHandler(RequestHandler): @property def admin_users(self): return self.settings.setdefault('admin_users', set()) + + @property + def cookie_max_age_days(self): + return self.settings.get('cookie_max_age_days', None) def get_current_user_token(self): """get_current_user from Authorization header token""" @@ -87,16 +91,25 @@ class BaseHandler(RequestHandler): def _user_for_cookie(self, cookie_name, cookie_value=None): """Get the User for a given cookie, if there is one""" - cookie_id = self.get_secure_cookie(cookie_name, cookie_value) + cookie_id = self.get_secure_cookie( + cookie_name, + cookie_value, + max_age_days=self.cookie_max_age_days, + ) + def clear(): + self.clear_cookie(cookie_name, path=self.hub.server.base_url) + if cookie_id is None: + if self.get_cookie(cookie_name): + self.log.warn("Invalid or expired cookie token") + clear() return cookie_id = cookie_id.decode('utf8', 'replace') user = self.db.query(orm.User).filter(orm.User.cookie_id==cookie_id).first() if user is None: - # don't log the token itself self.log.warn("Invalid cookie token") # have cookie, but it's not valid. Clear it and start over. - self.clear_cookie(self.hub.server.cookie_name, path=self.hub.server.base_url) + clear() return user def get_current_user_cookie(self):