Repository: tornadoweb/tornado
Pull request #1460 (2015-07-02 11:46:36)

Add Google OAuth2 user info request.
-   Added a Google user info API URL to `GoogleOAuth2Mixin`
-   Added code to get user object to the `GoogleOAuth2Mixin.get_authenticated_user` docstring example
-   Added user object process to the auth tests
-   Changed what looks like the wrong function in `oauth2_request`



Patch:
diff --git a/tornado/auth.py b/tornado/auth.py
index ebf0ecdd..c904847b 100644
--- a/tornado/auth.py
+++ b/tornado/auth.py
@@ -661,7 +661,7 @@ class OAuth2Mixin(object):
 
         if all_args:
             url += "?" + urllib_parse.urlencode(all_args)
-        callback = functools.partial(self._on_facebook_request, callback)
+        callback = functools.partial(self._on_oauth2_request, callback)
         http = self.get_auth_http_client()
         if post_args is not None:
             http.fetch(url, method="POST", body=urllib_parse.urlencode(post_args),
@@ -857,6 +857,7 @@ class GoogleOAuth2Mixin(OAuth2Mixin):
     """
     _OAUTH_AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/auth"
     _OAUTH_ACCESS_TOKEN_URL = "https://accounts.google.com/o/oauth2/token"
+    _OAUTH_USERINFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo"
     _OAUTH_NO_CALLBACKS = False
     _OAUTH_SETTINGS_KEY = 'google_oauth'
 
@@ -881,9 +882,12 @@ class GoogleOAuth2Mixin(OAuth2Mixin):
                 @tornado.gen.coroutine
                 def get(self):
                     if self.get_argument('code', False):
-                        user = yield self.get_authenticated_user(
+                        access = yield self.get_authenticated_user(
                             redirect_uri='http://your.site.com/auth/google',
                             code=self.get_argument('code'))
+                        args = dict(access_token=access["access_token"])
+                        url = self._OAUTH_USERINFO_URL + "?" + urllib_parse.urlencode(args)
+                        user = yield self.oauth2_request(url)
                         # Save the user with e.g. set_secure_cookie
                     else:
                         yield self.authorize_redirect(


Test patch:
diff --git a/tornado/test/auth_test.py b/tornado/test/auth_test.py
index fee79779..9ea4e00e 100644
--- a/tornado/test/auth_test.py
+++ b/tornado/test/auth_test.py
@@ -421,14 +421,17 @@ class GoogleLoginHandler(RequestHandler, GoogleOAuth2Mixin):
         self._OAUTH_REDIRECT_URI = test.get_url('/client/login')
         self._OAUTH_AUTHORIZE_URL = test.get_url('/google/oauth2/authorize')
         self._OAUTH_ACCESS_TOKEN_URL = test.get_url('/google/oauth2/token')
+        self._OAUTH_USERINFO_URL = test.get_url('/google/oauth2/userinfo')
 
     @gen.coroutine
     def get(self):
         code = self.get_argument('code', None)
         if code is not None:
             # retrieve authenticate google user
-            user = yield self.get_authenticated_user(self._OAUTH_REDIRECT_URI,
-                                                     code)
+            access = yield self.get_authenticated_user(self._OAUTH_REDIRECT_URI,
+                                                       code)
+            url = self._OAUTH_USERINFO_URL + "?access_token=" + access["access_token"]
+            user = yield self.oauth2_request(url)
             # return the user as json
             self.write(user)
         else:
@@ -459,6 +462,16 @@ class GoogleOAuth2TokenHandler(RequestHandler):
         })
 
 
+class GoogleOAuth2UserinfoHandler(RequestHandler):
+    def get(self):
+        assert self.get_argument('access_token') == 'fake-access-token'
+        # return a fake user
+        self.finish({
+                'name': 'Foo',
+                'email': 'foo@example.com'
+                })
+
+
 class GoogleOAuth2Test(AsyncHTTPTestCase):
     def get_app(self):
         return Application(
@@ -469,6 +482,7 @@ class GoogleOAuth2Test(AsyncHTTPTestCase):
                 # simulated google authorization server endpoints
                 ('/google/oauth2/authorize', GoogleOAuth2AuthorizeHandler),
                 ('/google/oauth2/token', GoogleOAuth2TokenHandler),
+                ('/google/oauth2/userinfo', GoogleOAuth2UserinfoHandler),
             ],
             google_oauth={
                 "key": 'fake_google_client_id',
@@ -478,6 +492,6 @@ class GoogleOAuth2Test(AsyncHTTPTestCase):
     def test_google_login(self):
         response = self.fetch('/client/login')
         self.assertDictEqual({
-            u('access_token'): u('fake-access-token'),
-            u('expires_in'): u('never-expires'),
+            u('name'): u('Foo'),
+            u('email'): u('foo@example.com'),
         }, json_decode(response.body))