Custom Authenticators

Let’s peek at the Authenticator classes:

In [1]:
from jupyterhub.auth import Authenticator, PAMAuthenticator
In [2]:
Authenticator.authenticate?
Signature: Authenticator.authenticate(self, handler, data)
Docstring:
Authenticate a user with login form data.

This must be a tornado gen.coroutine.
It must return the username on successful authentication,
and return None on failed authentication.

Checking the whitelist is handled separately by the caller.

Args:
    handler (tornado.web.RequestHandler): the current request handler
    data (dict): The formdata of the login form.
                 The default form has 'username' and 'password' fields.
Return:
    str: the username of the authenticated user
    None: Authentication failed
File:      ~/conda/envs/jupyterhub-tutorial/lib/python3.5/site-packages/jupyterhub/auth.py
Type:      function

PAM calls out to a library with the given username and password:

In [3]:
PAMAuthenticator.authenticate??
Signature: PAMAuthenticator.authenticate(self, handler, data)
Source:
    @gen.coroutine
    def authenticate(self, handler, data):
        """Authenticate with PAM, and return the username if login is successful.
    
        Return None otherwise.
        """
        username = data['username']
        try:
            pamela.authenticate(username, data['password'], service=self.service)
        except pamela.PAMError as e:
            if handler is not None:
                self.log.warning("PAM Authentication failed (%s@%s): %s", username, handler.request.remote_ip, e)
            else:
                self.log.warning("PAM Authentication failed: %s", e)
        else:
            return username

File:      ~/conda/envs/jupyterhub-tutorial/lib/python3.5/site-packages/jupyterhub/auth.py
Type:      function

Here’s a super advanced Authenticator that does very secure password verification:

In [4]:
class SecureAuthenticator(Authenticator):
    def authenticate(self, handler, data):
        username = data['username']
        # check password:
        if data['username'] == data['password']:
            return username

Exercise:

Write a custom username+password Authenticator where:

  1. passwords are loaded from a dict
  2. hashed+salted passwords are stored somewhere, but not cleartext passwords
In [5]:
# possibly useful:

from jupyterhub.utils import hash_token, compare_token
hash_token('mypassword')
Out[5]:
'sha512:16384:703fa736d506ca25:d47727cb57a2bd3c0fba618aea1dd71fc053de5d406d43d1ea81676cf386fa5e8a9dd364b59456c4ad65a096d9fab08b51795994376caf642016f43f5b44f4bf'
In [6]:
compare_token(_, 'mypassword')
Out[6]:
True