    async def authenticate(self, handler, data):
        """Authenticate a user with login form data

        This must be a coroutine.

        It must return the username on successful authentication,
        and return None on failed authentication.

        Subclasses can also raise a `web.HTTPError(403, message)`
        in order to halt the authentication process
        and customize the error message that will be shown to the user.
        This error may be raised anywhere in the authentication process
        (`authenticate`, `check_allowed`, `check_blocked_users`).

        Checking allowed_users/blocked_users is handled separately by the caller.

        .. versionchanged:: 0.8
            Allow `authenticate` to return a dict containing auth_state.

        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.
        Returns:
            user (str or dict or None):
                The username of the authenticated user,
                or None if Authentication failed.

                The Authenticator may return a dict instead, which MUST have a
                key `name` holding the username, and MAY have additional keys:

                - `auth_state`, a dictionary of auth state that will be persisted;
                - `admin`, the admin setting value for the user
                - `groups`, the list of group names the user should be a member of,
                  if Authenticator.manage_groups is True.
                  `groups` MUST always be present if manage_groups is enabled.
        Raises:
            web.HTTPError(403):
                Raising errors directly allows customizing the message shown to the user.
        """
Authenticate a user with login form data

This must be a coroutine.

It must return the username on successful authentication,
and return None on failed authentication.

Subclasses can also raise a `web.HTTPError(403, message)`
in order to halt the authentication process
and customize the error message that will be shown to the user.
This error may be raised anywhere in the authentication process
(`authenticate`, `check_allowed`, `check_blocked_users`).

Checking allowed_users/blocked_users is handled separately by the caller.

.. versionchanged:: 0.8
Allow `authenticate` to return a dict containing auth_state.

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.
Returns:
user (str or dict or None):
The username of the authenticated user,
or None if Authentication failed.

The Authenticator may return a dict instead, which MUST have a
key `name` holding the username, and MAY have additional keys:

- `auth_state`, a dictionary of auth state that will be persisted;
- `admin`, the admin setting value for the user
- `groups`, the list of group names the user should be a member of,
if Authenticator.manage_groups is True.
`groups` MUST always be present if manage_groups is enabled.
Raises:
web.HTTPError(403):
Raising errors directly allows customizing the message shown to the user.
jupyterhub/auth.py