-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathoauthenticator.py
134 lines (103 loc) · 3.84 KB
/
oauthenticator.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
"""
Custom Authenticator to use Google OAuth with JupyterHub.
Derived from GitHub OAuth authenticator,
https://github.com/jupyter/oauthenticator
"""
import os
import binascii
import json
from tornado import gen
from tornado.auth import GoogleOAuth2Mixin
from tornado.escape import to_unicode
from tornado.web import HTTPError
from IPython.utils.traitlets import Unicode
from jupyterhub.handlers import BaseHandler
from jupyterhub.auth import Authenticator, LocalAuthenticator
from jupyterhub.utils import url_path_join
class GoogleLoginHandler(BaseHandler, GoogleOAuth2Mixin):
def get(self):
guess_uri = '{proto}://{host}{path}'.format(
proto=self.request.protocol,
host=self.request.host,
path=url_path_join(self.hub.server.base_url, 'oauth_callback')
)
redirect_uri = self.authenticator.oauth_callback_url or guess_uri
self.log.info('oauth redirect: %r', redirect_uri)
self.authorize_redirect(
redirect_uri=redirect_uri,
client_id=self.authenticator.oauth_client_id,
scope=['openid', 'email'],
response_type='code')
class GoogleOAuthHandler(BaseHandler, GoogleOAuth2Mixin):
@gen.coroutine
def get(self):
self.settings['google_oauth'] = {
'key': self.authenticator.oauth_client_id,
'secret': self.authenticator.oauth_client_secret,
'scope': ['openid', 'email']
}
# TODO: Check if state argument needs to be checked
#state = to_unicode(self.get_secure_cookie('openid_state'))
#if not state == self.get_argument('state', False):
# raise HTTPError(400, "Invalid state")
username = yield self.authenticator.authenticate(self)
self.log.info('GOOGLEAPPS: username: "%s"', username)
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 HTTPError(403)
class GoogleOAuthenticator(Authenticator):
login_service = "Google"
ACCESS_TOKEN_URL = 'https://www.googleapis.com/oauth2/v1/userinfo'
oauth_callback_url = Unicode('', config=True)
oauth_client_id = Unicode(os.environ.get('OAUTH_CLIENT_ID', ''),
config=True)
oauth_client_secret = Unicode(os.environ.get('OAUTH_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', GoogleLoginHandler),
(r'/oauth2callback', GoogleOAuthHandler),
]
@gen.coroutine
def authenticate(self, handler):
code = handler.get_argument('code', False)
if not code:
raise HTTPError(400, "oauth callback made without a token")
user = yield handler.get_authenticated_user(
redirect_uri=self.oauth_callback_url,
code=code)
access_token = str(user['access_token'])
http_client = handler.get_auth_http_client()
response = yield http_client.fetch(
self.ACCESS_TOKEN_URL + '?access_token=' + access_token
)
if not response:
self.clear_all_cookies()
raise HTTPError(500, 'Google authentication failed')
bodyjs = json.loads(response.body.decode())
username = bodyjs['email']
raise gen.Return(username)
class GoogleAppsOAuthenticator(GoogleOAuthenticator):
hosted_domain = Unicode(os.environ.get('HOSTED_DOMAIN', ''), config=True)
@gen.coroutine
def authenticate(self, handler):
username = yield GoogleOAuthenticator.authenticate(self, handler)
if not username or not username.endswith('@'+self.hosted_domain):
username = None
else:
username = username.split('@')[0]
if self.whitelist and username not in self.whitelist:
username = None
raise gen.Return(username)
class LocalGoogleOAuthenticator(LocalAuthenticator, GoogleOAuthenticator):
"""A version that mixes in local system user creation"""
pass
class LocalGoogleAppsOAuthenticator(LocalAuthenticator, GoogleAppsOAuthenticator):
"""A version that mixes in local system user creation"""
pass