Authenticators#

Module: jupyterhub.auth#

Base Authenticator class and the default PAM Authenticator

Authenticator#

class jupyterhub.auth.Authenticator(**kwargs: Any)#

Base class for implementing an authentication provider for JupyterHub

add_user(user)#

Hook called when a user is added to JupyterHub

This is called:
  • When a user first authenticates, _after_ all allow and block checks have passed

  • When the hub restarts, for all users in the database (i.e. users previously allowed)

  • When a user is added to the database, either via configuration or REST API

This method may be a coroutine.

By default, this adds the user to the allowed_users set if allow_existing_users is true.

Subclasses may do more extensive things, such as creating actual system users, but they should call super to ensure the allowed_users set is updated.

Note that this should be idempotent, since it is called whenever the hub restarts for all users.

Changed in version 5.0: Now adds users to the allowed_users set if allow_all is False and allow_existing_users is True, instead of if allowed_users is not empty.

Parameters:

user (User) – The User wrapper object

admin_users c.Authenticator.admin_users = Set()#

Set of users that will have admin rights on this JupyterHub.

Note: As of JupyterHub 2.0, full admin rights should not be required, and more precise permissions can be managed via roles.

Admin users have extra privileges:
  • Use the admin panel to see list of users logged in

  • Add / remove users in some authenticators

  • Restart / halt the hub

  • Start / stop users’ single-user servers

  • Can access each individual users’ single-user server (if configured)

Admin access should be treated the same way root access is.

Defaults to an empty set, in which case no user has admin access.

allow_all c.Authenticator.allow_all = Bool(False)#

Allow every user who can successfully authenticate to access JupyterHub.

False by default, which means for most Authenticators, _some_ allow-related configuration is required to allow users to log in.

Authenticator subclasses may override the default with e.g.:

@default("allow_all")
def _default_allow_all(self):
    # if _any_ auth config (depends on the Authenticator)
    if self.allowed_users or self.allowed_groups or self.allow_existing_users:
        return False
    else:
        return True

Added in version 5.0.

Changed in version 5.0: Prior to 5.0, allow_all wasn’t defined on its own, and was instead implicitly True when no allow config was provided, i.e. allowed_users unspecified or empty on the base Authenticator class.

To preserve pre-5.0 behavior, set allow_all = True if you have no other allow configuration.

allow_existing_users c.Authenticator.allow_existing_users = Bool(False)#

Allow existing users to login.

Defaults to True if allowed_users is set for historical reasons, and False otherwise.

With this enabled, all users present in the JupyterHub database are allowed to login. This has the effect of any user who has _previously_ been allowed to login via any means will continue to be allowed until the user is deleted via the /hub/admin page or REST API.

Warning

Before enabling this you should review the existing users in the JupyterHub admin panel at /hub/admin. You may find users existing there because they have previously been declared in config such as allowed_users or allowed to sign in.

Warning

When this is enabled and you wish to remove access for one or more users previously allowed, you must make sure that they are removed from the jupyterhub database. This can be tricky to do if you stop allowing an externally managed group of users for example.

With this enabled, JupyterHub admin users can visit /hub/admin or use JupyterHub’s REST API to add and remove users to manage who can login.

Added in version 5.0.

allowed_users c.Authenticator.allowed_users = Set()#

Set of usernames that are allowed to log in.

Use this to limit which authenticated users may login. Default behavior: only users in this set are allowed.

If empty, does not perform any restriction, in which case any authenticated user is allowed.

Authenticators may extend Authenticator.check_allowed() to combine allowed_users with other configuration to either expand or restrict access.

Changed in version 1.2: Authenticator.whitelist renamed to allowed_users

any_allow_config c.Authenticator.any_allow_config = Bool(False)#

Is there any allow config?

Used to show a warning if it looks like nobody can access the Hub, which can happen when upgrading to JupyterHub 5, now that allow_all defaults to False.

Deployments can set this explicitly to True to suppress the “No allow config found” warning.

Will be True if any config tagged with .tag(allow_config=True) or starts with allow is truthy.

Added in version 5.0.

auth_refresh_age c.Authenticator.auth_refresh_age = Int(300)#

The max age (in seconds) of authentication info before forcing a refresh of user auth info.

Refreshing auth info allows, e.g. requesting/re-validating auth tokens.

See refresh_user() for what happens when user auth info is refreshed (nothing by default).

async authenticate(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.

Changed in version 0.8: Allow authenticate to return a dict containing auth_state.

Parameters:
  • 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:

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.

Return type:

user (str or dict or None)

Raises:

web.HTTPError(403) – Raising errors directly allows customizing the message shown to the user.

auto_login c.Authenticator.auto_login = Bool(False)#

Automatically begin the login process

rather than starting with a “Login with…” link at /hub/login

To work, .login_url() must give a URL other than the default /hub/login, such as an oauth handler or another automatic login handler, registered with .get_handlers().

Added in version 0.8.

auto_login_oauth2_authorize c.Authenticator.auto_login_oauth2_authorize = Bool(False)#

Automatically begin login process for OAuth2 authorization requests

When another application is using JupyterHub as OAuth2 provider, it sends users to /hub/api/oauth2/authorize. If the user isn’t logged in already, and auto_login is not set, the user will be dumped on the hub’s home page, without any context on what to do next.

Setting this to true will automatically redirect users to login if they aren’t logged in only on the /hub/api/oauth2/authorize endpoint.

Added in version 1.5.

blocked_users c.Authenticator.blocked_users = Set()#

Set of usernames that are not allowed to log in.

Use this with supported authenticators to restrict which users can not log in. This is an additional block list that further restricts users, beyond whatever restrictions the authenticator has in place.

If empty, does not perform any additional restriction.

Changed in version 1.2: Authenticator.blacklist renamed to blocked_users

check_allow_config()#

Log a warning if no allow config can be found.

Could get a false positive if _only_ unrecognized allow config is used. Authenticators can apply .tag(allow_config=True) to label this config to make sure it is found.

Subclasses can override to perform additonal checks and warn about likely authenticator configuration problems.

Added in version 5.0.

check_allowed(username, authentication=None)#

Check if a username is allowed to authenticate based on configuration

Return True if username is allowed, False otherwise.

No allowed_users set means any username is allowed.

Names are normalized before being checked against the allowed set.

Changed in version 1.0: Signature updated to accept authentication data and any future changes

Changed in version 1.2: Renamed check_whitelist to check_allowed

Parameters:
  • username (str) – The normalized username

  • authentication (dict) – The authentication model, as returned by .authenticate().

Returns:

Whether the user is allowed

Return type:

allowed (bool)

Raises:

web.HTTPError(403) – Raising HTTPErrors directly allows customizing the message shown to the user.

check_blocked_users(username, authentication=None)#

Check if a username is blocked to authenticate based on Authenticator.blocked_users configuration

Return True if username is allowed, False otherwise. No block list means any username is allowed.

Names are normalized before being checked against the block list.

Changed in version 1.0: Signature updated to accept authentication data as second argument

Changed in version 1.2: Renamed check_blacklist to check_blocked_users

Parameters:
  • username (str) – The normalized username

  • authentication (dict) – The authentication model, as returned by .authenticate().

Returns:

Whether the user is allowed

Return type:

allowed (bool)

Raises:

web.HTTPError(403, message) – Raising HTTPErrors directly allows customizing the message shown to the user.

custom_html Unicode('')#

HTML form to be overridden by authenticators if they want a custom authentication form.

Defaults to an empty string, which shows the default username/password form.

delete_invalid_users c.Authenticator.delete_invalid_users = Bool(False)#

Delete any users from the database that do not pass validation

When JupyterHub starts, .add_user will be called on each user in the database to verify that all users are still valid.

If delete_invalid_users is True, any users that do not pass validation will be deleted from the database. Use this if users might be deleted from an external system, such as local user accounts.

If False (default), invalid users remain in the Hub’s database and a warning will be issued. This is the default to avoid data loss due to config changes.

delete_user(user)#

Hook called when a user is deleted

Removes the user from the allowed_users set. Subclasses should call super to ensure the allowed_users set is updated.

Parameters:

user (User) – The User wrapper object

enable_auth_state c.Authenticator.enable_auth_state = Bool(False)#

Enable persisting auth_state (if available).

auth_state will be encrypted and stored in the Hub’s database. This can include things like authentication tokens, etc. to be passed to Spawners as environment variables.

Encrypting auth_state requires the cryptography package.

Additionally, the JUPYTERHUB_CRYPT_KEY environment variable must contain one (or more, separated by ;) 32B encryption keys. These can be either base64 or hex-encoded.

If encryption is unavailable, auth_state cannot be persisted.

New in JupyterHub 0.8

async get_authenticated_user(handler, data)#

Authenticate the user who is attempting to log in

Returns user dict if successful, None otherwise.

This calls authenticate, which should be overridden in subclasses, normalizes the username if any normalization should be done, and then validates the name in the allowed set.

This is the outer API for authenticating a user. Subclasses should not override this method.

The various stages can be overridden separately:
  • authenticate turns formdata into a username

  • normalize_username normalizes the username

  • check_blocked_users check against the blocked usernames

  • allow_all is checked

  • check_allowed checks against the allowed usernames

  • is_admin check if a user is an admin

Changed in version 0.8: return dict instead of username

get_custom_html(base_url)#

Get custom HTML for the authenticator.

get_handlers(app)#

Return any custom handlers the authenticator needs to register

Used in conjugation with login_url and logout_url.

Parameters:

app (JupyterHub Application) – the application object, in case it needs to be accessed for info.

Returns:

list of ('/url', Handler) tuples passed to tornado. The Hub prefix is added to any URLs.

Return type:

handlers (list)

is_admin(handler, authentication)#

Authentication helper to determine a user’s admin status.

Parameters:
  • handler (tornado.web.RequestHandler) – the current request handler

  • authentication – The authentication dict generated by authenticate.

Returns:

The admin status of the user, or None if it could not be determined or should not change.

Return type:

admin_status (Bool or None)

async load_managed_roles()#

Load roles managed by authenticator.

Returns a list of predefined role dictionaries to load at startup, following the same format as JupyterHub.load_roles.

Added in version 5.0.

login_service Unicode('')#

Name of the login service that this authenticator is providing using to authenticate users.

Example: GitHub, MediaWiki, Google, etc.

Setting this value replaces the login form with a “Login with <login_service>” button.

Any authenticator that redirects to an external service (e.g. using OAuth) should set this.

login_url(base_url)#

Override this when registering a custom login handler

Generally used by authenticators that do not use simple form-based authentication.

The subclass overriding this is responsible for making sure there is a handler available to handle the URL returned from this method, using the get_handlers method.

Parameters:

base_url (str) – the base URL of the Hub (e.g. /hub/)

Returns:

The login URL, e.g. ‘/hub/login’

Return type:

str

logout_url(base_url)#

Override when registering a custom logout handler

The subclass overriding this is responsible for making sure there is a handler available to handle the URL returned from this method, using the get_handlers method.

Parameters:

base_url (str) – the base URL of the Hub (e.g. /hub/)

Returns:

The logout URL, e.g. ‘/hub/logout’

Return type:

str

manage_groups c.Authenticator.manage_groups = Bool(False)#

Let authenticator manage user groups

If True, Authenticator.authenticate and/or .refresh_user may return a list of group names in the ‘groups’ field, which will be assigned to the user.

All group-assignment APIs are disabled if this is True.

manage_roles c.Authenticator.manage_roles = Bool(False)#

Let authenticator manage roles

If True, Authenticator.authenticate and/or .refresh_user may return a list of roles in the ‘roles’ field, which will be added to the database.

When enabled, all role management will be handled by the authenticator; in particular, assignment of roles via JupyterHub.load_roles traitlet will not be possible.

Added in version 5.0.

normalize_username(username)#

Normalize the given username and return it

Override in subclasses if usernames need different normalization rules.

The default attempts to lowercase the username and apply username_map if it is set.

otp_prompt c.Authenticator.otp_prompt = Any('OTP:')#

The prompt string for the extra OTP (One Time Password) field.

Added in version 5.0.

post_auth_hook c.Authenticator.post_auth_hook = Any(None)#

An optional hook function that you can implement to do some bootstrapping work during authentication. For example, loading user account details from an external system.

This function is called after the user has passed all authentication checks and is ready to successfully authenticate. This function must return the auth_model dict reguardless of changes to it. The hook is called with 3 positional arguments: (authenticator, handler, auth_model).

This may be a coroutine.

Example:

import os
import pwd
def my_hook(authenticator, handler, auth_model):
    user_data = pwd.getpwnam(auth_model['name'])
    spawn_data = {
        'pw_data': user_data
        'gid_list': os.getgrouplist(auth_model['name'], user_data.pw_gid)
    }

    if auth_model['auth_state'] is None:
        auth_model['auth_state'] = {}
    auth_model['auth_state']['spawn_data'] = spawn_data

    return auth_model

c.Authenticator.post_auth_hook = my_hook
post_spawn_stop(user, spawner)#

Hook called after stopping a user container

Can be used to do auth-related cleanup, e.g. closing PAM sessions.

pre_spawn_start(user, spawner)#

Hook called before spawning a user’s server

Can be used to do auth-related startup, e.g. opening PAM sessions.

refresh_pre_spawn c.Authenticator.refresh_pre_spawn = Bool(False)#

Force refresh of auth prior to spawn.

This forces refresh_user() to be called prior to launching a server, to ensure that auth state is up-to-date.

This can be important when e.g. auth tokens that may have expired are passed to the spawner via environment variables from auth_state.

If refresh_user cannot refresh the user auth data, launch will fail until the user logs in again.

async refresh_user(user, handler=None)#

Refresh auth data for a given user

Allows refreshing or invalidating auth data.

Only override if your authenticator needs to refresh its data about users once in a while.

Parameters:
Returns:

Return True if auth data for the user is up-to-date and no updates are required.

Return False if the user’s auth data has expired, and they should be required to login again.

Return a dict of auth data if some values should be updated. This dict should have the same structure as that returned by authenticate() when it returns a dict. Any fields present will refresh the value for the user. Any fields not present will be left unchanged. This can include updating .admin or .auth_state fields.

Return type:

auth_data (bool or dict)

request_otp c.Authenticator.request_otp = Bool(False)#

Prompt for OTP (One Time Password) in the login form.

Added in version 5.0.

reset_managed_roles_on_startup c.Authenticator.reset_managed_roles_on_startup = Bool(False)#

Reset managed roles to result of load_managed_roles() on startup.

If True:
  • stale managed roles will be removed,

  • stale assignments to managed roles will be removed.

Any role not present in load_managed_roles() will be considered ‘stale’.

The ‘stale’ status for role assignments is also determined from load_managed_roles() result:

  • user role assignments status will depend on whether the users key is defined or not:

    • if a list is defined under the users key and the user is not listed, then the user role assignment will be considered ‘stale’,

    • if the users key is not provided, the user role assignment will be preserved;

  • service and group role assignments will be considered ‘stale’:

    • if not included in the services and groups list,

    • if the services and groups keys are not provided.

Added in version 5.0.

async run_post_auth_hook(handler, auth_model)#

Run the post_auth_hook if defined

Parameters:
  • handler (tornado.web.RequestHandler) – the current request handler

  • auth_model (dict) – User authentication data dictionary. Contains the username (‘name’), admin status (‘admin’), and auth state dictionary (‘auth_state’).

Returns:

The hook must always return the auth_model dict

Return type:

auth_model (dict)

username_map c.Authenticator.username_map = Dict()#

Dictionary mapping authenticator usernames to JupyterHub users.

Primarily used to normalize OAuth user names to local users.

username_pattern c.Authenticator.username_pattern = Unicode('')#

Regular expression pattern that all valid usernames must match.

If a username does not match the pattern specified here, authentication will not be attempted.

If not set, allow any username.

username_regex Any(None)#

Compiled regex kept in sync with username_pattern

validate_username(username)#

Validate a normalized username

Return True if username is valid, False otherwise.

whitelist c.Authenticator.whitelist = Set()#

Deprecated, use Authenticator.allowed_users

LocalAuthenticator#

class jupyterhub.auth.LocalAuthenticator(**kwargs: Any)#

Base class for Authenticators that work with local Linux/UNIX users

Checks for local users, and can attempt to create them if they exist.

add_system_user(user)#

Create a new local UNIX user on the system.

Tested to work on FreeBSD and Linux, at least.

async add_user(user)#

Hook called whenever a new user is added

If self.create_system_users, the user will attempt to be created if it doesn’t exist.

add_user_cmd c.LocalAuthenticator.add_user_cmd = Command()#

The command to use for creating users as a list of strings

For each element in the list, the string USERNAME will be replaced with the user’s username. The username will also be appended as the final argument.

For Linux, the default value is:

[‘adduser’, ‘-q’, ‘–gecos’, ‘””’, ‘–disabled-password’]

To specify a custom home directory, set this to:

[‘adduser’, ‘-q’, ‘–gecos’, ‘””’, ‘–home’, ‘/customhome/USERNAME’, ‘–disabled-password’]

This will run the command:

adduser -q –gecos “” –home /customhome/river –disabled-password river

when the user ‘river’ is created.

admin_users c.LocalAuthenticator.admin_users = Set()#

Set of users that will have admin rights on this JupyterHub.

Note: As of JupyterHub 2.0, full admin rights should not be required, and more precise permissions can be managed via roles.

Admin users have extra privileges:
  • Use the admin panel to see list of users logged in

  • Add / remove users in some authenticators

  • Restart / halt the hub

  • Start / stop users’ single-user servers

  • Can access each individual users’ single-user server (if configured)

Admin access should be treated the same way root access is.

Defaults to an empty set, in which case no user has admin access.

allow_all c.LocalAuthenticator.allow_all = Bool(False)#

Allow every user who can successfully authenticate to access JupyterHub.

False by default, which means for most Authenticators, _some_ allow-related configuration is required to allow users to log in.

Authenticator subclasses may override the default with e.g.:

@default("allow_all")
def _default_allow_all(self):
    # if _any_ auth config (depends on the Authenticator)
    if self.allowed_users or self.allowed_groups or self.allow_existing_users:
        return False
    else:
        return True

Added in version 5.0.

Changed in version 5.0: Prior to 5.0, allow_all wasn’t defined on its own, and was instead implicitly True when no allow config was provided, i.e. allowed_users unspecified or empty on the base Authenticator class.

To preserve pre-5.0 behavior, set allow_all = True if you have no other allow configuration.

allow_existing_users c.LocalAuthenticator.allow_existing_users = Bool(False)#

Allow existing users to login.

Defaults to True if allowed_users is set for historical reasons, and False otherwise.

With this enabled, all users present in the JupyterHub database are allowed to login. This has the effect of any user who has _previously_ been allowed to login via any means will continue to be allowed until the user is deleted via the /hub/admin page or REST API.

Warning

Before enabling this you should review the existing users in the JupyterHub admin panel at /hub/admin. You may find users existing there because they have previously been declared in config such as allowed_users or allowed to sign in.

Warning

When this is enabled and you wish to remove access for one or more users previously allowed, you must make sure that they are removed from the jupyterhub database. This can be tricky to do if you stop allowing an externally managed group of users for example.

With this enabled, JupyterHub admin users can visit /hub/admin or use JupyterHub’s REST API to add and remove users to manage who can login.

Added in version 5.0.

allowed_groups c.LocalAuthenticator.allowed_groups = Set()#

Allow login from all users in these UNIX groups.

Changed in version 5.0: allowed_groups may be specified together with allowed_users, to grant access by group OR name.

allowed_users c.LocalAuthenticator.allowed_users = Set()#

Set of usernames that are allowed to log in.

Use this to limit which authenticated users may login. Default behavior: only users in this set are allowed.

If empty, does not perform any restriction, in which case any authenticated user is allowed.

Authenticators may extend Authenticator.check_allowed() to combine allowed_users with other configuration to either expand or restrict access.

Changed in version 1.2: Authenticator.whitelist renamed to allowed_users

any_allow_config c.LocalAuthenticator.any_allow_config = Bool(False)#

Is there any allow config?

Used to show a warning if it looks like nobody can access the Hub, which can happen when upgrading to JupyterHub 5, now that allow_all defaults to False.

Deployments can set this explicitly to True to suppress the “No allow config found” warning.

Will be True if any config tagged with .tag(allow_config=True) or starts with allow is truthy.

Added in version 5.0.

auth_refresh_age c.LocalAuthenticator.auth_refresh_age = Int(300)#

The max age (in seconds) of authentication info before forcing a refresh of user auth info.

Refreshing auth info allows, e.g. requesting/re-validating auth tokens.

See refresh_user() for what happens when user auth info is refreshed (nothing by default).

auto_login c.LocalAuthenticator.auto_login = Bool(False)#

Automatically begin the login process

rather than starting with a “Login with…” link at /hub/login

To work, .login_url() must give a URL other than the default /hub/login, such as an oauth handler or another automatic login handler, registered with .get_handlers().

Added in version 0.8.

auto_login_oauth2_authorize c.LocalAuthenticator.auto_login_oauth2_authorize = Bool(False)#

Automatically begin login process for OAuth2 authorization requests

When another application is using JupyterHub as OAuth2 provider, it sends users to /hub/api/oauth2/authorize. If the user isn’t logged in already, and auto_login is not set, the user will be dumped on the hub’s home page, without any context on what to do next.

Setting this to true will automatically redirect users to login if they aren’t logged in only on the /hub/api/oauth2/authorize endpoint.

Added in version 1.5.

blocked_users c.LocalAuthenticator.blocked_users = Set()#

Set of usernames that are not allowed to log in.

Use this with supported authenticators to restrict which users can not log in. This is an additional block list that further restricts users, beyond whatever restrictions the authenticator has in place.

If empty, does not perform any additional restriction.

Changed in version 1.2: Authenticator.blacklist renamed to blocked_users

check_allowed(username, authentication=None)#

Check if a username is allowed to authenticate based on configuration

Return True if username is allowed, False otherwise.

No allowed_users set means any username is allowed.

Names are normalized before being checked against the allowed set.

Changed in version 1.0: Signature updated to accept authentication data and any future changes

Changed in version 1.2: Renamed check_whitelist to check_allowed

Parameters:
  • username (str) – The normalized username

  • authentication (dict) – The authentication model, as returned by .authenticate().

Returns:

Whether the user is allowed

Return type:

allowed (bool)

Raises:

web.HTTPError(403) – Raising HTTPErrors directly allows customizing the message shown to the user.

check_allowed_groups(username, authentication=None)#

If allowed_groups is configured, check if authenticating user is part of group.

create_system_users c.LocalAuthenticator.create_system_users = Bool(False)#

If set to True, will attempt to create local system users if they do not exist already.

Supports Linux and BSD variants only.

delete_invalid_users c.LocalAuthenticator.delete_invalid_users = Bool(False)#

Delete any users from the database that do not pass validation

When JupyterHub starts, .add_user will be called on each user in the database to verify that all users are still valid.

If delete_invalid_users is True, any users that do not pass validation will be deleted from the database. Use this if users might be deleted from an external system, such as local user accounts.

If False (default), invalid users remain in the Hub’s database and a warning will be issued. This is the default to avoid data loss due to config changes.

enable_auth_state c.LocalAuthenticator.enable_auth_state = Bool(False)#

Enable persisting auth_state (if available).

auth_state will be encrypted and stored in the Hub’s database. This can include things like authentication tokens, etc. to be passed to Spawners as environment variables.

Encrypting auth_state requires the cryptography package.

Additionally, the JUPYTERHUB_CRYPT_KEY environment variable must contain one (or more, separated by ;) 32B encryption keys. These can be either base64 or hex-encoded.

If encryption is unavailable, auth_state cannot be persisted.

New in JupyterHub 0.8

group_whitelist c.LocalAuthenticator.group_whitelist = Set()#

DEPRECATED: use allowed_groups

manage_groups c.LocalAuthenticator.manage_groups = Bool(False)#

Let authenticator manage user groups

If True, Authenticator.authenticate and/or .refresh_user may return a list of group names in the ‘groups’ field, which will be assigned to the user.

All group-assignment APIs are disabled if this is True.

manage_roles c.LocalAuthenticator.manage_roles = Bool(False)#

Let authenticator manage roles

If True, Authenticator.authenticate and/or .refresh_user may return a list of roles in the ‘roles’ field, which will be added to the database.

When enabled, all role management will be handled by the authenticator; in particular, assignment of roles via JupyterHub.load_roles traitlet will not be possible.

Added in version 5.0.

otp_prompt c.LocalAuthenticator.otp_prompt = Any('OTP:')#

The prompt string for the extra OTP (One Time Password) field.

Added in version 5.0.

post_auth_hook c.LocalAuthenticator.post_auth_hook = Any(None)#

An optional hook function that you can implement to do some bootstrapping work during authentication. For example, loading user account details from an external system.

This function is called after the user has passed all authentication checks and is ready to successfully authenticate. This function must return the auth_model dict reguardless of changes to it. The hook is called with 3 positional arguments: (authenticator, handler, auth_model).

This may be a coroutine.

Example:

import os
import pwd
def my_hook(authenticator, handler, auth_model):
    user_data = pwd.getpwnam(auth_model['name'])
    spawn_data = {
        'pw_data': user_data
        'gid_list': os.getgrouplist(auth_model['name'], user_data.pw_gid)
    }

    if auth_model['auth_state'] is None:
        auth_model['auth_state'] = {}
    auth_model['auth_state']['spawn_data'] = spawn_data

    return auth_model

c.Authenticator.post_auth_hook = my_hook
refresh_pre_spawn c.LocalAuthenticator.refresh_pre_spawn = Bool(False)#

Force refresh of auth prior to spawn.

This forces refresh_user() to be called prior to launching a server, to ensure that auth state is up-to-date.

This can be important when e.g. auth tokens that may have expired are passed to the spawner via environment variables from auth_state.

If refresh_user cannot refresh the user auth data, launch will fail until the user logs in again.

request_otp c.LocalAuthenticator.request_otp = Bool(False)#

Prompt for OTP (One Time Password) in the login form.

Added in version 5.0.

reset_managed_roles_on_startup c.LocalAuthenticator.reset_managed_roles_on_startup = Bool(False)#

Reset managed roles to result of load_managed_roles() on startup.

If True:
  • stale managed roles will be removed,

  • stale assignments to managed roles will be removed.

Any role not present in load_managed_roles() will be considered ‘stale’.

The ‘stale’ status for role assignments is also determined from load_managed_roles() result:

  • user role assignments status will depend on whether the users key is defined or not:

    • if a list is defined under the users key and the user is not listed, then the user role assignment will be considered ‘stale’,

    • if the users key is not provided, the user role assignment will be preserved;

  • service and group role assignments will be considered ‘stale’:

    • if not included in the services and groups list,

    • if the services and groups keys are not provided.

Added in version 5.0.

system_user_exists(user)#

Check if the user exists on the system

uids c.LocalAuthenticator.uids = Dict()#

Dictionary of uids to use at user creation time. This helps ensure that users created from the database get the same uid each time they are created in temporary deployments or containers.

username_map c.LocalAuthenticator.username_map = Dict()#

Dictionary mapping authenticator usernames to JupyterHub users.

Primarily used to normalize OAuth user names to local users.

username_pattern c.LocalAuthenticator.username_pattern = Unicode('')#

Regular expression pattern that all valid usernames must match.

If a username does not match the pattern specified here, authentication will not be attempted.

If not set, allow any username.

whitelist c.LocalAuthenticator.whitelist = Set()#

Deprecated, use Authenticator.allowed_users

PAMAuthenticator#

class jupyterhub.auth.PAMAuthenticator(**kwargs: Any)#

Authenticate local UNIX users with PAM

add_user_cmd c.PAMAuthenticator.add_user_cmd = Command()#

The command to use for creating users as a list of strings

For each element in the list, the string USERNAME will be replaced with the user’s username. The username will also be appended as the final argument.

For Linux, the default value is:

[‘adduser’, ‘-q’, ‘–gecos’, ‘””’, ‘–disabled-password’]

To specify a custom home directory, set this to:

[‘adduser’, ‘-q’, ‘–gecos’, ‘””’, ‘–home’, ‘/customhome/USERNAME’, ‘–disabled-password’]

This will run the command:

adduser -q –gecos “” –home /customhome/river –disabled-password river

when the user ‘river’ is created.

admin_groups c.PAMAuthenticator.admin_groups = Set()#

Authoritative list of user groups that determine admin access. Users not in these groups can still be granted admin status through admin_users.

allowed/blocked rules still apply.

Note: As of JupyterHub 2.0, full admin rights should not be required, and more precise permissions can be managed via roles.

admin_users c.PAMAuthenticator.admin_users = Set()#

Set of users that will have admin rights on this JupyterHub.

Note: As of JupyterHub 2.0, full admin rights should not be required, and more precise permissions can be managed via roles.

Admin users have extra privileges:
  • Use the admin panel to see list of users logged in

  • Add / remove users in some authenticators

  • Restart / halt the hub

  • Start / stop users’ single-user servers

  • Can access each individual users’ single-user server (if configured)

Admin access should be treated the same way root access is.

Defaults to an empty set, in which case no user has admin access.

allow_all c.PAMAuthenticator.allow_all = Bool(False)#

Allow every user who can successfully authenticate to access JupyterHub.

False by default, which means for most Authenticators, _some_ allow-related configuration is required to allow users to log in.

Authenticator subclasses may override the default with e.g.:

@default("allow_all")
def _default_allow_all(self):
    # if _any_ auth config (depends on the Authenticator)
    if self.allowed_users or self.allowed_groups or self.allow_existing_users:
        return False
    else:
        return True

Added in version 5.0.

Changed in version 5.0: Prior to 5.0, allow_all wasn’t defined on its own, and was instead implicitly True when no allow config was provided, i.e. allowed_users unspecified or empty on the base Authenticator class.

To preserve pre-5.0 behavior, set allow_all = True if you have no other allow configuration.

allow_existing_users c.PAMAuthenticator.allow_existing_users = Bool(False)#

Allow existing users to login.

Defaults to True if allowed_users is set for historical reasons, and False otherwise.

With this enabled, all users present in the JupyterHub database are allowed to login. This has the effect of any user who has _previously_ been allowed to login via any means will continue to be allowed until the user is deleted via the /hub/admin page or REST API.

Warning

Before enabling this you should review the existing users in the JupyterHub admin panel at /hub/admin. You may find users existing there because they have previously been declared in config such as allowed_users or allowed to sign in.

Warning

When this is enabled and you wish to remove access for one or more users previously allowed, you must make sure that they are removed from the jupyterhub database. This can be tricky to do if you stop allowing an externally managed group of users for example.

With this enabled, JupyterHub admin users can visit /hub/admin or use JupyterHub’s REST API to add and remove users to manage who can login.

Added in version 5.0.

allowed_groups c.PAMAuthenticator.allowed_groups = Set()#

Allow login from all users in these UNIX groups.

Changed in version 5.0: allowed_groups may be specified together with allowed_users, to grant access by group OR name.

allowed_users c.PAMAuthenticator.allowed_users = Set()#

Set of usernames that are allowed to log in.

Use this to limit which authenticated users may login. Default behavior: only users in this set are allowed.

If empty, does not perform any restriction, in which case any authenticated user is allowed.

Authenticators may extend Authenticator.check_allowed() to combine allowed_users with other configuration to either expand or restrict access.

Changed in version 1.2: Authenticator.whitelist renamed to allowed_users

any_allow_config c.PAMAuthenticator.any_allow_config = Bool(False)#

Is there any allow config?

Used to show a warning if it looks like nobody can access the Hub, which can happen when upgrading to JupyterHub 5, now that allow_all defaults to False.

Deployments can set this explicitly to True to suppress the “No allow config found” warning.

Will be True if any config tagged with .tag(allow_config=True) or starts with allow is truthy.

Added in version 5.0.

auth_refresh_age c.PAMAuthenticator.auth_refresh_age = Int(300)#

The max age (in seconds) of authentication info before forcing a refresh of user auth info.

Refreshing auth info allows, e.g. requesting/re-validating auth tokens.

See refresh_user() for what happens when user auth info is refreshed (nothing by default).

auto_login c.PAMAuthenticator.auto_login = Bool(False)#

Automatically begin the login process

rather than starting with a “Login with…” link at /hub/login

To work, .login_url() must give a URL other than the default /hub/login, such as an oauth handler or another automatic login handler, registered with .get_handlers().

Added in version 0.8.

auto_login_oauth2_authorize c.PAMAuthenticator.auto_login_oauth2_authorize = Bool(False)#

Automatically begin login process for OAuth2 authorization requests

When another application is using JupyterHub as OAuth2 provider, it sends users to /hub/api/oauth2/authorize. If the user isn’t logged in already, and auto_login is not set, the user will be dumped on the hub’s home page, without any context on what to do next.

Setting this to true will automatically redirect users to login if they aren’t logged in only on the /hub/api/oauth2/authorize endpoint.

Added in version 1.5.

blocked_users c.PAMAuthenticator.blocked_users = Set()#

Set of usernames that are not allowed to log in.

Use this with supported authenticators to restrict which users can not log in. This is an additional block list that further restricts users, beyond whatever restrictions the authenticator has in place.

If empty, does not perform any additional restriction.

Changed in version 1.2: Authenticator.blacklist renamed to blocked_users

check_account c.PAMAuthenticator.check_account = Bool(True)#

Whether to check the user’s account status via PAM during authentication.

The PAM account stack performs non-authentication based account management. It is typically used to restrict/permit access to a service and this step is needed to access the host’s user access control.

Disabling this can be dangerous as authenticated but unauthorized users may be granted access and, therefore, arbitrary execution on the system.

create_system_users c.PAMAuthenticator.create_system_users = Bool(False)#

If set to True, will attempt to create local system users if they do not exist already.

Supports Linux and BSD variants only.

delete_invalid_users c.PAMAuthenticator.delete_invalid_users = Bool(False)#

Delete any users from the database that do not pass validation

When JupyterHub starts, .add_user will be called on each user in the database to verify that all users are still valid.

If delete_invalid_users is True, any users that do not pass validation will be deleted from the database. Use this if users might be deleted from an external system, such as local user accounts.

If False (default), invalid users remain in the Hub’s database and a warning will be issued. This is the default to avoid data loss due to config changes.

enable_auth_state c.PAMAuthenticator.enable_auth_state = Bool(False)#

Enable persisting auth_state (if available).

auth_state will be encrypted and stored in the Hub’s database. This can include things like authentication tokens, etc. to be passed to Spawners as environment variables.

Encrypting auth_state requires the cryptography package.

Additionally, the JUPYTERHUB_CRYPT_KEY environment variable must contain one (or more, separated by ;) 32B encryption keys. These can be either base64 or hex-encoded.

If encryption is unavailable, auth_state cannot be persisted.

New in JupyterHub 0.8

encoding c.PAMAuthenticator.encoding = Unicode('utf8')#

The text encoding to use when communicating with PAM

group_whitelist c.PAMAuthenticator.group_whitelist = Set()#

DEPRECATED: use allowed_groups

manage_groups c.PAMAuthenticator.manage_groups = Bool(False)#

Let authenticator manage user groups

If True, Authenticator.authenticate and/or .refresh_user may return a list of group names in the ‘groups’ field, which will be assigned to the user.

All group-assignment APIs are disabled if this is True.

manage_roles c.PAMAuthenticator.manage_roles = Bool(False)#

Let authenticator manage roles

If True, Authenticator.authenticate and/or .refresh_user may return a list of roles in the ‘roles’ field, which will be added to the database.

When enabled, all role management will be handled by the authenticator; in particular, assignment of roles via JupyterHub.load_roles traitlet will not be possible.

Added in version 5.0.

open_sessions c.PAMAuthenticator.open_sessions = Bool(False)#

Whether to open a new PAM session when spawners are started.

This may trigger things like mounting shared filesystems, loading credentials, etc. depending on system configuration.

The lifecycle of PAM sessions is not correct, so many PAM session configurations will not work.

If any errors are encountered when opening/closing PAM sessions, this is automatically set to False.

Changed in version 2.2: Due to longstanding problems in the session lifecycle, this is now disabled by default. You may opt-in to opening sessions by setting this to True.

otp_prompt c.PAMAuthenticator.otp_prompt = Any('OTP:')#

The prompt string for the extra OTP (One Time Password) field.

Added in version 5.0.

pam_normalize_username c.PAMAuthenticator.pam_normalize_username = Bool(False)#

Round-trip the username via PAM lookups to make sure it is unique

PAM can accept multiple usernames that map to the same user, for example DOMAINusername in some cases. To prevent this, convert username into uid, then back to uid to normalize.

post_auth_hook c.PAMAuthenticator.post_auth_hook = Any(None)#

An optional hook function that you can implement to do some bootstrapping work during authentication. For example, loading user account details from an external system.

This function is called after the user has passed all authentication checks and is ready to successfully authenticate. This function must return the auth_model dict reguardless of changes to it. The hook is called with 3 positional arguments: (authenticator, handler, auth_model).

This may be a coroutine.

Example:

import os
import pwd
def my_hook(authenticator, handler, auth_model):
    user_data = pwd.getpwnam(auth_model['name'])
    spawn_data = {
        'pw_data': user_data
        'gid_list': os.getgrouplist(auth_model['name'], user_data.pw_gid)
    }

    if auth_model['auth_state'] is None:
        auth_model['auth_state'] = {}
    auth_model['auth_state']['spawn_data'] = spawn_data

    return auth_model

c.Authenticator.post_auth_hook = my_hook
refresh_pre_spawn c.PAMAuthenticator.refresh_pre_spawn = Bool(False)#

Force refresh of auth prior to spawn.

This forces refresh_user() to be called prior to launching a server, to ensure that auth state is up-to-date.

This can be important when e.g. auth tokens that may have expired are passed to the spawner via environment variables from auth_state.

If refresh_user cannot refresh the user auth data, launch will fail until the user logs in again.

request_otp c.PAMAuthenticator.request_otp = Bool(False)#

Prompt for OTP (One Time Password) in the login form.

Added in version 5.0.

reset_managed_roles_on_startup c.PAMAuthenticator.reset_managed_roles_on_startup = Bool(False)#

Reset managed roles to result of load_managed_roles() on startup.

If True:
  • stale managed roles will be removed,

  • stale assignments to managed roles will be removed.

Any role not present in load_managed_roles() will be considered ‘stale’.

The ‘stale’ status for role assignments is also determined from load_managed_roles() result:

  • user role assignments status will depend on whether the users key is defined or not:

    • if a list is defined under the users key and the user is not listed, then the user role assignment will be considered ‘stale’,

    • if the users key is not provided, the user role assignment will be preserved;

  • service and group role assignments will be considered ‘stale’:

    • if not included in the services and groups list,

    • if the services and groups keys are not provided.

Added in version 5.0.

service c.PAMAuthenticator.service = Unicode('login')#

The name of the PAM service to use for authentication

uids c.PAMAuthenticator.uids = Dict()#

Dictionary of uids to use at user creation time. This helps ensure that users created from the database get the same uid each time they are created in temporary deployments or containers.

username_map c.PAMAuthenticator.username_map = Dict()#

Dictionary mapping authenticator usernames to JupyterHub users.

Primarily used to normalize OAuth user names to local users.

username_pattern c.PAMAuthenticator.username_pattern = Unicode('')#

Regular expression pattern that all valid usernames must match.

If a username does not match the pattern specified here, authentication will not be attempted.

If not set, allow any username.

whitelist c.PAMAuthenticator.whitelist = Set()#

Deprecated, use Authenticator.allowed_users

DummyAuthenticator#

class jupyterhub.auth.DummyAuthenticator(**kwargs: Any)#

Dummy Authenticator for testing

By default, any username + password is allowed If a non-empty password is set, any username will be allowed if it logs in with that password.

Added in version 1.0.

Added in version 5.0: allow_all defaults to True, preserving default behavior.

admin_users c.DummyAuthenticator.admin_users = Set()#

Set of users that will have admin rights on this JupyterHub.

Note: As of JupyterHub 2.0, full admin rights should not be required, and more precise permissions can be managed via roles.

Admin users have extra privileges:
  • Use the admin panel to see list of users logged in

  • Add / remove users in some authenticators

  • Restart / halt the hub

  • Start / stop users’ single-user servers

  • Can access each individual users’ single-user server (if configured)

Admin access should be treated the same way root access is.

Defaults to an empty set, in which case no user has admin access.

allow_all c.DummyAuthenticator.allow_all = Bool(False)#

Allow every user who can successfully authenticate to access JupyterHub.

False by default, which means for most Authenticators, _some_ allow-related configuration is required to allow users to log in.

Authenticator subclasses may override the default with e.g.:

@default("allow_all")
def _default_allow_all(self):
    # if _any_ auth config (depends on the Authenticator)
    if self.allowed_users or self.allowed_groups or self.allow_existing_users:
        return False
    else:
        return True

Added in version 5.0.

Changed in version 5.0: Prior to 5.0, allow_all wasn’t defined on its own, and was instead implicitly True when no allow config was provided, i.e. allowed_users unspecified or empty on the base Authenticator class.

To preserve pre-5.0 behavior, set allow_all = True if you have no other allow configuration.

allow_existing_users c.DummyAuthenticator.allow_existing_users = Bool(False)#

Allow existing users to login.

Defaults to True if allowed_users is set for historical reasons, and False otherwise.

With this enabled, all users present in the JupyterHub database are allowed to login. This has the effect of any user who has _previously_ been allowed to login via any means will continue to be allowed until the user is deleted via the /hub/admin page or REST API.

Warning

Before enabling this you should review the existing users in the JupyterHub admin panel at /hub/admin. You may find users existing there because they have previously been declared in config such as allowed_users or allowed to sign in.

Warning

When this is enabled and you wish to remove access for one or more users previously allowed, you must make sure that they are removed from the jupyterhub database. This can be tricky to do if you stop allowing an externally managed group of users for example.

With this enabled, JupyterHub admin users can visit /hub/admin or use JupyterHub’s REST API to add and remove users to manage who can login.

Added in version 5.0.

allowed_users c.DummyAuthenticator.allowed_users = Set()#

Set of usernames that are allowed to log in.

Use this to limit which authenticated users may login. Default behavior: only users in this set are allowed.

If empty, does not perform any restriction, in which case any authenticated user is allowed.

Authenticators may extend Authenticator.check_allowed() to combine allowed_users with other configuration to either expand or restrict access.

Changed in version 1.2: Authenticator.whitelist renamed to allowed_users

any_allow_config c.DummyAuthenticator.any_allow_config = Bool(False)#

Is there any allow config?

Used to show a warning if it looks like nobody can access the Hub, which can happen when upgrading to JupyterHub 5, now that allow_all defaults to False.

Deployments can set this explicitly to True to suppress the “No allow config found” warning.

Will be True if any config tagged with .tag(allow_config=True) or starts with allow is truthy.

Added in version 5.0.

auth_refresh_age c.DummyAuthenticator.auth_refresh_age = Int(300)#

The max age (in seconds) of authentication info before forcing a refresh of user auth info.

Refreshing auth info allows, e.g. requesting/re-validating auth tokens.

See refresh_user() for what happens when user auth info is refreshed (nothing by default).

auto_login c.DummyAuthenticator.auto_login = Bool(False)#

Automatically begin the login process

rather than starting with a “Login with…” link at /hub/login

To work, .login_url() must give a URL other than the default /hub/login, such as an oauth handler or another automatic login handler, registered with .get_handlers().

Added in version 0.8.

auto_login_oauth2_authorize c.DummyAuthenticator.auto_login_oauth2_authorize = Bool(False)#

Automatically begin login process for OAuth2 authorization requests

When another application is using JupyterHub as OAuth2 provider, it sends users to /hub/api/oauth2/authorize. If the user isn’t logged in already, and auto_login is not set, the user will be dumped on the hub’s home page, without any context on what to do next.

Setting this to true will automatically redirect users to login if they aren’t logged in only on the /hub/api/oauth2/authorize endpoint.

Added in version 1.5.

blocked_users c.DummyAuthenticator.blocked_users = Set()#

Set of usernames that are not allowed to log in.

Use this with supported authenticators to restrict which users can not log in. This is an additional block list that further restricts users, beyond whatever restrictions the authenticator has in place.

If empty, does not perform any additional restriction.

Changed in version 1.2: Authenticator.blacklist renamed to blocked_users

delete_invalid_users c.DummyAuthenticator.delete_invalid_users = Bool(False)#

Delete any users from the database that do not pass validation

When JupyterHub starts, .add_user will be called on each user in the database to verify that all users are still valid.

If delete_invalid_users is True, any users that do not pass validation will be deleted from the database. Use this if users might be deleted from an external system, such as local user accounts.

If False (default), invalid users remain in the Hub’s database and a warning will be issued. This is the default to avoid data loss due to config changes.

enable_auth_state c.DummyAuthenticator.enable_auth_state = Bool(False)#

Enable persisting auth_state (if available).

auth_state will be encrypted and stored in the Hub’s database. This can include things like authentication tokens, etc. to be passed to Spawners as environment variables.

Encrypting auth_state requires the cryptography package.

Additionally, the JUPYTERHUB_CRYPT_KEY environment variable must contain one (or more, separated by ;) 32B encryption keys. These can be either base64 or hex-encoded.

If encryption is unavailable, auth_state cannot be persisted.

New in JupyterHub 0.8

manage_groups c.DummyAuthenticator.manage_groups = Bool(False)#

Let authenticator manage user groups

If True, Authenticator.authenticate and/or .refresh_user may return a list of group names in the ‘groups’ field, which will be assigned to the user.

All group-assignment APIs are disabled if this is True.

manage_roles c.DummyAuthenticator.manage_roles = Bool(False)#

Let authenticator manage roles

If True, Authenticator.authenticate and/or .refresh_user may return a list of roles in the ‘roles’ field, which will be added to the database.

When enabled, all role management will be handled by the authenticator; in particular, assignment of roles via JupyterHub.load_roles traitlet will not be possible.

Added in version 5.0.

otp_prompt c.DummyAuthenticator.otp_prompt = Any('OTP:')#

The prompt string for the extra OTP (One Time Password) field.

Added in version 5.0.

password c.DummyAuthenticator.password = Unicode('')#

Set a global password for all users wanting to log in.

This allows users with any username to log in with the same static password.

post_auth_hook c.DummyAuthenticator.post_auth_hook = Any(None)#

An optional hook function that you can implement to do some bootstrapping work during authentication. For example, loading user account details from an external system.

This function is called after the user has passed all authentication checks and is ready to successfully authenticate. This function must return the auth_model dict reguardless of changes to it. The hook is called with 3 positional arguments: (authenticator, handler, auth_model).

This may be a coroutine.

Example:

import os
import pwd
def my_hook(authenticator, handler, auth_model):
    user_data = pwd.getpwnam(auth_model['name'])
    spawn_data = {
        'pw_data': user_data
        'gid_list': os.getgrouplist(auth_model['name'], user_data.pw_gid)
    }

    if auth_model['auth_state'] is None:
        auth_model['auth_state'] = {}
    auth_model['auth_state']['spawn_data'] = spawn_data

    return auth_model

c.Authenticator.post_auth_hook = my_hook
refresh_pre_spawn c.DummyAuthenticator.refresh_pre_spawn = Bool(False)#

Force refresh of auth prior to spawn.

This forces refresh_user() to be called prior to launching a server, to ensure that auth state is up-to-date.

This can be important when e.g. auth tokens that may have expired are passed to the spawner via environment variables from auth_state.

If refresh_user cannot refresh the user auth data, launch will fail until the user logs in again.

request_otp c.DummyAuthenticator.request_otp = Bool(False)#

Prompt for OTP (One Time Password) in the login form.

Added in version 5.0.

reset_managed_roles_on_startup c.DummyAuthenticator.reset_managed_roles_on_startup = Bool(False)#

Reset managed roles to result of load_managed_roles() on startup.

If True:
  • stale managed roles will be removed,

  • stale assignments to managed roles will be removed.

Any role not present in load_managed_roles() will be considered ‘stale’.

The ‘stale’ status for role assignments is also determined from load_managed_roles() result:

  • user role assignments status will depend on whether the users key is defined or not:

    • if a list is defined under the users key and the user is not listed, then the user role assignment will be considered ‘stale’,

    • if the users key is not provided, the user role assignment will be preserved;

  • service and group role assignments will be considered ‘stale’:

    • if not included in the services and groups list,

    • if the services and groups keys are not provided.

Added in version 5.0.

username_map c.DummyAuthenticator.username_map = Dict()#

Dictionary mapping authenticator usernames to JupyterHub users.

Primarily used to normalize OAuth user names to local users.

username_pattern c.DummyAuthenticator.username_pattern = Unicode('')#

Regular expression pattern that all valid usernames must match.

If a username does not match the pattern specified here, authentication will not be attempted.

If not set, allow any username.

whitelist c.DummyAuthenticator.whitelist = Set()#

Deprecated, use Authenticator.allowed_users