Repository: Kinto/kinto

Issue:
OpenID authentication plugin is missing a profile view
When validating the token, the plugn is fetching the profile, but there are no endpoint available to fetch this info: https://github.com/Kinto/kinto/blob/master/kinto/plugins/openid/__init__.py#L74

I would be in favor of adding a `/v1/openid/google/profile` view in order to grab the username and the profile_pic of the user.

This information might also be added in the `user` field of the `/v1/` endpoint. What do you think?


Discussion:
the `user` field in the root URL would be nice, but probably not as easy as the other option ;)
I would be willing to try that, it makes a lot of sense to me.

Resolving patch:
diff --git a/kinto/core/__init__.py b/kinto/core/__init__.py
--- a/kinto/core/__init__.py
+++ b/kinto/core/__init__.py
@@ -143,7 +143,10 @@ def __init__(self, fmt=None, datefmt=None, style="%"):
 
 def get_user_info(request):
     # Default user info (shown in hello view for example).
-    return {"id": request.prefixed_userid, "principals": request.prefixed_principals}
+    user_info = {"id": request.prefixed_userid, "principals": request.prefixed_principals}
+    if hasattr(request, "get_user_profile"):
+        user_info["profile"] = request.get_user_profile()
+    return user_info
 
 
 def includeme(config):
diff --git a/kinto/plugins/openid/__init__.py b/kinto/plugins/openid/__init__.py
--- a/kinto/plugins/openid/__init__.py
+++ b/kinto/plugins/openid/__init__.py
@@ -56,6 +56,7 @@ def unauthenticated_userid(self, request):
                 return None
         # Save for next time / refresh ttl.
         request.registry.cache.set(cache_key, payload, ttl=self.verification_ttl)
+        request.bound_data["user_profile"] = payload
         # Extract meaningful field from userinfo (eg. email or sub)
         return payload.get(self.userid_field)
 
@@ -79,6 +80,10 @@ def _verify_token(self, access_token):
             return None
 
 
+def get_user_profile(request):
+    return request.bound_data.get("user_profile", {})
+
+
 def includeme(config):
     # Activate end-points.
     config.scan("kinto.plugins.openid.views")
@@ -124,3 +129,4 @@ def includeme(config):
         url="http://kinto.readthedocs.io/en/stable/api/1.x/authentication.html",
         providers=providers_infos,
     )
+    config.add_request_method(get_user_profile, name="get_user_profile")


Test patch:
diff --git a/tests/core/test_views_hello.py b/tests/core/test_views_hello.py
--- a/tests/core/test_views_hello.py
+++ b/tests/core/test_views_hello.py
@@ -52,6 +52,23 @@ def test_if_user_authenticated_userid_is_provided(self):
             userid.startswith("basicauth:"), '"{}" does not start with "basicauth:"'.format(userid)
         )
 
+    def test_if_user_authenticated_profile_is_provided_if_present(self):
+        settings = self.get_app_settings()
+        config = testing.setUp(settings=settings)
+        app = self.make_app(config=config)
+        profile = {
+            "email": "foobar@domain.tld",
+            "profile_pic": "https://flatimage.com/100x100.png",
+        }
+
+        def get_profile(request):
+            return profile
+
+        config.add_request_method(get_profile, name="get_user_profile")
+
+        response = app.get("/", headers=self.headers)
+        self.assertEqual(response.json["user"]["profile"], profile)
+
     def test_return_http_api_version_when_set(self):
         with mock.patch.dict(self.app.app.registry.settings, [("http_api_version", "1.2")]):
             response = self.app.get("/")
diff --git a/tests/plugins/test_openid.py b/tests/plugins/test_openid.py
--- a/tests/plugins/test_openid.py
+++ b/tests/plugins/test_openid.py
@@ -90,6 +90,21 @@ def test_openid_one_provider(self):
         providers = capabilities["openid"]["providers"]
         assert len(providers) == 1
 
+    def test_profile_is_exposed(self):
+        key = "openid:verify:444c6694937007bbf494f155f6cb12139db4c4c6a926742f3fe0bb4b5d191aa3"
+        profile = {"sub": "abcd", "email": "foobar@tld.com"}
+        self.app.app.registry.cache.set(key, profile, ttl=30)
+        with mock.patch("kinto.plugins.openid.utils.requests.get") as m:
+            m.return_value.json.return_value = {
+                "userinfo_endpoint": "http://uinfo",
+                "jwks_uri": "https://jwks",
+            }
+            fetch_openid_config("https://fxa")
+
+        resp = self.app.get("/", headers={"Authorization": "Bearer avrbnnbrbr"})
+        assert "profile" in resp.json["user"]
+        assert resp.json["user"]["profile"] == {"sub": "abcd", "email": "foobar@tld.com"}
+
 
 class HelloViewTest(OpenIDWebTest):
     def test_openid_capability_if_enabled(self):