mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-07 18:14:10 +00:00
add external oauth example
This commit is contained in:
74
examples/external-oauth/README.md
Normal file
74
examples/external-oauth/README.md
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# Using JupyterHub as an OAuth provider
|
||||||
|
|
||||||
|
JupyterHub 0.9 introduces
|
||||||
|
Uses `jupyterhub.services.HubAuthenticated` to authenticate requests with the Hub.
|
||||||
|
|
||||||
|
There is an implementation each of cookie-based `HubAuthenticated` and OAuth-based `HubOAuthenticated`.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
1. generate an API token:
|
||||||
|
|
||||||
|
export JUPYTERHUB_API_TOKEN=`openssl rand -hex 32`
|
||||||
|
|
||||||
|
2. launch the whoami service:
|
||||||
|
|
||||||
|
bash launch-service.sh &
|
||||||
|
|
||||||
|
3. Launch JupyterHub:
|
||||||
|
|
||||||
|
4. Visit http://127.0.0.1:5555/
|
||||||
|
|
||||||
|
After logging in with your local-system credentials, you should see a JSON dump of your user info:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"admin": false,
|
||||||
|
"last_activity": "2016-05-27T14:05:18.016372",
|
||||||
|
"name": "queequeg",
|
||||||
|
"pending": null,
|
||||||
|
"server": "/user/queequeg"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
The essential pieces for using JupyterHub as an OAuth provider are:
|
||||||
|
|
||||||
|
1. registering your service with jupyterhub:
|
||||||
|
|
||||||
|
```python
|
||||||
|
c.JupyterHub.services = [
|
||||||
|
{
|
||||||
|
# the name of your service
|
||||||
|
# should be simple and unique.
|
||||||
|
# mostly used to identify your service in logging
|
||||||
|
"name": "my-service",
|
||||||
|
# the oauth client id of your service
|
||||||
|
# must be unique but isn't private
|
||||||
|
# can be randomly generated or hand-written
|
||||||
|
"oauth_client_id": "abc123",
|
||||||
|
# the API token and client secret of the service
|
||||||
|
# should be generated securely,
|
||||||
|
# e.g. via `openssl rand -hex 32`
|
||||||
|
"api_token": "abc123...",
|
||||||
|
# the redirect target for jupyterhub to send users
|
||||||
|
# after successful authentication
|
||||||
|
"oauth_redirect_uri": "https://service-host/oauth_callback"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Telling your service how to authenticate with JupyterHub.
|
||||||
|
|
||||||
|
The relevant OAuth URLs for working with JupyterHub are:
|
||||||
|
|
||||||
|
1. the client_id, used in oauth requests
|
||||||
|
2. the api token registered with jupyterhub is the client_secret for oauth requests
|
||||||
|
3. oauth url of the Hub, which is "/hub/api/oauth2/authorize", e.g. `https://myhub.horse/hub/api/oauth2/authorize`
|
||||||
|
4. a redirect handler to receive the authenticated response
|
||||||
|
(at `oauth_redirect_uri` registered in jupyterhub config)
|
||||||
|
5. the token URL for completing the oauth process is "/hub/api/oauth2/token",
|
||||||
|
e.g. `https://myhub.horse/hub/api/oauth2/token`.
|
||||||
|
The reply is JSON and the token is in the field `access_token`.
|
||||||
|
6. Users can be identified by oauth token by making a request to `/hub/api/user`
|
||||||
|
with the new token in the `Authorization` header.
|
17
examples/external-oauth/jupyterhub_config.py
Normal file
17
examples/external-oauth/jupyterhub_config.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
# get the oauth client's API token.
|
||||||
|
# this could come from anywhere
|
||||||
|
api_token = os.getenv("JUPYTERHUB_API_TOKEN")
|
||||||
|
if not api_token:
|
||||||
|
raise ValueError("Make sure to `export JUPYTERHUB_API_TOKEN=$(openssl rand -hex 32)`")
|
||||||
|
|
||||||
|
# tell JupyterHub to register the service as an external oauth client
|
||||||
|
c.JupyterHub.services = [
|
||||||
|
{
|
||||||
|
'name': 'external-oauth',
|
||||||
|
'oauth_client_id': "whoami-oauth-client-test",
|
||||||
|
'api_token': api_token,
|
||||||
|
'oauth_redirect_uri': 'http://127.0.0.1:5555/oauth_callback',
|
||||||
|
},
|
||||||
|
]
|
20
examples/external-oauth/launch-service.sh
Normal file
20
examples/external-oauth/launch-service.sh
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# the service needs to know:
|
||||||
|
# 1. API token
|
||||||
|
if [[ -z "${JUPYTERHUB_API_TOKEN}" ]]; then
|
||||||
|
echo 'set API token with export JUPYTERHUB_API_TOKEN=$(openssl rand -hex 32)'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. oauth client ID
|
||||||
|
export JUPYTERHUB_CLIENT_ID="whoami-oauth-client-test"
|
||||||
|
# 3. what URL to run on
|
||||||
|
export JUPYTERHUB_SERVICE_PREFIX='/'
|
||||||
|
export JUPYTERHUB_SERVICE_URL='http://127.0.0.1:5555'
|
||||||
|
export JUPYTERHUB_OAUTH_CALLBACK_URL="$JUPYTERHUB_SERVICE_URL/oauth_callback"
|
||||||
|
# 4. where the Hub is
|
||||||
|
export JUPYTERHUB_HOST='http://127.0.0.1:8000'
|
||||||
|
|
||||||
|
# launch the service
|
||||||
|
exec python3 whoami-oauth.py
|
46
examples/external-oauth/whoami-oauth.py
Normal file
46
examples/external-oauth/whoami-oauth.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
"""An example service authenticating with the Hub.
|
||||||
|
|
||||||
|
This example service serves `/services/whoami/`,
|
||||||
|
authenticated with the Hub,
|
||||||
|
showing the user their own info.
|
||||||
|
"""
|
||||||
|
from getpass import getuser
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
from tornado.ioloop import IOLoop
|
||||||
|
from tornado import log
|
||||||
|
from tornado.httpserver import HTTPServer
|
||||||
|
from tornado.web import RequestHandler, Application, authenticated
|
||||||
|
|
||||||
|
from jupyterhub.services.auth import HubOAuthenticated, HubOAuthCallbackHandler
|
||||||
|
from jupyterhub.utils import url_path_join
|
||||||
|
|
||||||
|
class WhoAmIHandler(HubOAuthenticated, RequestHandler):
|
||||||
|
hub_users = {getuser()} # the users allowed to access this service
|
||||||
|
|
||||||
|
@authenticated
|
||||||
|
def get(self):
|
||||||
|
user_model = self.get_current_user()
|
||||||
|
self.set_header('content-type', 'application/json')
|
||||||
|
self.write(json.dumps(user_model, indent=1, sort_keys=True))
|
||||||
|
|
||||||
|
def main():
|
||||||
|
log.enable_pretty_logging()
|
||||||
|
app = Application([
|
||||||
|
(os.environ['JUPYTERHUB_SERVICE_PREFIX'], WhoAmIHandler),
|
||||||
|
(url_path_join(os.environ['JUPYTERHUB_SERVICE_PREFIX'], 'oauth_callback'), HubOAuthCallbackHandler),
|
||||||
|
(r'.*', WhoAmIHandler),
|
||||||
|
], cookie_secret=os.urandom(32))
|
||||||
|
|
||||||
|
http_server = HTTPServer(app)
|
||||||
|
url = urlparse(os.environ['JUPYTERHUB_SERVICE_URL'])
|
||||||
|
log.app_log.info("Running whoami service on %s", os.environ['JUPYTERHUB_SERVICE_URL'])
|
||||||
|
|
||||||
|
http_server.listen(url.port, url.hostname)
|
||||||
|
|
||||||
|
IOLoop.current().start()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
BIN
examples/external-oauth/whoami.png
Normal file
BIN
examples/external-oauth/whoami.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
Reference in New Issue
Block a user