]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Add in Google OAuth2 support.
authorStephen McMillen <sclm3000@gmail.com>
Wed, 17 Jul 2013 22:59:13 +0000 (18:59 -0400)
committerStephen McMillen <sclm3000@gmail.com>
Wed, 17 Jul 2013 22:59:13 +0000 (18:59 -0400)
docs/releases/next.rst
tornado/auth.py

index d8d516b51591fc84153c1475261658c560b63ba4..dba53ca9d235f24ad620bb9b04b623fcf1e37503 100644 (file)
@@ -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
index 0cbfa7c04d407f75e07b4d51f4b214e225f892fc..284c4d1e01a95f639c09f90abf4267f66089f250 100644 (file)
@@ -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.