Repository: Kinto/kinto (Python)

Issue:
OpenID payload cache uses the wrong cache key
The `cache_key` becomes the same for every access token due to this bug: https://github.com/Kinto/kinto/blob/e1e0d6be0024418fd100210901f9d2ca06344fe1/kinto/plugins/openid/__init__.py#L51
No matter what the `hmac_tokens` variable is the `cache_key` always becomes `'openid:verify:%s'`.




Discussion:
![](https://media.giphy.com/media/nspvD8XYqUxP2/giphy.gif)



Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 3e0d33448..2b1f9670d 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -12,6 +12,10 @@ This document describes changes between each past release.
 - Kinto Admin plugin now supports OpenID Connect
 - Limit network requests to current domain in Kinto Admin using `Content-Security Policies <https://hacks.mozilla.org/2016/02/implementing-content-security-policy/>`_
 
+**Bug fixes**
+
+- OpenID plugin used the same cache key for every access-token (fixes #1660)
+
 **Internal changes**
 
 - Upgrade to kinto-admin v1.18.0
diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst
index fc1fea527..d0820375a 100644
--- a/CONTRIBUTORS.rst
+++ b/CONTRIBUTORS.rst
@@ -57,6 +57,7 @@ Contributors
 * Oron Gola <oron.golar@gmail.com>
 * Palash Nigam <npalash25@gmail.com>
 * PeriGK <per.gkolias@gmail.com>
+* Peter Bengtsson <mail@peterbe.com>
 * realsumit <sumitsarinofficial@gmail.com>
 * Rektide <rektide@voodoowarez.com>
 * Rémy Hubscher <rhubscher@mozilla.com>
diff --git a/kinto/plugins/openid/__init__.py b/kinto/plugins/openid/__init__.py
index 3debe38c6..59d50c248 100644
--- a/kinto/plugins/openid/__init__.py
+++ b/kinto/plugins/openid/__init__.py
@@ -48,7 +48,7 @@ def unauthenticated_userid(self, request):
 
         # Check cache if these tokens were already verified.
         hmac_tokens = core_utils.hmac_digest(hmac_secret, access_token)
-        cache_key = 'openid:verify:%s'.format(hmac_tokens)
+        cache_key = 'openid:verify:{}'.format(hmac_tokens)
         payload = request.registry.cache.get(cache_key)
         if payload is None:
             # This can take some time.


Test patch:
diff --git a/tests/plugins/test_openid.py b/tests/plugins/test_openid.py
index 16e14cb7a..b3b35c799 100644
--- a/tests/plugins/test_openid.py
+++ b/tests/plugins/test_openid.py
@@ -111,13 +111,35 @@ def test_returns_none_if_no_cache_and_invalid_access_token(self):
     def test_payload_is_read_from_cache(self):
         self.request.headers['Authorization'] = 'Bearer xyz'
         self.request.registry.cache.get.return_value = {'sub': 'me'}
-        self.policy.unauthenticated_userid(self.request) == 'me'
+        assert self.policy.unauthenticated_userid(self.request) == 'me'
 
     def test_payload_is_stored_in_cache(self):
         self.request.headers['Authorization'] = 'Bearer xyz'
         assert self.policy.unauthenticated_userid(self.request) == 'userid'
         assert self.request.registry.cache.set.called
 
+    def test_payload_is_read_from_cache_but_differently_by_access_token(self):
+        # State to keep track of cache keys queried.
+        cache_keys_used = []
+
+        def mocked_cache_get(cache_key):
+            # This makes sure the same cache key is not used twice
+            assert cache_key not in cache_keys_used
+            cache_keys_used.append(cache_key)
+            if len(cache_keys_used) == 1:
+                return {'sub': 'me'}
+            elif len(cache_keys_used) == 2:
+                return {'sub': 'you'}
+
+        self.request.registry.cache.get.side_effect = mocked_cache_get
+
+        self.request.headers['Authorization'] = 'Bearer xyz'
+        assert self.policy.unauthenticated_userid(self.request) == 'me'
+
+        # Change the Authorization header the second time
+        self.request.headers['Authorization'] = 'Bearer abc'
+        assert self.policy.unauthenticated_userid(self.request) == 'you'
+
 
 class VerifyTokenTest(unittest.TestCase):