Base classes for use by OAuth2 based JupyterHub authenticator classes.

Founded based on work by Kyle Kelley (@rgbkrk)

class oauthenticator.oauth2.OAuthLoginHandler(application: Application, request: HTTPServerRequest, **kwargs: Any)#

Base class for OAuth login handler

Typically subclasses will need

class oauthenticator.oauth2.OAuthCallbackHandler(application: Application, request: HTTPServerRequest, **kwargs: Any)#

Basic handler for OAuth callback. Calls authenticator to verify username.

class oauthenticator.oauth2.OAuthenticator(**kwargs: Any)#

Base class for OAuthenticators.

Subclasses should, in an increasing level of customization:

  • Override the constant user_auth_state_key

  • Override various config’s default values, such as authorize_url, token_url, userdata_url, and login_service.

  • Override various methods called by the authenticate method, which subclasses should not override.

  • Override handler classes such as login_handler, callback_handler, and logout_handler.


Overrides Authenticator.add_user, a hook called for all users in the database on startup and for each user being created.

The purpose of the override is to implement the allow_existing_users config by adding users to the allowed_users set only if allow_existing_users is truthy. The overridden behavior is to do it if allowed_users is truthy.

The implementation is adjusted from JupyterHub 4.0.1: jupyterhub/jupyterhub

admin_groups c.OAuthenticator.admin_groups = Set()#

Allow members of selected groups to sign in and consider them as JupyterHub admins.

If this is set and a user isn’t part of one of these groups or listed in admin_users, a user signing in will have their admin status revoked.

Requires manage_groups to also be True.

admin_users c.OAuthenticator.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.OAuthenticator.allow_all = Bool(False)#

Allow all authenticated users to login.

Overrides all other allow configuration.

Added in version 16.0.

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

Allow existing users to login.

Enable this if you want to manage user access via the JupyterHub admin page (/hub/admin).

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.


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.


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 a group of externally managed 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 16.0.

Changed in version 16.0: Before this config was available, the default behavior was to allow existing users if allowed_users was configured with one or more user.

allowed_groups c.OAuthenticator.allowed_groups = Set()#

Allow members of selected groups to log in.

Requires manage_groups to also be True.

allowed_scopes c.OAuthenticator.allowed_scopes = List()#

Allow users who have been granted all these scopes to log in.

We request all the scopes listed in the ‘scope’ config, but only a subset of these may be granted by the authorization server. This may happen if the user does not have permissions to access a requested scope, or has chosen to not give consent for a particular scope. If the scopes listed in this config are not granted, the user will not be allowed to log in.

The granted scopes will be part of the access token (fetched from self.token_url). See https://datatracker.ietf.org/doc/html/rfc6749#section-3.3 for more information.

See the OAuth documentation of your OAuth provider for various options.

allowed_users c.OAuthenticator.allowed_users = Set()#

Set of usernames that should be allowed to login.

If unspecified, grants no access. You must set at least one other allow configuration if any users are to have permission to access the Hub.

Any usernames in admin_users will also be allowed to login.

any_allow_config c.OAuthenticator.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.OAuthenticator.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).

auth_state_groups_key c.OAuthenticator.auth_state_groups_key = Union()#

Determine groups this user belongs based on contents of auth_state.

Can be a string key name (use periods for nested keys), or a callable that accepts the auth state (as a dict) and returns the groups list.

Requires manage_groups to also be True.

async authenticate(handler, data=None, **kwargs)#

A JupyterHub Authenticator’s authenticate method’s job is:

  • return None if the user isn’t successfully authenticated

  • return a dictionary if authentication is successful with name, admin (optional), and auth_state (optional)

Subclasses should not override this method.

authorize_url c.OAuthenticator.authorize_url = Unicode('')#

The URL to where the user is to be redirected initially based on the OAuth2 protocol. The user will be redirected back with an authorization grant code after authenticating successfully with the identity provider.

For more context, see the Protocol Flow section in the OAuth2 standard document, specifically steps A-B.

auto_login c.OAuthenticator.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.OAuthenticator.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.

basic_auth c.OAuthenticator.basic_auth = Bool(False)#

Whether or to use HTTP Basic authentication instead of form based authentication in requests to token_url.

When using HTTP Basic authentication, a HTTP header is set with the client_id and client_secret encoded in it.

When using form based authentication, the client_id and client_secret is put in the HTTP POST request’s body.

Changed in version 16.0.0: This configuration now toggles between HTTP Basic authentication and form based authentication when working against the token_url.

Previously when this was configured True, both would be used contrary to a recommendation in OAuth 2.0 documentation.

Changed in version 16.0.2: The default value for this configuration for GenericOAuthenticator changed from True to False.

blocked_users c.OAuthenticator.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

build_access_tokens_request_params(handler, data=None)#

Builds the parameters that should be passed to the URL request that exchanges the OAuth code for the Access Token. Called by the oauthenticator.OAuthenticator.authenticate().

build_auth_state_dict(token_info, user_info)#

Builds the auth_state dict that will be returned by a succesfull authenticate method call.

  • token_info – the dictionary returned by the token request (exchanging the OAuth code for an Access Token)

  • user_info – the dictionary returned by the userdata request


a dictionary of auth state that should be persisted with the following keys:
  • ”access_token”: the access_token

  • ”refresh_token”: the refresh_token, if available

  • ”id_token”: the id_token, if available

  • ”scope”: the scopes, if available

  • ”token_response”: the full token_info response

  • self.user_auth_state_key: the full user_info response

Return type:


Called by the oauthenticator.OAuthenticator.authenticate()


Builds and returns the headers to be used in the access token request. Called by the oauthenticator.OAuthenticator.get_token_info().

The Content-Type header is specified by the OAuth 2.0 RFC in https://www.rfc-editor.org/rfc/rfc6749#section-4.1.3. utf-8 is also required according to https://www.rfc-editor.org/rfc/rfc6749#appendix-B, and that can be specified with a Content-Type directive according to https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type#directives.

build_userdata_request_headers(access_token, token_type)#

Builds and returns the headers to be used in the userdata request. Called by the oauthenticator.OAuthenticator.token_to_user()


alias of OAuthCallbackHandler

async check_allowed(username, auth_model)#

Returns True for users allowed to be authorized

If a user must be disallowed, raises a 403 exception.

Overrides Authenticator.check_allowed that is called from Authenticator.get_authenticated_user after OAuthenticator.authenticate has been called, and therefore also after update_auth_model has been called.

Subclasses with additional config to allow a user should override this method and return True when this method returns True or if a user is allowed via the additional config.

client_id c.OAuthenticator.client_id = Unicode('')#

The client id of the OAuth2 application registered with the identity provider.

client_secret c.OAuthenticator.client_secret = Unicode('')#

The client secret of the OAuth2 application registered with the identity provider.

custom_403_message c.OAuthenticator.custom_403_message = Unicode('Sorry, you are not currently authorized to use this hub. Please contact the hub administrator.')#

The message to be shown when user was not allowed

delete_invalid_users c.OAuthenticator.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.OAuthenticator.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

extra_authorize_params c.OAuthenticator.extra_authorize_params = Dict()#

Extra GET params to send along with the initial OAuth request to the OAuth provider.

async fetch(req, label='fetching', parse_json=True, **kwargs)#

Wrapper for http requests

logs error responses, parses successful JSON responses

  • req – tornado HTTPRequest

  • label (str) – label describing what is happening, used in log message when the request fails.

  • parse_json (bool) – whether to parse the response as JSON

  • **kwargs – remaining keyword args passed to underlying client.fetch(req, **kwargs)


parsed JSON response if parse_json=True, else tornado.HTTPResponse


Get my OAuth redirect URL

Either from config or guess based on the current request.


Return any custom handlers the authenticator needs to register

Used in conjugation with login_url and logout_url.


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


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

Return type:

handlers (list)

async get_prev_refresh_token(handler, username)#

Retrieves the refresh_token from previous encrypted auth state. Called by the oauthenticator.OAuthenticator.authenticate()

async get_token_info(handler, params)#

Makes a “POST” request to self.token_url, with the parameters received as argument.


the JSON response to the token_url the request.

Called by the oauthenticator.OAuthenticator.authenticate()

get_user_groups(auth_state: dict)#

Returns a set of groups the user belongs to based on auth_state_groups_key and provided auth_state.

  • If auth_state_groups_key is a callable, it returns the list of groups directly.

  • If auth_state_groups_key is a nested dictionary key like “permissions.groups”, this function returns auth_state[“permissions”][“groups”].

http_request_kwargs c.OAuthenticator.http_request_kwargs = Dict()#

Extra default kwargs passed to all HTTPRequests.

# Example: send requests through a proxy
c.OAuthenticator.http_request_kwargs = {
    "proxy_host": "proxy.example.com",
    "proxy_port": 8080,

# Example: validate against certain root certificates
c.OAuthenticator.http_request_kwargs = {
    "ca_certs": "/path/to/a.crt",

See tornado.httpclient.HTTPRequest for all kwargs options you can pass. Note that the HTTP client making these requests is tornado.httpclient.AsyncHTTPClient.

async httpfetch(url, label='fetching', parse_json=True, raise_error=True, **kwargs)#

Wrapper for creating and fetching http requests

Includes http_request_kwargs in request kwargs logs error responses, parses successful JSON responses

  • url (str) – url to fetch

  • label (str) – label describing what is happening, used in log message when the request fails.

  • parse_json (bool) – whether to parse the response as JSON

  • raise_error (bool) – whether to raise an exception on HTTP errors

  • **kwargs – remaining keyword args passed to underlying tornado.HTTPRequest, overrides http_request_kwargs


parsed JSON response if parse_json=True, else tornado.HTTPResponse


alias of OAuthLoginHandler

login_service c.OAuthenticator.login_service = Unicode('OAuth 2.0')#

Name of the login service or identity provider that this authenticator is using to authenticate users.

This config influences the text on a button shown to unauthenticated users before they click it to login, assuming auto_login isn’t configured True.

The login button’s text will be “Login with <login_service>”.


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.


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


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

Return type:



alias of OAuthLogoutHandler

logout_redirect_url c.OAuthenticator.logout_redirect_url = Unicode('')#

When configured, users are not presented with the JupyterHub logout page, but instead redirected to this destination.


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.


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


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

Return type:


manage_groups c.OAuthenticator.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.OAuthenticator.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.

oauth_callback_url c.OAuthenticator.oauth_callback_url = Unicode('')#

Callback URL to use.

When registering an OAuth2 application with an identity provider, this is typically called the redirect url.

Should very likely be set to https://[your-domain]/hub/oauth_callback.

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

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

Added in version 5.0.

post_auth_hook c.OAuthenticator.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.


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.OAuthenticator.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.OAuthenticator.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.OAuthenticator.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.

scope c.OAuthenticator.scope = List()#

The OAuth scopes to request.

See the OAuth documentation of your OAuth provider for options.

token_params c.OAuthenticator.token_params = Dict()#

Extra parameters for first POST request exchanging the OAuth code for an Access Token

async token_to_user(token_info)#

Determines who the logged-in user by sending a “GET” request to oauthenticator.OAuthenticator.userdata_url using the access_token.

If oauthenticator.OAuthenticator.userdata_from_id_token is set then extracts the corresponding info from an id_token instead.


token_info – the dictionary returned by the token request (exchanging the OAuth code for an Access Token)


the JSON response to the userdata_url request.

Called by the oauthenticator.OAuthenticator.authenticate()

token_url c.OAuthenticator.token_url = Unicode('')#

The URL to where this authenticator makes a request to acquire an access token based on the authorization code received by the user returning from the authorize_url.

For more context, see the Protocol Flow section in the OAuth2 standard document, specifically steps C-D.

async update_auth_model(auth_model)#

Updates and returns the auth_model dict.

Should be overridden to collect information required for check_allowed.

Args: auth_model - the auth model dictionary, containing:
  • name: the normalized username

  • admin: the admin status (True/False/None), where None means it

    should be unchanged.

  • auth_state: the dictionary of of auth state

    returned by oauthenticator.OAuthenticator.build_auth_state_dict()

Called by the oauthenticator.OAuthenticator.authenticate()


Gets the self.username_claim key’s value from the user_info dictionary.

Should be overridden by the authenticators for which the hub username cannot be extracted this way and needs extra processing.


user_info – the dictionary returned by the userdata request


user_info[“self.username_claim”] or raises an error if such value isn’t found.

Called by the oauthenticator.OAuthenticator.authenticate()

userdata_from_id_token c.OAuthenticator.userdata_from_id_token = Bool(False)#

Extract user details from an id token received via a request to token_url, rather than making a follow-up request to the userinfo endpoint userdata_url.

Should only be used if token_url uses HTTPS, to ensure token authenticity.

For more context, see Authentication using the Authorization Code Flow in the OIDC Core standard document.

userdata_params c.OAuthenticator.userdata_params = Dict()#

Userdata params to get user data login information.

userdata_token_method c.OAuthenticator.userdata_token_method = Unicode('header')#

Method for sending access token in userdata request.

Supported methods: header, url.

userdata_url c.OAuthenticator.userdata_url = Unicode('')#

The URL to where this authenticator makes a request to acquire user details with an access token received via a request to the token_url.

For more context, see the Protocol Flow section in the OAuth2 standard document, specifically steps E-F.

Incompatible with userdata_from_id_token.

username_claim c.OAuthenticator.username_claim = Union()#

When userdata_url returns a json response, the username will be taken from this key.

Can be a string key name or a callable that accepts the returned userdata json (as a dict) and returns the username. The callable is useful e.g. for extracting the username from a nested object in the response or doing other post processing.

What keys are available will depend on the scopes requested and the authenticator used.

username_map c.OAuthenticator.username_map = Dict()#

Dictionary mapping authenticator usernames to JupyterHub users.

Primarily used to normalize OAuth user names to local users.

username_pattern c.OAuthenticator.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.

validate_server_cert c.OAuthenticator.validate_server_cert = Bool(False)#

Determines if certificates are validated.

Only set this to False if you feel confident it will not be a security concern.

whitelist c.OAuthenticator.whitelist = Set()#

Deprecated, use Authenticator.allowed_users