Created
June 20, 2018 05:59
-
-
Save ammaraskar/e259bc7d50df354e796e3c0d61137b10 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff -r f40f48714e04 extensions/oic_login.py | |
--- a/extensions/oic_login.py Fri Jun 15 20:25:08 2018 +0200 | |
+++ b/extensions/oic_login.py Wed Jun 20 01:52:00 2018 -0400 | |
@@ -11,6 +11,7 @@ | |
from roundup.cgi.actions import Action | |
from roundup.cgi.exceptions import * | |
from roundup import password, hyperdb | |
+import logging | |
try: | |
from oic.oauth2 import rndstr | |
@@ -65,28 +66,65 @@ | |
res.append(self.db.oic_session.get('sid')) | |
return res | |
+PROVIDER_GOOGLE = 'Google' | |
+PROVIDER_GITHUB = 'Github' | |
+PROVIDER_URL_MAP = { | |
+ 'Github': 'https://github.com/settings/developers', | |
+ 'Google': 'https://accounts.google.com' | |
+} | |
+ | |
+def select_provider(form): | |
+ if 'provider' in form: | |
+ return form['provider'].value | |
+ else: | |
+ return PROVIDER_GOOGLE | |
+ | |
class OICMixin: | |
- # XXX this somehow needs to be generalized if more OIC provider are supported | |
- provider = "https://accounts.google.com" | |
- def init_oic(self): | |
- self.scopes = ["openid","profile","email"] | |
+ def init_oic(self, provider_name): | |
+ assert provider_name in PROVIDER_URL_MAP | |
+ provider = PROVIDER_URL_MAP[provider_name] | |
+ logging.debug("init_oic: {}".format(provider)) | |
+ | |
+ self.scopes = ["openid", "profile", "email"] | |
db = SessionStore(self.db) | |
client = Consumer(db, consumer_config, client_config=client_config) | |
client.allow['issuer_mismatch'] = True | |
- client.provider_info = client.provider_config(self.provider) | |
- providers = self.db.oic_registration.filter(None, {'issuer':self.provider}) | |
+ | |
+ # Github does not support dynamically resolving open id configuration | |
+ if provider_name == PROVIDER_GITHUB: | |
+ self.scopes = ["user:email", "read:user"] | |
+ client.provider_info = { | |
+ 'authorization_endpoint': 'https://github.com/login/oauth/authorize', | |
+ 'token_endpoint': 'https://github.com/login/oauth/access_token' | |
+ } | |
+ client.handle_provider_config(client.provider_info, 'Github') | |
+ else: | |
+ client.provider_info = client.provider_config(provider) | |
+ | |
+ providers = self.db.oic_registration.filter(None, {'issuer': provider}) | |
assert len(providers) == 1 | |
provider = self.db.oic_registration.getnode(providers[0]) | |
+ | |
client_reg = RegistrationResponse(client_id=provider['client_id'], | |
client_secret=provider['client_secret']) | |
client.store_registration_info(client_reg) | |
return client | |
+ def redirect_uri(self, provider): | |
+ redirect_uri = self.base + 'index?@action=oic_authresp' | |
+ # Avoid breaking existing google callback urls which will not have | |
+ # provider tagged along | |
+ if provider != PROVIDER_GOOGLE: | |
+ redirect_uri += ('&provider=' + provider) | |
+ return redirect_uri | |
class OICLogin(Action, OICMixin): | |
def handle(self): | |
- client = self.init_oic() | |
- client.redirect_uris=[self.base+'index?@action=oic_authresp'] | |
+ provider = select_provider(self.client.form) | |
+ client = self.init_oic(provider) | |
+ | |
+ redirect_uri = self.redirect_uri(provider) | |
+ client.redirect_uris = [redirect_uri] | |
client.state = rndstr() | |
_nonce = rndstr() | |
@@ -124,8 +162,11 @@ | |
return | |
def handle(self): | |
- client = self.init_oic() | |
- client.redirect_uris=[self.base+'index?@action=oic_authresp'] | |
+ provider = select_provider(self.client.form) | |
+ client = self.init_oic(provider) | |
+ | |
+ redirect_uri = self.redirect_uri(provider) | |
+ client.redirect_uris = [redirect_uri] | |
aresp = client.parse_response(AuthorizationResponse, info=self.client.env['QUERY_STRING'], | |
sformat="urlencoded") | |
@@ -143,8 +184,13 @@ | |
resp = client.do_access_token_request(scope=self.scopes, | |
state=aresp["state"], | |
request_args=args, | |
- authn_method="client_secret_post" | |
+ authn_method="client_secret_post", | |
+ headers={'Accept': 'application/json'} | |
) | |
+ | |
+ if provider == PROVIDER_GITHUB: | |
+ return self.on_github_response(client, resp) | |
+ | |
try: | |
id_token = resp['id_token'] | |
except KeyError: | |
@@ -229,6 +275,58 @@ | |
# confirmation action. Doing so is deferred until need arises | |
raise ValueError, "Your OpenID Connect account is not supported. Please contact [email protected]" | |
+ def on_github_response(self, client, response): | |
+ assert 'access_token' in response | |
+ token = response['access_token'] | |
+ | |
+ # Grab their info from the github api | |
+ user_info = client.http_request('https://api.github.com/user', | |
+ method="GET", headers={ | |
+ 'Authorization': 'token {}'.format(token), | |
+ 'User-Agent': 'bugs.python.org', | |
+ 'Accept': 'application/json' | |
+ }) | |
+ | |
+ assert user_info.status_code == 200 | |
+ user_info = user_info.json() | |
+ | |
+ github_issuer = PROVIDER_URL_MAP[PROVIDER_GITHUB] | |
+ github_id = str(user_info['id']) | |
+ | |
+ oic_account = self.db.oic_account.filter(None, {'issuer': github_issuer, 'subject': github_id}) | |
+ # Existing user | |
+ if oic_account: | |
+ # there should be only one user with that ID | |
+ assert len(oic_account) == 1 | |
+ user = self.db.oic_account.get(oic_account[0], 'user') | |
+ return self.login(user) | |
+ | |
+ # Look for unused account name | |
+ github_name = user_info['login'] | |
+ | |
+ username = github_name | |
+ suffix = 1 | |
+ while True: | |
+ user = self.db.user.filter(None, {'username': username}) | |
+ if not user: | |
+ break | |
+ suffix += 1 | |
+ username = github_name + str(suffix) | |
+ | |
+ # generate account | |
+ | |
+ pw = password.Password(password.generatePassword()) | |
+ user = self.db.user.create(username=username, | |
+ realname=user_info['name'], | |
+ github=user_info['login'], | |
+ password=pw, | |
+ roles=self.db.config['NEW_WEB_USER_ROLES'], | |
+ address=user_info['email']) | |
+ self.db.oic_account.create(user=user, issuer=github_issuer, subject=github_id) | |
+ # complete login | |
+ self.db.commit() | |
+ return self.login(user) | |
+ | |
class OICDelete(Action): | |
def handle(self): | |
if not self.form.has_key('openid'): | |
diff -r f40f48714e04 html/page.html | |
--- a/html/page.html Fri Jun 15 20:25:08 2018 +0200 | |
+++ b/html/page.html Wed Jun 20 01:52:00 2018 -0400 | |
@@ -194,6 +194,11 @@ | |
height="16" | |
src="https://www.google.com/favicon.ico" | |
alt="Google" title="Google" /></a> | |
+ <a style="display:inline; width:0; margin: 0" href="index?@action=oic_login&provider=Github"> | |
+ <img hspace="0" vspace="0" width="16" height="16" | |
+ src="https://www.github.com/favicon.ico" | |
+ alt="Login with Github" title="Login with Github" /> | |
+ </a> | |
<a style="display:inline;width:0;margin:0" tal:repeat="prov python:utils.openid_links(request)" tal:attributes="href prov/href"> | |
<img hspace="0" vspace="0" width="16" height="16" tal:attributes="src prov/src;title prov/title;alt prov/alt"/></a> | |
<input size="10" name="openid_identifier" style="background:url(@@file/openid-16x16.gif) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment