Skip to content

Commit

Permalink
adapt OAuth toy into JupyterHub Authenticator
Browse files Browse the repository at this point in the history
  • Loading branch information
minrk committed Sep 23, 2014
1 parent 90d15c4 commit 71a0f79
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 130 deletions.
2 changes: 1 addition & 1 deletion LICENSE
@@ -1,4 +1,4 @@
Copyright (c) 2014, Kyle Kelley
Copyright (c) 2014, Jupyter Developers
All rights reserved.

Redistribution and use in source and binary forms, with or without
Expand Down
127 changes: 127 additions & 0 deletions oauthenticator.py
@@ -0,0 +1,127 @@
"""
Custom Authenticator to use GitHub OAuth with JupyterHub
Most of the code c/o Kyle Kelley (@rgbkrk)
"""


import json
import os

from tornado.auth import OAuth2Mixin
from tornado import gen, web

from tornado.httputil import url_concat
from tornado.httpclient import HTTPRequest, AsyncHTTPClient

from jupyterhub.handlers import BaseHandler
from jupyterhub.auth import Authenticator
from jupyterhub.utils import url_path_join

from IPython.utils.traitlets import Unicode

class GitHubMixin(OAuth2Mixin):
_OAUTH_AUTHORIZE_URL = "https://github.com/login/oauth/authorize"
_OAUTH_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token"


class GitHubLoginHandler(BaseHandler, GitHubMixin):
def get(self):
redirect_uri='{proto}://{host}{path}'.format(
proto=self.request.protocol,
host=self.request.host,
path=url_path_join(
self.hub.server.base_url,
'oauth_callback'
),
)
self.log.warn('oauth redirect: %r', redirect_uri)

self.authorize_redirect(
redirect_uri=redirect_uri,
client_id=self.authenticator.github_client_id,
scope=[],
response_type='code')


class GitHubOAuthHandler(BaseHandler):
@gen.coroutine
def get(self):
# TODO: Check if state argument needs to be checked
username = yield self.authenticator.authenticate(self)
if username:
user = self.user_from_username(username)
self.set_login_cookie(user)
self.redirect(url_path_join(self.hub.server.base_url, 'home'))
else:
# todo: custom error page?
raise web.HTTPError(403)


class GitHubOAuthenticator(Authenticator):

github_client_id = Unicode(os.environ.get('GITHUB_CLIENT_ID', ''),
config=True)
github_client_secret = Unicode(os.environ.get('GITHUB_CLIENT_SECRET', ''),
config=True)

def login_url(self, base_url):
return url_path_join(base_url, 'oauth_login')

def get_handlers(self, app):
return [
(r'/oauth_login', GitHubLoginHandler),
(r'/oauth_callback', GitHubOAuthHandler),
]

@gen.coroutine
def authenticate(self, handler):
code = handler.get_argument("code", False)
if not code:
raise web.HTTPError(400, "oauth callback made without a token")
# TODO: Configure the curl_httpclient for tornado
http_client = AsyncHTTPClient()

# Exchange the OAuth code for a GitHub Access Token
#
# See: https://developer.github.com/v3/oauth/

# GitHub specifies a POST request yet requires URL parameters
params = dict(
client_id=self.github_client_id,
client_secret=self.github_client_secret,
code=code
)

url = url_concat("https://github.com/login/oauth/access_token",
params)

req = HTTPRequest(url,
method="POST",
headers={"Accept": "application/json"},
body='' # Body is required for a POST...
)

resp = yield http_client.fetch(req)
resp_json = json.loads(resp.body.decode('utf8', 'replace'))

access_token = resp_json['access_token']

# Determine who the logged in user is
headers={"Accept": "application/json",
"User-Agent": "JupyterHub",
"Authorization": "token {}".format(access_token)
}
req = HTTPRequest("https://api.github.com/user",
method="GET",
headers=headers
)
resp = yield http_client.fetch(req)
resp_json = json.loads(resp.body.decode('utf8', 'replace'))

username = resp_json["login"]
if self.whitelist and username not in self.whitelist:
username = None
raise gen.Return(username)


129 changes: 0 additions & 129 deletions toy.py

This file was deleted.

0 comments on commit 71a0f79

Please sign in to comment.