Closed pfisterer closed 4 years ago
Hi @pfisterer, How are you generating the client_secret:
c.GenericOAuthenticator.client_secret = 'asfdsdf-dfdf-fhhfh-aed7-asdafsdfsaf'
@willingc I obtained the (original) client secret from Keycloak (and verified that it is correct using curl). However, the string above is a modified version (just as the domain name does not reflect my internal settings at the university).
My problem ist that the browser is never redirected to Keycloak for the user to authenticate.
Would it help if I supply a keycloak instance for testing?
@pfisterer Thanks for the testing offer. I'm out of the office for the next week so I may have a delayed response.
@minrk Have you worked with Keycloak? I'm thinking that we need to add to the generic auth to support Keycloak's use of Realm.
I haven't used the generic authenticator. @dmvieira do you have an example of a fully working generic oauth config?
@pfisterer what happens when you hit the 'Login with...' button exactly, if there is no redirect? Are there errors in the js console or the jupyterhub logs?
For OAuthenticator generic integration we need to add at jupyterhub_config.py: c.JupyterHub.authenticator_class = 'oauthenticator.generic.GenericOAuthenticator' c.OAuthenticator.client_id # oauth2 client id for your app c.OAuthenticator.client_secret # oauth2 client secret for your app c.GenericOAuthenticator.token_url # oauth2 provider's token url c.GenericOAuthenticator.userdata_url # oauth2 provider's endpoint with user data c.GenericOAuthenticator.userdata_method # method used to request user data endpoint c.GenericOAuthenticator.userdata_params # params to send for userdata endpoint c.GenericOAuthenticator.username_key # username key from json returned from user data endpoint
And export these environment variables: OAUTH2_TOKEN_URL # the same as token_url param OAUTH2_AUTHORIZE_URL # oauth2 provider's authorization andpoint
For more details I'll show how to integrate with generic authentication in https://conferences.oreilly.com/jupyter/jup-ny/public/schedule/detail/59400
Perhaps you forgot environment variables
Was there any resolution to this? I'm attempting a similar setup and have been running into the same issues. The button for the login page still directs to example.com:8000/hub/login
so it won't direct the user to the Keycloak login. If I manually navigate to example.com:8000/hub/oauth_login
I end up with an infinite redirect to example.com:8000/hub/oauth_callback?...
and don't actually hit Keycloak's login.
Are you using the environment variables?
OAUTH2_TOKEN_URL # the same as token_url param OAUTH2_AUTHORIZE_URL # oauth2 provider's authorization andpoint
Fixing the env variables solved the infinite redirect after navigating to /hub/oauth_login
. Unfortunately the login url for the button on /hub/login
still doesn't get set properly.
You can use custom template paths and change login button template to redirect to correct login URL.
Here I'm using extra_template_path and template_paths at jupyter and jupyterhub configs. Then you can use your custom htmls
It's strange because you should never click on login button... Oauthenticator always redirects you to authorize URL when you try to access Notebook. Please, explain me a bit more and paste your Hub and Notebook configuration
Censored some bits but here's the config for authentication:
from oauthenticator.generic import LocalGenericOAuthenticator
c.JupyterHub.authenticator_class = LocalGenericOAuthenticator
c.OAuthenticator.client_id = '{client_id}'
c.OAuthenticator.client_secret = '{client_secret}'
c.LocalGenericOAuthenticator.token_url = 'https://{domain}:{port}/auth/realms/{realm}/protocol/openid-connect/token'# oauth2 provider's token url
c.LocalGenericOAuthenticator.userdata_url = 'https://{domain}:{port}/auth/realms/{realm}/protocol/openid-connect/userinfo'
c.LocalGenericOAuthenticator.userdata_method = 'GET'
c.LocalGenericOAuthenticator.userdata_params = {"state": "state"}
c.LocalGenericOAuthenticator.username_key = "preferred_username"
c.LocalAuthenticator.create_system_users = True
and env vars
OAUTH2_AUTHORIZE_URL=https://{domain}:{port}/auth/realms/{realm}/protocol/openid-connect/auth
OAUTH2_TOKEN_URL=https:/{domain}:{port}/auth/realms/{realm}/protocol/openid-connect/token
Should I be setting something like c.Authenticator.auto_login = True
to get the intended behavior?
I never used LocalGenericOAuthenticator only GenericOAuthenticator and it works great... LocalGenericOAuthenticator I don't know
Hi All,
I am able to log in to Jupyterhub successfully with configuration mentioned above. (using Keycloak for authentication and GenericOAuthenticator as Oauth class), but the problem is that I am not able to logout. The only thing that happens when I click on "logout" button - I am redirected to default login page with login "Sign in with GenericOAuth2" button again. And if I click on this button I am not prompted for any credentials.
Log record below...
[I 2017-10-25 13:33:23.105 JupyterHub log:122] 302 GET /hub/logout → /hub/login
It means that my session tokens/cookies are not removed, hence no call to Keycloack to end up the session is done - so I actually still remain logged in. Am I missing anything in my jupyterhub_config.py file, should I implement my own logout handler or etc. ?
Keycloak has logout callback? Use logout callback to call /logout on jupyterhub
Made a keycloak authenticator a while ago: https://github.com/matipp/oauthenticator/commit/326d2617ce09749ca0c4d9a19e9651fe437e1755 but never had time to write tests for a PR, sorry
Finally, I got the integration working. Maybe a question for those who had a successful set up.
How can we restrict the access to jupyterhub after a successful login through keycloak?
I have been experementing with the Group function in kycloak, but it does not help.
The whitelist
in jupyterhub is not working.
Any information?
Thank you.
Hey @SofianeB, I stumbled upon the same problem a while ago and built something that works for us.
We are using a JupyterHub managed service that first checks the Keycloak API for users that are in a certain group (e.g. jupyterhub_user
). Afterwards, it checks whether this user is already in JupyterHub's whitelist via the Hub's API. If not, it creates this user. It also works the other way round, i.e. deleting users that are in the whitelist but not in the correct Keycloak group anymore.
This approach feels pretty hacky, but I also haven't found a neat way to use Keycloak's roles/groups with JupyterHub. If somebody has a better solution, I'm happy to see it!
Anyway, here's the code:
1) get_members_jupyterhub_user.sh
– Checks Keycloak for users in the jupyterhub_user
group.
#!/usr/bin/env bash
# Obtain a valid access token by passing the admin credentials
# (a token is valid just for 1 minute)
KC_TKN=`curl -s -k \
-d "client_id=admin-cli" \
-d "username=admin" \
-d "password=$KC_ADM_PASSWORD" \
-d "grant_type=password" \
"$KC_ADM_API" \
| jq -r ".access_token"`
if [ -z $KC_TKN ]; then echo "Warning: KC_TKN is empty"; fi
# Before getting the new usernames, put the old ones away in a
# seperate file. This is necessary as you might want to delete
# users that lost their right to access the JHub.
if [ -f "./jsons/jhub_user.json" ]; then
cp ./jsons/jhub_user.json ./jsons/OLD_jhub_user.json
fi
# Get the names of all users that are in the "jupyterhub_user" group
# and write it in a file named "jhub_user"
curl -s -k "$KC_ROOT/admin/realms/$KC_REALM/groups/$KC_GRP_ID/members" \
-H "Accept: application/json" \
-H "Authorization: Bearer $KC_TKN" | jq "." \
> ./jsons/jhub_user.json
2) update_whitelist.sh
– Checks if the users obtained in step 1) should be whitelisted or not.
#!/usr/bin/env bash
# Adding users to JupyterHub that are in KC's "jupyterhub_user" group
# (See doc: https://jupyterhub.readthedocs.io/en/latest/_static/rest-api/)
JHUB_TKN=$(cd /srv/jupyterhub && jupyterhub token admin)
if [ -z $JHUB_TKN ]; then echo "Warning: JHUB_TKN is empty"; fi
# Save the allowed users (retrieved by "get_members_jhub_user.sh")
# in the variable NEW
NEW=`cat ./jsons/jhub_user.json | jq -r ".[].username"`
# Also, save the "old" users from the get-script's previous fetch
# (if existent) in the variable OLD in order to delete users that are not in KC's
# "jhub_user" group anymore
if [[ -f "./jsons/OLD_jhub_user.json" ]]; then
OLD=`cat ./jsons/OLD_jhub_user.json | jq -r ".[].username"`
fi
# Put content of NEW and OLD (if existent)
# in array ARRNEW/ARROLD
read -ra ARRNEW -d '' <<<"$NEW"
if [[ ! -z " $OLD" ]]; then
read -ra ARROLD -d '' <<<"$OLD"
fi
# UNIQ is a variable that contains the usernames that differ
# between the old and the freshly fetched file.
# By UNIQ, check if a new user has to be created or if an old
# user should not be on JHub's whitelist anymore.
UNIQ=`echo ${ARRNEW[@]} ${ARROLD[@]} | tr ' ' '\n' | sort | uniq -u`
# Put UNIQ in array as well
read -ra UNIQ -d '' <<<"$UNIQ"
# If UNIQ is empty, do nothing and quit
if [[ "${#UNIQ[@]}" -eq 0 ]]; then
exit
fi
# For every user in the UNIQ array, check if he/she's in the
# ARRNEW (needs to be written to the whitelist) or not (needs to
# be deleted)
for user in "${UNIQ[@]}"
do
if [[ "${ARRNEW[@]}" =~ "$user" ]]; then
curl -s -X POST \
-H "Authorization: token $JHUB_TKN" \
-H "Accept: application/json" \
"$JHUB_API/users/$user" \
&& echo -e "\nCreated user $user"
else
curl -s -X DELETE \
-H "Authorization: token $JHUB_TKN" \
-H "Accept: application/json" \
"$JHUB_API/users/$user" \
&& echo -e "\Deleted user $user"
fi
done
3) init_updating.sh
– The actual service run by JupyterHub.
#!/bin/bash
# This script is automatically run by JupyterHub as a service.
# (See bottom of "jupyterhub_config.py")
# Set the working directory to the path where the script is
cd "${0%/*}"
# First, delete JSONs that might exist from previous JHub instances.
# This is done to replace users that might have been accidently
# deleted via JHubs's admin console.
# This will result in some warnings when you start the JupyterHub
# saying that users already exist. You can safely ignore these.
if [ ! -z "$(ls -A ./jsons)" ]; then
rm ./jsons/*
fi
# Sleep 10 secs to suppress initial warnings because of Keycloak
# which might not be ready to accept API connections yet.
sleep 10
# Check every 5 seconds if there are new users that have to
# be added to JHub's whitelist or old ones that have to be
# deleted.
while true; do
./get_members_jhub_user.sh
./update_whitelist.sh
sleep 5
done
4) Put the following in the jupyterhub_config.py
:
c.JupyterHub.services = [
{
'name': 'update-whitelist',
'admin': True,
'environment': {
'KC_ADM_PASSWORD': os.environ.get('KC_ADM_PASSWORD'),
'KC_ADM_API': os.environ.get('KC_ADM_API'),
'KC_ROOT': os.environ.get('KC_ROOT'),
'KC_REALM': os.environ.get('KC_REALM'),
'KC_GRP_ID': os.environ.get('KC_GRP_ID'),
'JHUB_API': os.environ.get('JHUB_API'),
},
'command': './services/init_updating.sh'.split()
}
]
Hope this helps!
You can create a user realm role mapper in the keycloak client, and enable auth_state. You can then override OIDCAuthenticator's authenticate method and check for the roles that you want to whitelist.
Hey @clkao, thanks for your answer, this does sound much more elegant indeed... Would you mind describing in greater detail how to achieve this? I and potentially billions of other people would really appreciate it. :slightly_smiling_face:
something like this? https://github.com/ausecocloud/keycloakauthenticator/blob/master/keycloakauthenticator/auth.py
Note, this code exploits the fact, that Keycloak issues JWT as access token.
Is there anyone dealing with this issue..? I want to ask your favor with 3 questions..
How can I logout by clicking jupyterhub logout button? (with cookie or session deleted)
How can I access directly to keycloak login page not seeing this page (url: [host]/hub/login)
How can I apply @gweis 's extension solution..?
see https://jupyterhub.readthedocs.io/en/stable/api/auth.html
c.Authenticator.auto_login = Bool(False)
setting this option to True will skip the extra login page.
Thanks for responding back to me. I have already set c.Authenticator.auto_login=True.
Anyhow, we finally escalate this to our internal Okta expert yesterday and it turns out the c.GenericOAuthenticator.userdata_url = 'https://$OktaServer/oauth2/v1/clients' they provided us was incorrect. It looks that generic.py forces Jupyterhub to call the userdata_endpoint (they said that is usually an optional step) and the correct userdata end point should be the my company’s Okta userinfo_endpoint which has a special string between oath2 and v1.
Thanks, Shawn
From: gweis [mailto:notifications@github.com] Sent: Tuesday, February 19, 2019 4:04 PM To: jupyterhub/oauthenticator oauthenticator@noreply.github.com Cc: Subscribed subscribed@noreply.github.com Subject: Re: [jupyterhub/oauthenticator] Working configuration for generic authenticator with Keycloak (#107)
see https://jupyterhub.readthedocs.io/en/stable/api/auth.html
c.Authenticator.auto_login = Bool(False)
setting this option to True will skip the extra login page.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHubhttps://github.com/jupyterhub/oauthenticator/issues/107#issuecomment-465363024, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AtO3fGl04BaKam1fG3pAdlfWiQf-XonKks5vPJDwgaJpZM4O7ZQg.
Error 302 i am not hitting keycloak login
For OAuthenticator generic integration we need to add at jupyterhub_config.py: c.JupyterHub.authenticator_class = 'oauthenticator.generic.GenericOAuthenticator' c.OAuthenticator.client_id # oauth2 client id for your app c.OAuthenticator.client_secret # oauth2 client secret for your app c.GenericOAuthenticator.token_url # oauth2 provider's token url c.GenericOAuthenticator.userdata_url # oauth2 provider's endpoint with user data c.GenericOAuthenticator.userdata_method # method used to request user data endpoint c.GenericOAuthenticator.userdata_params # params to send for userdata endpoint c.GenericOAuthenticator.username_key # username key from json returned from user data endpoint
And export these environment variables: OAUTH2_TOKEN_URL # the same as token_url param OAUTH2_AUTHORIZE_URL # oauth2 provider's authorization andpoint
For more details I'll show how to integrate with generic authentication in https://conferences.oreilly.com/jupyter/jup-ny/public/schedule/detail/59400
If I'm doing this in docker container, then the environment variables should be set using Dockerfile right?
For OAuthenticator generic integration we need to add at jupyterhub_config.py: c.JupyterHub.authenticator_class = 'oauthenticator.generic.GenericOAuthenticator' c.OAuthenticator.client_id # oauth2 client id for your app c.OAuthenticator.client_secret # oauth2 client secret for your app c.GenericOAuthenticator.token_url # oauth2 provider's token url c.GenericOAuthenticator.userdata_url # oauth2 provider's endpoint with user data c.GenericOAuthenticator.userdata_method # method used to request user data endpoint c.GenericOAuthenticator.userdata_params # params to send for userdata endpoint c.GenericOAuthenticator.username_key # username key from json returned from user data endpoint And export these environment variables: OAUTH2_TOKEN_URL # the same as token_url param OAUTH2_AUTHORIZE_URL # oauth2 provider's authorization andpoint For more details I'll show how to integrate with generic authentication in https://conferences.oreilly.com/jupyter/jup-ny/public/schedule/detail/59400
If I'm doing this in docker container, then the environment variables should be set using Dockerfile right?
Yes, but how is your infrastructure?
For OAuthenticator generic integration we need to add at jupyterhub_config.py: c.JupyterHub.authenticator_class = 'oauthenticator.generic.GenericOAuthenticator' c.OAuthenticator.client_id # oauth2 client id for your app c.OAuthenticator.client_secret # oauth2 client secret for your app c.GenericOAuthenticator.token_url # oauth2 provider's token url c.GenericOAuthenticator.userdata_url # oauth2 provider's endpoint with user data c.GenericOAuthenticator.userdata_method # method used to request user data endpoint c.GenericOAuthenticator.userdata_params # params to send for userdata endpoint c.GenericOAuthenticator.username_key # username key from json returned from user data endpoint And export these environment variables: OAUTH2_TOKEN_URL # the same as token_url param OAUTH2_AUTHORIZE_URL # oauth2 provider's authorization andpoint For more details I'll show how to integrate with generic authentication in https://conferences.oreilly.com/jupyter/jup-ny/public/schedule/detail/59400
If I'm doing this in docker container, then the environment variables should be set using Dockerfile right?
Yes, but how is your infrastructure?
Hey! How can I set this environment variables? Using Dockerfile?
Or in Dockerfile with ENV OAUTH2_TOKEN_URL https://YOUR_URL
or using docker command docker run -e OAUTH2_TOKEN_URL='https://YOUR_URL'
Thanks @dmvieira . Actually, I'm little confused regarding the setup of authentication with jupyterhub and my on-premise Oauth service.
Do I just need to add the variables mentioned above along with env variables?
No where redirect uri is mentioned? So, is it of no use here?
If I exclude the userdata variables such as params and key and url. Will this work?
My service gives me resource URI and client ID but not client secret as github oauth gives. How can I get that?
Can you please help me out with this? Thanks
Getting the same issue as @pfisterer after I configured Jupyterhub with Keycloak. The login mechanism is working fine, however the logout button always redirect to login URI (/hub/logout -> /hub/login) and then on clicking "Sign in using Keycloak" it directly opens home page without any authentication. I found that the keycloak logout API is not getting called from GenericOauthenticator & that's why the SSO user session is not getting terminated. keycloak logout URL :https://{ip}:{port}/auth/realms/jhub_realm/protocol/openid-connect/logout?redirect_uri= OAuthenticator configuration in jupyterhub_config.py: from oauthenticator.generic import GenericOAuthenticator c.JupyterHub.authenticator_class = GenericOAuthenticator c.GenericOAuthenticator.login_service = 'keycloak' c.GenericOAuthenticator.userdata_params = {"state": "state"} c.GenericOAuthenticator.auto_login = False c.Authenticator.auto_login = False
and exporter this env variables,
export OAUTH2_AUTHORIZE_URL=https://{ip}:{port}/auth/realms/jhub_realm/protocol/openid-connect/auth export OAUTH2_TOKEN_URL=https://{ip}:{port}/auth/realms/jhub_realm/protocol/openid-connect/token export OAUTH_CLIENT_SECRET=***** export OAUTH_CALLBACK_URL=http://{ip}:{port}/hub/oauth_callback export OAUTH2_USERDATA_URL=https://{ip}:{port}/auth/realms/jhub_realm/protocol/openid-connect/userinfo export OAUTH_CLIENT_ID=jupyter-hub export OAUTH2_USERNAME_KEY=preferred_username
Can anyone help me regarding this, how to call keycloak user session end point from jupyterhub GenericOauthenticator ?????
This issue has been mentioned on Jupyter Community Forum. There might be relevant details there:
https://discourse.jupyter.org/t/generic-oauthenticator-redirect-issue/4078/5
Hi, I am also facing an issue with logging out. Once I sign in I am unable to log out unless I go to keycloak and end the session. Only then do I get prompted to enter the username and password again. I am using the juyterhub helm chart and have the following values which work except that I can not logout. Can anyone help identify what I might be missing? Thanks.
hub:
extraEnv:
OAUTH2_AUTHORIZE_URL: https://${host}/auth/realms/${realm}/protocol/openid-connect/auth
OAUTH2_TOKEN_URL: https://${host}/auth/realms/${realm}/protocol/openid-connect/token
OAUTH_CALLBACK_URL: https://<your_jupyterhub_host>/hub/oauth_callback
OAUTH2_TLS_VERIFY: false
auth:
type: custom
custom:
className: oauthenticator.generic.GenericOAuthenticator
config:
login_service: "Keycloak"
client_id: "XXX"
client_secret: "XXX"
token_url: https://${host}/auth/realms/${realm}/protocol/openid-connect/token
userdata_url: https://${host}/auth/realms/${realm}/protocol/openid-connect/userinfo
userdata_method: GET
userdata_params: {'state': 'state'}
username_key: preferred_username
If that can help, I got logout with keycloak working with oauthenticator 0.11.0 with the following code:
An authenticator with a LogoutHandler
since the default one does not handles logout against authentication service:
from traitlets import Unicode
from oauthenticator.generic import GenericOAuthenticator
from jupyterhub.utils import url_path_join
from jupyterhub.handlers.login import LogoutHandler
from tornado.httputil import url_concat
class KeycloakLogoutHandler(LogoutHandler):
"""Logout handler for keycloak"""
async def render_logout_page(self):
params = {
'redirect_uri': '%s://%s%s' % (
self.request.protocol,
self.request.host,
self.hub.server.base_url),
}
self.redirect(
url_concat(self.authenticator.keycloak_logout_url, params),
permanent=False
)
class KeycloakAuthenticator(GenericOAuthenticator):
"""Authenticator handling keycloak logout"""
keycloak_logout_url = Unicode(
config=True,
help="The keycloak logout URL"
)
def get_handlers(self, app):
return super().get_handlers(app) + [(r'/logout', KeycloakLogoutHandler)]
And then use the authenticator and logout URL configuration:
c.JupyterHub.authenticator_class = KeycloakAuthenticator
c.KeycloakAuthenticator.keycloak_logout_url = "https://example.com/auth/realms/REALM/protocol/openid-connect/logout"
I don't know how much that can be generalized, but when using keycloak that would be great to have the logout handled by default.
@t20100 Thank you! Really appreciate it. The code helped me understand what I needed to do and I was able to use it in the helm chart for jupyterhub.
Hi, I'm closing this as it seems to have ended up covering several issues. There's a recent example of a working config on the Jupyter community forum: https://discourse.jupyter.org/t/configuring-zero-to-jupyter-hub-to-use-an-arbitrary-oauth2-provider/5542/4
If anyone has a concrete idea for improving the docs please open a PR 😀
All those advices helped me a lot, but when I try to use OAuth 2.0 I get this message from keycloak:
We are sorry... Invalid parameter: redirect_uri
And this is the log from keyclock server:
2020-10-29 10:34:58,025 WARN [org.keycloak.events] (default task-5) type=LOGIN_ERROR, realmId=master, clientId=account, userId=null, ipAddress=10.0.2.2, error=invalid_redirect_uri, redirect_uri=http://192.168.100.1:8600/hub/oauth_callback
jupyterhub_config.py:
` host_ip = '192.168.100.1' ip = host_ip keycloak_guest_port = '8180' keycloak_host_port = '8586' keycloak_port = keycloak_host_port c.JupyterHub.authenticator_class = GenericOAuthenticator
c.OAuthenticator.client_id = 'account'
c.OAuthenticator.client_secret = '5110687d-5c91-406d-8721-2f09b11e21fe'
c.GenericOAuthenticator.token_url = "http://" + ip + ":" +keycloak_port + "/auth/realms/master/protocol/openid-connect/token"
c.GenericOAuthenticator.userdata_url = "http://" + ip + ":" +keycloak_port + "/auth/realms/master/protocol/openid-connect/userinfo"
c.GenericOAuthenticator.userdata_method = 'GET'
c.GenericOAuthenticator.userdata_params = {'state': 'state'}
c.GenericOAuthenticator.username_key = 'admin' `
Enviromental variables:
export OAUTH2_TOKEN_URL=http://192.168.100.1:8586/auth/realms/master/protocol/openid-connect/token export OAUTH2_AUTHORIZE_URL=http://192.168.100.1:8586/auth/realms/master/protocol/openid-connect/auth export OAUTH_CALLBACK_URL=http://192.168.100.1:8600/hub/oauth_callback
Ports: ` config.vm.network "forwarded_port", guest: 8000, host: 8600
config.vm.network "forwarded_port", guest: 8001, host: 8601
config.vm.network "forwarded_port", guest: 8081, host: 8681
config.vm.network "forwarded_port", guest: 8180, host: 8586 `
As you can probably see, I'm not using docker, but Vagrant. Nothing I've found helped me solve this issue. Edit: I had to configure the keycloak client
@t20100 What is that Unicode() function? I can't find a unicode function with config param, and the logout does not work when I replace it with a string.
It looks like from traitlets import Unicode
was missing, I've added it to the sample code.
Are you sure that this is everything? It does not seem to be working:
[E 2021-02-03 17:30:44.074 JupyterHub app:2859]
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/jupyterhub/app.py", line 2856, in launch_instance_async
await self.initialize(argv)
File "/usr/local/lib/python3.6/site-packages/jupyterhub/app.py", line 2347, in initialize
self.load_config_file(self.config_file)
File "<decorator-gen-5>", line 2, in load_config_file
File "/usr/local/lib/python3.6/site-packages/traitlets/config/application.py", line 87, in catch_config_error
return method(app, *args, **kwargs)
File "/usr/local/lib/python3.6/site-packages/traitlets/config/application.py", line 602, in load_config_file
raise_config_file_errors=self.raise_config_file_errors,
File "/usr/local/lib/python3.6/site-packages/traitlets/config/application.py", line 563, in _load_config_files
config = loader.load_config()
File "/usr/local/lib/python3.6/site-packages/traitlets/config/loader.py", line 457, in load_config
self._read_file_as_dict()
File "/usr/local/lib/python3.6/site-packages/traitlets/config/loader.py", line 489, in _read_file_as_dict
py3compat.execfile(conf_filename, namespace)
File "/usr/local/lib/python3.6/site-packages/ipython_genutils/py3compat.py", line 198, in execfile
exec(compiler(f.read(), fname, 'exec'), glob, loc)
File "/vagrant/modules/jupyterhub/jupyterhub_config.py", line 36, in <module>
class KeycloakAuthenticator(GenericOAuthenticator):
File "/vagrant/modules/jupyterhub/jupyterhub_config.py", line 40, in KeycloakAuthenticator
help="The keycloak logout URL"
TypeError: 'config' is an invalid keyword argument for this function
In my jupyterhub_config.py i put the below settings to resolve the redirect issue
import os
os.environ['OAUTH2_TOKEN_URL'] = 'https://{domain}:{port}/auth/realms/{realm}/protocol/openid-connect/token'
os.environ['OAUTH2_AUTHORIZE_URL'] = 'https://{domain}:{port}/auth/realms/{realm}/protocol/openid-connect/auth'
os.environ['OAUTH2_USERDATA_URL'] = 'https://{domain}:{port}/auth/realms/{realm}/protocol/openid-connect/userinfo'
from oauthenticator.generic import GenericOAuthenticator
c.JupyterHub.authenticator_class = 'oauthenticator.generic.GenericOAuthenticator'
c.GenericOAuthenticator.login_service = 'Keycloak'
c.OAuthenticator.client_id = '{client_id}'
c.OAuthenticator.client_secret = '{client_secret}'
c.GenericOAuthenticator.oauth_callback_url = 'https://{hub}/jupyter/hub/oauth_callback'
c.GenericOAuthenticator.userdata_url = 'https://{domain}:{port}/auth/realms/{realm}/protocol/openid-connect/userinfo'
c.GenericOAuthenticator.token_url = 'https://{domain}:{port}/auth/realms/{realm}/protocol/openid-connect/token'
c.GenericOAuthenticator.userdata_method = 'GET'
c.GenericOAuthenticator.userdata_params = {"state": "state"}
c.GenericOAuthenticator.username_key = "preferred_username"
However i after successfull authentication from keycloak i got SSL error below
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:877)
to temporary resolve it i add this settings
c.OAuthenticator.tls_verify = False
hope this helps.
Hi, I am also facing an issue with logging out. Once I sign in I am unable to log out unless I go to keycloak and end the session. Only then do I get prompted to enter the username and password again. I am using the juyterhub helm chart and have the following values which work except that I can not logout. Can anyone help identify what I might be missing? Thanks.
hub: extraEnv: OAUTH2_AUTHORIZE_URL: https://${host}/auth/realms/${realm}/protocol/openid-connect/auth OAUTH2_TOKEN_URL: https://${host}/auth/realms/${realm}/protocol/openid-connect/token OAUTH_CALLBACK_URL: https://<your_jupyterhub_host>/hub/oauth_callback OAUTH2_TLS_VERIFY: false auth: type: custom custom: className: oauthenticator.generic.GenericOAuthenticator config: login_service: "Keycloak" client_id: "XXX" client_secret: "XXX" token_url: https://${host}/auth/realms/${realm}/protocol/openid-connect/token userdata_url: https://${host}/auth/realms/${realm}/protocol/openid-connect/userinfo userdata_method: GET userdata_params: {'state': 'state'} username_key: preferred_username
using you cool exmaple it worked like a charm, but logout is not working :( Did you find the solution?
Hi everyone,
I am using the generic authenticator to integrate jupyterhub with an external OIDC provider. When I press the login button I will be redirected to the OIDC provider login page. When I log in using my credentials, I don't redirect back to the jupyterhub and I will stay on my page on the OIDC provider side. Any hint to tackle this issue? This is the part of my config file related to authentication :
c = get_config()
import os
# use Generic OAuthenticator for local users
from oauthenticator.generic import GenericOAuthenticator
c.Application.log_level = 'DEBUG'
c.JupyterHub.authenticator_class = GenericOAuthenticator
c.GenericOAuthenticator.client_id = os.environ['IAM_CLIENT_ID']
c.GenericOAuthenticator.client_secret = os.environ['IAM_CLIENT_SECRET']
c.GenericOAuthenticator.token_url = os.environ['OAUTH2_TOKEN_URL']
c.GenericOAuthenticator.userdata_method = 'GET'
c.GenericOAuthenticator.userdata_params = {"state": "state"}
c.GenericOAuthenticator.login_service = 'ESCAPE IAM'
c.GenericOAuthenticator.oauth_callback_url = os.environ['OAUTH_CALLBACK_URL']
i am able to integrate keycloak with jupyterhub but still not able to figure it out how can we restrict users with particular keycloak role to access jupyterhub. Any leads would be appreciated.
Hey @clkao, thanks for your answer, this does sound much more elegant indeed... Would you mind describing in greater detail how to achieve this? I and potentially billions of other people would really appreciate it. 🙂
Hi, could you please let me know if you have got the solution ?
i am able to integrate keycloak with jupyterhub but still not able to figure it out how can we restrict users with particular keycloak role to access jupyterhub. Any leads would be appreciated.
me too, very important feature
Hello @navdeepbansal - are you able to post the solution?
As for group restriction - have you seen this?
https://github.com/jupyterhub/oauthenticator/tree/main/examples/generic - there is a section at the bottom about groups
I have tried to configure Jupyterhub to use the generic OAuth2 authentication mechanism with Keycloak as OAuth2 sever. However, it never redirects to it for authentication. Could you please support me and provide a sample configuration that is supposed to work? Or is there anything that I'm obviously doing wrong?
Thank you very much! Dennis
OAuthenticator configuration in jupyterhub_config.py:
Keycloak Endpoints: