Authenticators#
Quetz delegates the task of authenticating users (checking passwords etc.) to third-party identity providers. It can communicate via the OAuth2 and OpenID protocols supported by services such as Github and Google. This means that you can configure Quetz to have users log in with their Github accounts. Quetz also supports the PAM-based authentication which uses local Unix users for authentication.
Warning
While it is possible to register and use multiple authenticators at once, it is heavily discouraged and a warning will be printed. Currently quetz does not automatically merge accounts based on email addresses and usernames from different auth providers can overlap.
A warning will be printed when running quetz with multiple activated auth providers.
Built-in authenticators#
These authenticator classes are built-in and can be activated by adding relevant section to the configuration file. See below for more details (the class names are only for reference, they are already included in the Quetz server).
PAM#
- class quetz.authentication.pam.PAMAuthenticator(config: Config, provider=None, app=None)#
Use PAM to authenticate with local system users.
To enable add the following to your configuration file:
[pamauthenticator] # name for the provider, used in the login URL provider = "pam" # use the following to translate the Unix groups that # users might belong to user role on Quetz server admin_groups = ["root", "quetz"] maintainer_groups = [] user_groups = []
On most Linux systems you can add users with:
useradd USERNAME # set password interactively with passwd USERNAME
Note
For this authenticator to work, the user who runs the server must be root or be in
shadow
group.
Github#
- class quetz.authentication.github.GithubAuthenticator(config: Config, client_kwargs=None, provider=None, app=None)#
Use Github account to authenticate users with Quetz.
To enable add the following to the configuration file:
[github] client_id = "fde330aef1fbe39991" client_secret = "03728444a12abff17e9444fd231b4379d58f0b"
You can obtain
client_id
andclient_secret
by registering your application with Github at this URL: settings/applications.
Gitlab#
- class quetz.authentication.gitlab.GitlabAuthenticator(config: Config, client_kwargs=None, provider=None, app=None)#
Use Gitlab account to authenticate users with Quetz.
To enable add the following to the configuration file:
[gitlab] client_id = "fde330aef1fbe39991" client_secret = "03728444a12abff17e9444fd231b4379d58f0b"
The above will use gitlab. You can specify a self-hosted GitLab instance using the
url
parameter:[gitlab] url = "https://gitlab.mydomain.org" client_id = "fde330aef1fbe39991" client_secret = "03728444a12abff17e9444fd231b4379d58f0b"
You can obtain
client_id
andclient_secret
by registering your application with Gitlab at this URL: -/profile/applications or https://gitlab.mydomain.org/admin/applications if using a self-hosted GitLab instance. Selectopenid
as scope. If you want to collect email addresses, make sure to also selectemail
,profile
andread_user
as scope in the Gitlab interface.
Google#
- class quetz.authentication.google.GoogleAuthenticator(config: Config, client_kwargs=None, provider=None, app=None)#
Use Google account to authenticate users with Quetz.
To enable add the following to the configuration file:
[google] client_id = "1111111111-dha39auqzp92110sdf.apps.googleusercontent.com" client_secret = "03728444a12abff17e9444fd231b4379d58f0b"
You can obtain
client_id
andclient_secret
by registering your application with Google platfrom at this URL: https://console.developers.google.com/apis/credentials.
Jupyterhub#
- class quetz.authentication.jupyterhub.JupyterhubAuthenticator(config: Config, client_kwargs=None, provider=None, app=None)#
Use Oauth2 protcol to authenticate with jupyterhub server, which acts as identity provider.
To activate add the following section to the
config.toml
(see Config file):[jupyterhubauthenticator] # client credentials, they need to be registered with # jupyterhub by adding an external service client_id = "quetz_client" client_secret = "super-secret" # token enpoint of Jupyterhub, needs to be accessible from Quetz server access_token_url = "http://JUPYTERHUB_HOST:PORT/hub/api/oauth2/token" # authorize endpoint of JupyterHub, needs to be accessible from users' browser authorize_url = "http://JUPYTERHUB_HOST:PORT/hub/api/oauth2/authorize" # API root, needs to be accesible from Quetz server api_base_url = "http://JUPYTERHUB_HOST:PORT/hub/api/"
To configure quetz as an oauth client in JupyterHub, you will need to define a JupyterHub service. You can achieve it by adding the following to the
jupyterhub_config.py
file of your JupyterHub instance:c.JupyterHub.services = [ { # service name, it will be used to setup routers 'name': 'quetz', # quetz URL to setup redirections, only required if you use # JupyterHub url scheme 'url': 'http://QUETZ_HOST:PORT', # any secret >8 characters, you will also need to set # the client_secret in the authenticator config with this # string 'api_token': 'super-secret', # client_id in the authenticator config 'oauth_client_id': 'quetz_client', # URL of the callback endpoint on the quetz server 'oauth_redirect_uri': 'http://QUETZ_HOST:PORT/auth/jupyterhub/authorize', } ]
Custom authenticators#
You can also implement new authenticators for Quetz server.
Authentication base classes#
The authenticator should derive from one of the base classes:
- class quetz.authentication.base.BaseAuthenticator(config: Config, provider=None, app=None)#
Base class for authenticators.
Subclasses MUST implement:
Subclasses SHOULD implement:
configure()
validate_token()
- class quetz.authentication.base.SimpleAuthenticator(config: Config, provider=None, app=None)#
A demo of a possible implementation. It redirects users to a simple HTML form where they can type their username and password.
Note: Consider this an example. In your production setting you would probably want to redirect to your custom login page. Make sure that this page submits data to
/auth/{provider}/authorize
endpoint.
- class quetz.authentication.oauth2.OAuthAuthenticator(config: Config, client_kwargs=None, provider=None, app=None)#
Base class for authenticators using Oauth2 protocol and its variants.
The
authenticate()
method is already implemented, but you will need to override some of the following variables in sublasses to make it work:- Variables:
provider (str) – name of the provider (it will be used in the url)
handler_cls – class with handlers for all oauth2 relevant endpoints in Quetz server
client_id – required, client id registered with the provider
client_secret – required, likewise
is_enabled (bool) – True if authenticator is enabled, can be configured in
configure()
methodaccess_token_url – URL of the OAuth2 endpoint ot request a token
authorize_url – URL of the OAuth2
authorize
endpointapi_base_url – URL of the API root of the provider server
validate_token_url – path of endpoint to validate the token
Implement authentication logic#
To implement some custom authentication logic, your class should override at least
authenticate()
method (except
for OAuthAuthenticator
subclasses):
- async BaseAuthenticator.authenticate(request, data=None, dao=None, config=None, **kwargs) Optional[Union[str, UserDict]] #
Authentication user with the data submitted.
This method should return:
None
if the authentication failed,string with username for successful authentication
or a dictionary with keys
username
,profile
(user profile data),auth_state
(extra authentication state to be stored in the browser session).
For example, the custom authenticator might be:
class DictionaryAuthenticator(SimpleAuthenticator):
"""Simple demo authenticator that authenticates with
users from a dictionary of usernames and passwords."""
passwords: dict = {"happyuser": "happy"}
provider = "dict"
async def authenticate(self, request, data, **kwargs):
"""``data`` argument is username and password entered by
user in the login form."""
if self.passwords.get(data['username']) == data['password']:
return data['username']
Registering authenticator#
The standard way to register an authenticator with Quetz, is to distibute it as a plugin (see Plugins). To automatize the creation of a plugin, check out our cookiecutter template.
If not using the cookiecutter, you can register an authenticator with Quetz by defining
an entry point. You can create entry point with the following snippet in the setup.py
:
from setuptools import setup
# you will need to adapt these variables
# to the names from your package
PACKAGE_NAME = quetz_dictauthenticator
AUTHENTICATOR_CLASS = "DictAuthenticator"
MODULE_NAME = authenticators
setup(
name="quetz-dictauthenticator",
install_requires="quetz",
entry_points={
"quetz.authenticator": [
f"{AUTHENTICATOR_CLASS.lower()} = {PACKAGE_NAME}:{MODULE_NAME}.{AUTHENTICATOR_CLASS}"
]
},
packages=[PACKAGE_NAME],
)