diff --git a/gluon/contrib/login_methods/oauth10a_account.py b/gluon/contrib/login_methods/oauth10a_account.py index 1f464f8c..d3341570 100644 --- a/gluon/contrib/login_methods/oauth10a_account.py +++ b/gluon/contrib/login_methods/oauth10a_account.py @@ -19,6 +19,7 @@ from urllib2 import urlopen import urllib2 from urllib import urlencode +from gluon import current class OAuthAccount(object): """ @@ -117,8 +118,8 @@ class OAuthAccount(object): self.client_id = client_id self.client_secret = client_secret self.code = None - self.request = g['request'] - self.session = g['session'] + self.request = current.request + self.session = current.session self.auth_url = auth_url self.token_url = token_url self.access_token_url = access_token_url diff --git a/gluon/contrib/login_methods/oauth20_account.py b/gluon/contrib/login_methods/oauth20_account.py index 788f08d0..195590a8 100644 --- a/gluon/contrib/login_methods/oauth20_account.py +++ b/gluon/contrib/login_methods/oauth20_account.py @@ -7,7 +7,8 @@ License: LGPL v3 Adds support for OAuth 2.0 authentication to web2py. -OAuth 2.0 Draft: http://tools.ietf.org/html/draft-ietf-oauth-v2-10 +OAuth 2.0 spec: http://tools.ietf.org/html/rfc6749 + """ import time @@ -17,6 +18,7 @@ import urllib2 from urllib import urlencode from gluon import current, redirect, HTTP +import json class OAuthAccount(object): """ @@ -105,7 +107,10 @@ server for requests. It can be used for the optional"scope" parameters for Face if not http_host: http_host = r.env.http_host - url_scheme = r.env.wsgi_url_scheme + if r.env.https == 'on': + url_scheme = 'https' + else: + url_scheme = r.env.wsgi_url_scheme if next: path_info = next else: @@ -144,31 +149,45 @@ server for requests. It can be used for the optional"scope" parameters for Face # reuse token until expiration if expires == 0 or expires > time.time(): return current.session.token['access_token'] - if current.session.code: + + code = current.request.vars.code + + if code: data = dict(client_id=self.client_id, client_secret=self.client_secret, redirect_uri=current.session.redirect_uri, - response_type='token', code=current.session.code) + code=code, + grant_type='authorization_code' + ) - if self.args: - data.update(self.args) open_url = None opener = self.__build_url_opener(self.token_url) try: open_url = opener.open(self.token_url, urlencode(data)) except urllib2.HTTPError, e: tmp = e.read() - print tmp raise Exception(tmp) finally: - del current.session.code # throw it away + if current.session.code: + del current.session.code # throw it away if open_url: try: data = open_url.read() - tokendata = cgi.parse_qs(data) - current.session.token = \ - dict([(k, v[-1]) for k, v in tokendata.items()]) + resp_type = open_url.info().get('Content-Type') + # try json style first + if not resp_type or resp_type == 'application/json': + try: + tokendata = json.loads(data) + current.session.token = tokendata + except Exception, e: + raise Exception("Cannot parse oauth server response %s %s" % (data, e)) + else: # try facebook style first with x-www-form-encoded + tokendata = cgi.parse_qs(data) + current.session.token = \ + dict([(k, v[-1]) for k, v in tokendata.items()]) + if not tokendata: # parsing failed? + raise Exception("Cannot parse oauth server response %s" % data) # set expiration absolute time try to avoid broken # implementations where "expires_in" becomes "expires" if 'expires_in' in current.session.token: @@ -258,20 +277,16 @@ server for requests. It can be used for the optional"scope" parameters for Face accessToken() """ - if not self.accessToken(): - if not current.request.vars.code: - current.session.redirect_uri = self.__redirect_uri(next) - data = dict(redirect_uri=current.session.redirect_uri, - response_type='code', - client_id=self.client_id) - if self.args: - data.update(self.args) - auth_request_url = self.auth_url + "?" + urlencode(data) - raise HTTP(307, - "You are not authenticated: you are being redirected to the authentication server", - Location=auth_request_url) - else: - current.session.code = current.request.vars.code - self.accessToken() - return current.session.code - return None + token = self.accessToken() + if not token: + current.session.redirect_uri = self.__redirect_uri(next) + data = dict(redirect_uri=current.session.redirect_uri, + response_type='code', + client_id=self.client_id) + if self.args: + data.update(self.args) + auth_request_url = self.auth_url + "?" + urlencode(data) + raise HTTP(307, + "You are not authenticated: you are being redirected to the authentication server", + Location=auth_request_url) + return diff --git a/gluon/tools.py b/gluon/tools.py index eb3489b2..9fc9201f 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -4434,12 +4434,12 @@ class Service(object): return return_error(id, e.code, e.info) except BaseException: etype, eval, etb = sys.exc_info() - code = -32001 - data = '%s: %s\n' % (etype.__name__, eval) + request.is_local and traceback.format_tb(etb) + code = -32099 + data = '%s: %s\n' % (etype.__name__, eval) + str(request.is_local and traceback.format_tb(etb)) return return_error(id, code, data=data) except: etype, eval, etb = sys.exc_info() - return return_error(id, 32099, data='Exception %s: %s' % (etype, eval)) + return return_error(id, -32099, data='Exception %s: %s' % (etype, eval)) def serve_xmlrpc(self):