prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN 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'`.
  
  
  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'`.
  
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `kinto/plugins/openid/__init__.py`
  Content:
  ```
  1 import re
  2 
  3 import requests
  4 from pyramid import authentication as base_auth
  5 from pyramid.interfaces import IAuthenticationPolicy
  6 from zope.interface import implementer
  7 
  8 from kinto.core import logger
  9 from kinto.core import utils as core_utils
  10 from kinto.core.openapi import OpenAPI
  11 
  12 from .utils import fetch_openid_config
  13 
  14 
  15 @implementer(IAuthenticationPolicy)
  16 class OpenIDConnectPolicy(base_auth.CallbackAuthenticationPolicy):
  17     def __init__(self, issuer, client_id, realm='Realm', **kwargs):
  18         self.realm = realm
  19         self.issuer = issuer
  20         self.client_id = client_id
  21         self.client_secret = kwargs.get('client_secret', '')
  22         self.header_type = kwargs.get('header_type', 'Bearer')
  23         self.userid_field = kwargs.get('userid_field', 'sub')
  24         self.verification_ttl = int(kwargs.get('verification_ttl_seconds', 86400))
  25 
  26         # Fetch OpenID config (at instantiation, ie. startup)
  27         self.oid_config = fetch_openid_config(issuer)
  28 
  29         self._jwt_keys = None
  30 
  31     def unauthenticated_userid(self, request):
  32         """Return the userid or ``None`` if token could not be verified.
  33         """
  34         settings = request.registry.settings
  35         hmac_secret = settings['userid_hmac_secret']
  36 
  37         authorization = request.headers.get('Authorization', '')
  38         try:
  39             authmeth, access_token = authorization.split(' ', 1)
  40         except ValueError:
  41             return None
  42 
  43         if authmeth.lower() != self.header_type.lower():
  44             return None
  45 
  46         # XXX JWT Access token
  47         # https://auth0.com/docs/tokens/access-token#access-token-format
  48 
  49         # Check cache if these tokens were already verified.
  50         hmac_tokens = core_utils.hmac_digest(hmac_secret, access_token)
  51         cache_key = 'openid:verify:%s'.format(hmac_tokens)
  52         payload = request.registry.cache.get(cache_key)
  53         if payload is None:
  54             # This can take some time.
  55             payload = self._verify_token(access_token)
  56             if payload is None:
  57                 return None
  58         # Save for next time / refresh ttl.
  59         request.registry.cache.set(cache_key, payload, ttl=self.verification_ttl)
  60         # Extract meaningful field from userinfo (eg. email or sub)
  61         return payload.get(self.userid_field)
  62 
  63     def forget(self, request):
  64         """A no-op. Credentials are sent on every request.
  65         Return WWW-Authenticate Realm header for Bearer token.
  66         """
  67         return [('WWW-Authenticate', '%s realm="%s"' % (self.header_type, self.realm))]
  68 
  69     def _verify_token(self, access_token):
  70         uri = self.oid_config['userinfo_endpoint']
  71         # Opaque access token string. Fetch user info from profile.
  72         try:
  73             resp = requests.get(uri, headers={'Authorization': 'Bearer ' + access_token})
  74             resp.raise_for_status()
  75             userprofile = resp.json()
  76             return userprofile
  77 
  78         except (requests.exceptions.HTTPError, ValueError, KeyError) as e:
  79             logger.debug('Unable to fetch user profile from %s (%s)' % (uri, e))
  80             return None
  81 
  82 
  83 def includeme(config):
  84     # Activate end-points.
  85     config.scan('kinto.plugins.openid.views')
  86 
  87     settings = config.get_settings()
  88 
  89     openid_policies = []
  90     for k, v in settings.items():
  91         m = re.match('multiauth\.policy\.(.*)\.use', k)
  92         if m:
  93             if v.endswith('OpenIDConnectPolicy'):
  94                 openid_policies.append(m.group(1))
  95 
  96     if len(openid_policies) == 0:
  97         # Do not add the capability if no policy is configured.
  98         return
  99 
  100     providers_infos = []
  101     for name in openid_policies:
  102         issuer = settings['multiauth.policy.%s.issuer' % name]
  103         openid_config = fetch_openid_config(issuer)
  104 
  105         client_id = settings['multiauth.policy.%s.client_id' % name]
  106         header_type = settings.get('multiauth.policy.%s.header_type', 'Bearer')
  107 
  108         providers_infos.append({
  109             'name': name,
  110             'issuer': openid_config['issuer'],
  111             'auth_path': '/openid/%s/login' % name,
  112             'client_id': client_id,
  113             'header_type': header_type,
  114             'userinfo_endpoint': openid_config['userinfo_endpoint'],
  115         })
  116 
  117         OpenAPI.expose_authentication_method(name, {
  118             'type': 'oauth2',
  119             'authorizationUrl': openid_config['authorization_endpoint'],
  120         })
  121 
  122     config.add_api_capability(
  123         'openid',
  124         description='OpenID connect support.',
  125         url='http://kinto.readthedocs.io/en/stable/api/1.x/authentication.html',
  126         providers=providers_infos)
  127
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: 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
  @@ -48,7 +48,7 @@
   
           # 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.
  
verification_info: {"golden_diff": "diff --git a/kinto/plugins/openid/__init__.py b/kinto/plugins/openid/__init__.py\n--- a/kinto/plugins/openid/__init__.py\n+++ b/kinto/plugins/openid/__init__.py\n@@ -48,7 +48,7 @@\n \n         # Check cache if these tokens were already verified.\n         hmac_tokens = core_utils.hmac_digest(hmac_secret, access_token)\n-        cache_key = 'openid:verify:%s'.format(hmac_tokens)\n+        cache_key = 'openid:verify:{}'.format(hmac_tokens)\n         payload = request.registry.cache.get(cache_key)\n         if payload is None:\n             # This can take some time.\n", "issue": "OpenID payload cache uses the wrong cache key\nThe `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\r\nNo matter what the `hmac_tokens` variable is the `cache_key` always becomes `'openid:verify:%s'`.\r\n\r\n\nOpenID payload cache uses the wrong cache key\nThe `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\r\nNo matter what the `hmac_tokens` variable is the `cache_key` always becomes `'openid:verify:%s'`.\r\n\r\n\n", "before_files": [{"content": "import re\n\nimport requests\nfrom pyramid import authentication as base_auth\nfrom pyramid.interfaces import IAuthenticationPolicy\nfrom zope.interface import implementer\n\nfrom kinto.core import logger\nfrom kinto.core import utils as core_utils\nfrom kinto.core.openapi import OpenAPI\n\nfrom .utils import fetch_openid_config\n\n\n@implementer(IAuthenticationPolicy)\nclass OpenIDConnectPolicy(base_auth.CallbackAuthenticationPolicy):\n    def __init__(self, issuer, client_id, realm='Realm', **kwargs):\n        self.realm = realm\n        self.issuer = issuer\n        self.client_id = client_id\n        self.client_secret = kwargs.get('client_secret', '')\n        self.header_type = kwargs.get('header_type', 'Bearer')\n        self.userid_field = kwargs.get('userid_field', 'sub')\n        self.verification_ttl = int(kwargs.get('verification_ttl_seconds', 86400))\n\n        # Fetch OpenID config (at instantiation, ie. startup)\n        self.oid_config = fetch_openid_config(issuer)\n\n        self._jwt_keys = None\n\n    def unauthenticated_userid(self, request):\n        \"\"\"Return the userid or ``None`` if token could not be verified.\n        \"\"\"\n        settings = request.registry.settings\n        hmac_secret = settings['userid_hmac_secret']\n\n        authorization = request.headers.get('Authorization', '')\n        try:\n            authmeth, access_token = authorization.split(' ', 1)\n        except ValueError:\n            return None\n\n        if authmeth.lower() != self.header_type.lower():\n            return None\n\n        # XXX JWT Access token\n        # https://auth0.com/docs/tokens/access-token#access-token-format\n\n        # Check cache if these tokens were already verified.\n        hmac_tokens = core_utils.hmac_digest(hmac_secret, access_token)\n        cache_key = 'openid:verify:%s'.format(hmac_tokens)\n        payload = request.registry.cache.get(cache_key)\n        if payload is None:\n            # This can take some time.\n            payload = self._verify_token(access_token)\n            if payload is None:\n                return None\n        # Save for next time / refresh ttl.\n        request.registry.cache.set(cache_key, payload, ttl=self.verification_ttl)\n        # Extract meaningful field from userinfo (eg. email or sub)\n        return payload.get(self.userid_field)\n\n    def forget(self, request):\n        \"\"\"A no-op. Credentials are sent on every request.\n        Return WWW-Authenticate Realm header for Bearer token.\n        \"\"\"\n        return [('WWW-Authenticate', '%s realm=\"%s\"' % (self.header_type, self.realm))]\n\n    def _verify_token(self, access_token):\n        uri = self.oid_config['userinfo_endpoint']\n        # Opaque access token string. Fetch user info from profile.\n        try:\n            resp = requests.get(uri, headers={'Authorization': 'Bearer ' + access_token})\n            resp.raise_for_status()\n            userprofile = resp.json()\n            return userprofile\n\n        except (requests.exceptions.HTTPError, ValueError, KeyError) as e:\n            logger.debug('Unable to fetch user profile from %s (%s)' % (uri, e))\n            return None\n\n\ndef includeme(config):\n    # Activate end-points.\n    config.scan('kinto.plugins.openid.views')\n\n    settings = config.get_settings()\n\n    openid_policies = []\n    for k, v in settings.items():\n        m = re.match('multiauth\\.policy\\.(.*)\\.use', k)\n        if m:\n            if v.endswith('OpenIDConnectPolicy'):\n                openid_policies.append(m.group(1))\n\n    if len(openid_policies) == 0:\n        # Do not add the capability if no policy is configured.\n        return\n\n    providers_infos = []\n    for name in openid_policies:\n        issuer = settings['multiauth.policy.%s.issuer' % name]\n        openid_config = fetch_openid_config(issuer)\n\n        client_id = settings['multiauth.policy.%s.client_id' % name]\n        header_type = settings.get('multiauth.policy.%s.header_type', 'Bearer')\n\n        providers_infos.append({\n            'name': name,\n            'issuer': openid_config['issuer'],\n            'auth_path': '/openid/%s/login' % name,\n            'client_id': client_id,\n            'header_type': header_type,\n            'userinfo_endpoint': openid_config['userinfo_endpoint'],\n        })\n\n        OpenAPI.expose_authentication_method(name, {\n            'type': 'oauth2',\n            'authorizationUrl': openid_config['authorization_endpoint'],\n        })\n\n    config.add_api_capability(\n        'openid',\n        description='OpenID connect support.',\n        url='http://kinto.readthedocs.io/en/stable/api/1.x/authentication.html',\n        providers=providers_infos)\n", "path": "kinto/plugins/openid/__init__.py"}], "after_files": [{"content": "import re\n\nimport requests\nfrom pyramid import authentication as base_auth\nfrom pyramid.interfaces import IAuthenticationPolicy\nfrom zope.interface import implementer\n\nfrom kinto.core import logger\nfrom kinto.core import utils as core_utils\nfrom kinto.core.openapi import OpenAPI\n\nfrom .utils import fetch_openid_config\n\n\n@implementer(IAuthenticationPolicy)\nclass OpenIDConnectPolicy(base_auth.CallbackAuthenticationPolicy):\n    def __init__(self, issuer, client_id, realm='Realm', **kwargs):\n        self.realm = realm\n        self.issuer = issuer\n        self.client_id = client_id\n        self.client_secret = kwargs.get('client_secret', '')\n        self.header_type = kwargs.get('header_type', 'Bearer')\n        self.userid_field = kwargs.get('userid_field', 'sub')\n        self.verification_ttl = int(kwargs.get('verification_ttl_seconds', 86400))\n\n        # Fetch OpenID config (at instantiation, ie. startup)\n        self.oid_config = fetch_openid_config(issuer)\n\n        self._jwt_keys = None\n\n    def unauthenticated_userid(self, request):\n        \"\"\"Return the userid or ``None`` if token could not be verified.\n        \"\"\"\n        settings = request.registry.settings\n        hmac_secret = settings['userid_hmac_secret']\n\n        authorization = request.headers.get('Authorization', '')\n        try:\n            authmeth, access_token = authorization.split(' ', 1)\n        except ValueError:\n            return None\n\n        if authmeth.lower() != self.header_type.lower():\n            return None\n\n        # XXX JWT Access token\n        # https://auth0.com/docs/tokens/access-token#access-token-format\n\n        # Check cache if these tokens were already verified.\n        hmac_tokens = core_utils.hmac_digest(hmac_secret, access_token)\n        cache_key = 'openid:verify:{}'.format(hmac_tokens)\n        payload = request.registry.cache.get(cache_key)\n        if payload is None:\n            # This can take some time.\n            payload = self._verify_token(access_token)\n            if payload is None:\n                return None\n        # Save for next time / refresh ttl.\n        request.registry.cache.set(cache_key, payload, ttl=self.verification_ttl)\n        # Extract meaningful field from userinfo (eg. email or sub)\n        return payload.get(self.userid_field)\n\n    def forget(self, request):\n        \"\"\"A no-op. Credentials are sent on every request.\n        Return WWW-Authenticate Realm header for Bearer token.\n        \"\"\"\n        return [('WWW-Authenticate', '%s realm=\"%s\"' % (self.header_type, self.realm))]\n\n    def _verify_token(self, access_token):\n        uri = self.oid_config['userinfo_endpoint']\n        # Opaque access token string. Fetch user info from profile.\n        try:\n            resp = requests.get(uri, headers={'Authorization': 'Bearer ' + access_token})\n            resp.raise_for_status()\n            userprofile = resp.json()\n            return userprofile\n\n        except (requests.exceptions.HTTPError, ValueError, KeyError) as e:\n            logger.debug('Unable to fetch user profile from %s (%s)' % (uri, e))\n            return None\n\n\ndef includeme(config):\n    # Activate end-points.\n    config.scan('kinto.plugins.openid.views')\n\n    settings = config.get_settings()\n\n    openid_policies = []\n    for k, v in settings.items():\n        m = re.match('multiauth\\.policy\\.(.*)\\.use', k)\n        if m:\n            if v.endswith('OpenIDConnectPolicy'):\n                openid_policies.append(m.group(1))\n\n    if len(openid_policies) == 0:\n        # Do not add the capability if no policy is configured.\n        return\n\n    providers_infos = []\n    for name in openid_policies:\n        issuer = settings['multiauth.policy.%s.issuer' % name]\n        openid_config = fetch_openid_config(issuer)\n\n        client_id = settings['multiauth.policy.%s.client_id' % name]\n        header_type = settings.get('multiauth.policy.%s.header_type', 'Bearer')\n\n        providers_infos.append({\n            'name': name,\n            'issuer': openid_config['issuer'],\n            'auth_path': '/openid/%s/login' % name,\n            'client_id': client_id,\n            'header_type': header_type,\n            'userinfo_endpoint': openid_config['userinfo_endpoint'],\n        })\n\n        OpenAPI.expose_authentication_method(name, {\n            'type': 'oauth2',\n            'authorizationUrl': openid_config['authorization_endpoint'],\n        })\n\n    config.add_api_capability(\n        'openid',\n        description='OpenID connect support.',\n        url='http://kinto.readthedocs.io/en/stable/api/1.x/authentication.html',\n        providers=providers_infos)\n", "path": "kinto/plugins/openid/__init__.py"}]}