Merge remote-tracking branch 'sclm/GoogleOAuth2' into merge

Conflicts:
	docs/releases/next.rst
This commit is contained in:
Ben Darnell 2013-11-03 14:37:43 -05:00
commit 92ac501277
3 changed files with 71 additions and 2 deletions

View File

@ -31,6 +31,9 @@
.. autoclass:: GoogleMixin
:members:
.. autoclass:: GoogleOAuth2Mixin
:members:
Facebook
--------

View File

@ -31,3 +31,4 @@ In Progress
arguments, just like `~.RequestHandler.clear_cookie`.
* The embedded ``ca-certificats.crt`` file has been updated with the current
Mozilla CA list.
* `.GoogleOAuth2Mixin` has been added so that Google's OAuth2 only apps are able to get a context without OpenID (which uses OAuth 1).

View File

@ -548,7 +548,8 @@ class OAuth2Mixin(object):
"""
@return_future
def authorize_redirect(self, redirect_uri=None, client_id=None,
client_secret=None, extra_params=None,
client_secret=None, scope=None,
response_type="code", extra_params=None,
callback=None):
"""Redirects the user to obtain OAuth authorization for this service.
@ -566,10 +567,13 @@ class OAuth2Mixin(object):
"""
args = {
"redirect_uri": redirect_uri,
"client_id": client_id
"client_id": client_id,
"response_type": response_type
}
if extra_params:
args.update(extra_params)
if scope:
args['scope'] = ' '.join(scope)
self.redirect(
url_concat(self._OAUTH_AUTHORIZE_URL, args))
callback()
@ -945,6 +949,67 @@ class GoogleMixin(OpenIdMixin, OAuthMixin):
return OpenIdMixin.get_authenticated_user(self)
class GoogleOAuth2Mixin(OAuth2Mixin):
"""Google authentication using OAuth2."""
_OAUTH_AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/auth"
_OAUTH_ACCESS_TOKEN_URL = "https://accounts.google.com/o/oauth2/token"
_OAUTH_NO_CALLBACKS = False
_OAUTH_SETTINGS_KEY = 'google_oauth'
@_auth_return_future
def get_authenticated_user(self, redirect_uri, code, callback):
"""Handles the login for the Google user, returning a user object.
Example usage::
class GoogleOAuth2LoginHandler(LoginHandler, tornado.auth.GoogleOAuth2Mixin):
@tornado.web.asynchronous
@tornado.gen.coroutine
def get(self):
if self.get_argument("code", False):
user = yield self.get_authenticated_user(
redirect_uri='http://your.site.com/auth/google',
code=self.get_argument("code"))
# Save the user with e.g. set_secure_cookie
else:
yield self.authorize_redirect(
redirect_uri='http://your.site.com/auth/google',
client_id=self.settings["google_consumer_key"],
scope=['openid', 'email'],
response_type='code',
extra_params={"approval_prompt": "auto"})
"""
http = self.get_auth_http_client()
body = urllib_parse.urlencode({
"redirect_uri": redirect_uri,
"code": code,
"client_id": self.settings[self._OAUTH_SETTINGS_KEY]['key'],
"client_secret": self.settings[self._OAUTH_SETTINGS_KEY]['secret'],
"grant_type": "authorization_code",
})
http.fetch(self._OAUTH_ACCESS_TOKEN_URL,
self.async_callback(self._on_access_token, callback),
method="POST", headers={'Content-Type': 'application/x-www-form-urlencoded'}, body=body)
def _on_access_token(self, future, response):
"""Callback function for the exchange to the access token."""
if response.error:
future.set_exception(AuthError('Google auth error: %s' % str(response)))
return
args = escape.json_decode(response.body)
future.set_result(args)
def get_auth_http_client(self):
"""Returns the `.AsyncHTTPClient` instance to be used for auth requests.
May be overridden by subclasses to use an HTTP client other than
the default.
"""
return httpclient.AsyncHTTPClient()
class FacebookMixin(object):
"""Facebook Connect authentication.