From: Stephen McMillen Date: Wed, 17 Jul 2013 22:59:13 +0000 (-0400) Subject: Add in Google OAuth2 support. X-Git-Tag: v3.2.0b1~54^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1942f97df5d49e20af0440e7b3867e70189149b6;p=thirdparty%2Ftornado.git Add in Google OAuth2 support. --- diff --git a/docs/releases/next.rst b/docs/releases/next.rst index d8d516b51..dba53ca9d 100644 --- a/docs/releases/next.rst +++ b/docs/releases/next.rst @@ -14,3 +14,4 @@ In Progress while idle. * `.FacebookGraphMixin` has been updated to use the current Facebook login URL, which saves a redirect. +* `.GoogleOAuth2Mixin` has been added so that Google's OAuth2 only apps are able to get a context without OpenID (which uses OAuth 1). \ No newline at end of file diff --git a/tornado/auth.py b/tornado/auth.py index 0cbfa7c04..284c4d1e0 100644 --- a/tornado/auth.py +++ b/tornado/auth.py @@ -945,6 +945,88 @@ 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 + + def authorize_redirect(self, redirect_uri=None, client_id=None, + scope=['openid', 'email'], response_type="code", overwrites={}): + """Redirect the user to Google to authenticate them. + + The API itself defaults some of these values that cna be overwritten by the overwrites object. + They are: + * approval_prompt = auto + * access_type = online + """ + extra_params = { + "scope": ' '.join(scope), + "response_type": response_type, + } + + extra_params.update(overwrites) + + OAuth2Mixin.authorize_redirect(self, + self.request.protocol + '://' + self.request.host + (redirect_uri or self.request.uri), + self.settings['google_consumer_key'], + '', extra_params) + + @_auth_return_future + def get_authenticated_user(self, redirect_uri, code, callback): + """Handles the login for the Facebook 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='/auth/google', + code=self.get_argument("code")) + # Save the user with e.g. set_secure_cookie + else: + yield self.authorize_redirect( + redirect_uri='/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.urlencode({ + "redirect_uri": self.request.protocol + '://' + self.request.host + redirect_uri, + "code": code, + "client_id": self.settings['google_consumer_key'], + "client_secret": self.settings['google_consumer_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 = json.loads(escape.native_str(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.