We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
OpenID state length is too long for the PostgreSQL cache backend
Those two lines are not compatible together:

- https://github.com/Kinto/kinto/blob/c6cc7bba094aed6897d0157dc78b1731ac12c8db/kinto/core/cache/postgresql/schema.sql#L7
- https://github.com/Kinto/kinto/blob/c6cc7bba094aed6897d0157dc78b1731ac12c8db/kinto/plugins/openid/views.py#L97
OpenID state length is too long for the PostgreSQL cache backend
Those two lines are not compatible together:

- https://github.com/Kinto/kinto/blob/c6cc7bba094aed6897d0157dc78b1731ac12c8db/kinto/core/cache/postgresql/schema.sql#L7
- https://github.com/Kinto/kinto/blob/c6cc7bba094aed6897d0157dc78b1731ac12c8db/kinto/plugins/openid/views.py#L97

--- 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/views.py`
Content:
```
1 import urllib.parse
2 
3 import colander
4 import requests
5 from pyramid import httpexceptions
6 
7 from cornice.validators import colander_validator
8 from kinto.core import Service
9 from kinto.core.errors import raise_invalid, ERRORS
10 from kinto.core.utils import random_bytes_hex
11 from kinto.core.resource.schema import ErrorResponseSchema
12 from kinto.core.schema import URL
13 
14 from .utils import fetch_openid_config
15 
16 
17 DEFAULT_STATE_TTL_SECONDS = 3600
18 
19 
20 class RedirectHeadersSchema(colander.MappingSchema):
21     """Redirect response headers."""
22     location = colander.SchemaNode(colander.String(), name='Location')
23 
24 
25 class RedirectResponseSchema(colander.MappingSchema):
26     """Redirect response schema."""
27     headers = RedirectHeadersSchema()
28 
29 
30 response_schemas = {
31     '307': RedirectResponseSchema(description='Successful redirection.'),
32     '400': ErrorResponseSchema(description='The request is invalid.'),
33 }
34 
35 
36 def provider_validator(request, **kwargs):
37     """
38     This validator verifies that the validator in URL (eg. /openid/auth0/login)
39     is a configured OpenIDConnect policy.
40     """
41     provider = request.matchdict['provider']
42     used = request.registry.settings.get('multiauth.policy.%s.use' % provider, '')
43     if not used.endswith('OpenIDConnectPolicy'):
44         request.errors.add('path', 'provider', 'Unknow provider %r' % provider)
45 
46 
47 class LoginQuerystringSchema(colander.MappingSchema):
48     """
49     Querystring schema for the login endpoint.
50     """
51     callback = URL()
52     scope = colander.SchemaNode(colander.String())
53 
54 
55 class LoginSchema(colander.MappingSchema):
56     querystring = LoginQuerystringSchema()
57 
58 
59 login = Service(name='openid_login',
60                 path='/openid/{provider}/login',
61                 description='Initiate the OAuth2 login')
62 
63 
64 @login.get(schema=LoginSchema(),
65            validators=(colander_validator, provider_validator),
66            response_schemas=response_schemas)
67 def get_login(request):
68     """Initiates to login dance for the specified scopes and callback URI
69     using appropriate redirections."""
70 
71     # Settings.
72     provider = request.matchdict['provider']
73     settings_prefix = 'multiauth.policy.%s.' % provider
74     issuer = request.registry.settings[settings_prefix + 'issuer']
75     client_id = request.registry.settings[settings_prefix + 'client_id']
76     userid_field = request.registry.settings.get(settings_prefix + 'userid_field')
77     state_ttl = int(request.registry.settings.get(settings_prefix + 'state_ttl_seconds',
78                                                   DEFAULT_STATE_TTL_SECONDS))
79 
80     # Read OpenID configuration (cached by issuer)
81     oid_config = fetch_openid_config(issuer)
82     auth_endpoint = oid_config['authorization_endpoint']
83 
84     scope = request.GET['scope']
85     callback = request.GET['callback']
86 
87     # Check that email scope is requested if userid field is configured as email.
88     if userid_field == 'email' and 'email' not in scope:
89         error_details = {
90             'name': 'scope',
91             'description': "Provider %s requires 'email' scope" % provider,
92         }
93         raise_invalid(request, **error_details)
94 
95     # Generate a random string as state.
96     # And save it until code is traded.
97     state = random_bytes_hex(256)
98     request.registry.cache.set('openid:state:' + state, callback, ttl=state_ttl)
99 
100     # Redirect the client to the Identity Provider that will eventually redirect
101     # to the OpenID token endpoint.
102     token_uri = request.route_url('openid_token', provider=provider) + '?'
103     params = dict(client_id=client_id, response_type='code', scope=scope,
104                   redirect_uri=token_uri, state=state)
105     redirect = '{}?{}'.format(auth_endpoint, urllib.parse.urlencode(params))
106     raise httpexceptions.HTTPTemporaryRedirect(redirect)
107 
108 
109 class TokenQuerystringSchema(colander.MappingSchema):
110     """
111     Querystring schema for the token endpoint.
112     """
113     code = colander.SchemaNode(colander.String())
114     state = colander.SchemaNode(colander.String())
115 
116 
117 class TokenSchema(colander.MappingSchema):
118     querystring = TokenQuerystringSchema()
119 
120 
121 token = Service(name='openid_token',
122                 path='/openid/{provider}/token',
123                 description='')
124 
125 
126 @token.get(schema=TokenSchema(),
127            validators=(colander_validator, provider_validator))
128 def get_token(request):
129     """Trades the specified code and state against access and ID tokens.
130     The client is redirected to the original ``callback`` URI with the
131     result in querystring."""
132 
133     # Settings.
134     provider = request.matchdict['provider']
135     settings_prefix = 'multiauth.policy.%s.' % provider
136     issuer = request.registry.settings[settings_prefix + 'issuer']
137     client_id = request.registry.settings[settings_prefix + 'client_id']
138     client_secret = request.registry.settings[settings_prefix + 'client_secret']
139 
140     # Read OpenID configuration (cached by issuer)
141     oid_config = fetch_openid_config(issuer)
142     token_endpoint = oid_config['token_endpoint']
143 
144     code = request.GET['code']
145     state = request.GET['state']
146 
147     # State can be used only once.
148     callback = request.registry.cache.delete('openid:state:' + state)
149     if callback is None:
150         error_details = {
151             'name': 'state',
152             'description': 'Invalid state',
153             'errno': ERRORS.INVALID_AUTH_TOKEN.value,
154         }
155         raise_invalid(request, **error_details)
156 
157     # Trade the code for tokens on the Identity Provider.
158     # Google Identity requires to specify again redirect_uri.
159     redirect_uri = request.route_url('openid_token', provider=provider) + '?'
160     data = {
161         'code': code,
162         'client_id': client_id,
163         'client_secret': client_secret,
164         'redirect_uri': redirect_uri,
165         'grant_type': 'authorization_code',
166     }
167     resp = requests.post(token_endpoint, data=data)
168 
169     # The IdP response is forwarded to the client in the querystring/location hash.
170     # (eg. callback=`http://localhost:3000/#tokens=`)
171     redirect = callback + urllib.parse.quote(resp.text)
172     raise httpexceptions.HTTPTemporaryRedirect(redirect)
173
```
--- 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()

```

diff --git a/kinto/plugins/openid/views.py b/kinto/plugins/openid/views.py
--- a/kinto/plugins/openid/views.py
+++ b/kinto/plugins/openid/views.py
@@ -15,6 +15,7 @@
 
 
 DEFAULT_STATE_TTL_SECONDS = 3600
+DEFAULT_STATE_LENGTH = 32
 
 
 class RedirectHeadersSchema(colander.MappingSchema):
@@ -76,6 +77,8 @@
     userid_field = request.registry.settings.get(settings_prefix + 'userid_field')
     state_ttl = int(request.registry.settings.get(settings_prefix + 'state_ttl_seconds',
                                                   DEFAULT_STATE_TTL_SECONDS))
+    state_length = int(request.registry.settings.get(settings_prefix + 'state_length',
+                                                     DEFAULT_STATE_LENGTH))
 
     # Read OpenID configuration (cached by issuer)
     oid_config = fetch_openid_config(issuer)
@@ -94,7 +97,7 @@
 
     # Generate a random string as state.
     # And save it until code is traded.
-    state = random_bytes_hex(256)
+    state = random_bytes_hex(state_length)
     request.registry.cache.set('openid:state:' + state, callback, ttl=state_ttl)
 
     # Redirect the client to the Identity Provider that will eventually redirect

{"golden_diff": "diff --git a/kinto/plugins/openid/views.py b/kinto/plugins/openid/views.py\n--- a/kinto/plugins/openid/views.py\n+++ b/kinto/plugins/openid/views.py\n@@ -15,6 +15,7 @@\n \n \n DEFAULT_STATE_TTL_SECONDS = 3600\n+DEFAULT_STATE_LENGTH = 32\n \n \n class RedirectHeadersSchema(colander.MappingSchema):\n@@ -76,6 +77,8 @@\n     userid_field = request.registry.settings.get(settings_prefix + 'userid_field')\n     state_ttl = int(request.registry.settings.get(settings_prefix + 'state_ttl_seconds',\n                                                   DEFAULT_STATE_TTL_SECONDS))\n+    state_length = int(request.registry.settings.get(settings_prefix + 'state_length',\n+                                                     DEFAULT_STATE_LENGTH))\n \n     # Read OpenID configuration (cached by issuer)\n     oid_config = fetch_openid_config(issuer)\n@@ -94,7 +97,7 @@\n \n     # Generate a random string as state.\n     # And save it until code is traded.\n-    state = random_bytes_hex(256)\n+    state = random_bytes_hex(state_length)\n     request.registry.cache.set('openid:state:' + state, callback, ttl=state_ttl)\n \n     # Redirect the client to the Identity Provider that will eventually redirect\n", "issue": "OpenID state length is too long for the PostgreSQL cache backend\nThose two lines are not compatible together:\r\n\r\n- https://github.com/Kinto/kinto/blob/c6cc7bba094aed6897d0157dc78b1731ac12c8db/kinto/core/cache/postgresql/schema.sql#L7\r\n- https://github.com/Kinto/kinto/blob/c6cc7bba094aed6897d0157dc78b1731ac12c8db/kinto/plugins/openid/views.py#L97\nOpenID state length is too long for the PostgreSQL cache backend\nThose two lines are not compatible together:\r\n\r\n- https://github.com/Kinto/kinto/blob/c6cc7bba094aed6897d0157dc78b1731ac12c8db/kinto/core/cache/postgresql/schema.sql#L7\r\n- https://github.com/Kinto/kinto/blob/c6cc7bba094aed6897d0157dc78b1731ac12c8db/kinto/plugins/openid/views.py#L97\n", "before_files": [{"content": "import urllib.parse\n\nimport colander\nimport requests\nfrom pyramid import httpexceptions\n\nfrom cornice.validators import colander_validator\nfrom kinto.core import Service\nfrom kinto.core.errors import raise_invalid, ERRORS\nfrom kinto.core.utils import random_bytes_hex\nfrom kinto.core.resource.schema import ErrorResponseSchema\nfrom kinto.core.schema import URL\n\nfrom .utils import fetch_openid_config\n\n\nDEFAULT_STATE_TTL_SECONDS = 3600\n\n\nclass RedirectHeadersSchema(colander.MappingSchema):\n    \"\"\"Redirect response headers.\"\"\"\n    location = colander.SchemaNode(colander.String(), name='Location')\n\n\nclass RedirectResponseSchema(colander.MappingSchema):\n    \"\"\"Redirect response schema.\"\"\"\n    headers = RedirectHeadersSchema()\n\n\nresponse_schemas = {\n    '307': RedirectResponseSchema(description='Successful redirection.'),\n    '400': ErrorResponseSchema(description='The request is invalid.'),\n}\n\n\ndef provider_validator(request, **kwargs):\n    \"\"\"\n    This validator verifies that the validator in URL (eg. /openid/auth0/login)\n    is a configured OpenIDConnect policy.\n    \"\"\"\n    provider = request.matchdict['provider']\n    used = request.registry.settings.get('multiauth.policy.%s.use' % provider, '')\n    if not used.endswith('OpenIDConnectPolicy'):\n        request.errors.add('path', 'provider', 'Unknow provider %r' % provider)\n\n\nclass LoginQuerystringSchema(colander.MappingSchema):\n    \"\"\"\n    Querystring schema for the login endpoint.\n    \"\"\"\n    callback = URL()\n    scope = colander.SchemaNode(colander.String())\n\n\nclass LoginSchema(colander.MappingSchema):\n    querystring = LoginQuerystringSchema()\n\n\nlogin = Service(name='openid_login',\n                path='/openid/{provider}/login',\n                description='Initiate the OAuth2 login')\n\n\n@login.get(schema=LoginSchema(),\n           validators=(colander_validator, provider_validator),\n           response_schemas=response_schemas)\ndef get_login(request):\n    \"\"\"Initiates to login dance for the specified scopes and callback URI\n    using appropriate redirections.\"\"\"\n\n    # Settings.\n    provider = request.matchdict['provider']\n    settings_prefix = 'multiauth.policy.%s.' % provider\n    issuer = request.registry.settings[settings_prefix + 'issuer']\n    client_id = request.registry.settings[settings_prefix + 'client_id']\n    userid_field = request.registry.settings.get(settings_prefix + 'userid_field')\n    state_ttl = int(request.registry.settings.get(settings_prefix + 'state_ttl_seconds',\n                                                  DEFAULT_STATE_TTL_SECONDS))\n\n    # Read OpenID configuration (cached by issuer)\n    oid_config = fetch_openid_config(issuer)\n    auth_endpoint = oid_config['authorization_endpoint']\n\n    scope = request.GET['scope']\n    callback = request.GET['callback']\n\n    # Check that email scope is requested if userid field is configured as email.\n    if userid_field == 'email' and 'email' not in scope:\n        error_details = {\n            'name': 'scope',\n            'description': \"Provider %s requires 'email' scope\" % provider,\n        }\n        raise_invalid(request, **error_details)\n\n    # Generate a random string as state.\n    # And save it until code is traded.\n    state = random_bytes_hex(256)\n    request.registry.cache.set('openid:state:' + state, callback, ttl=state_ttl)\n\n    # Redirect the client to the Identity Provider that will eventually redirect\n    # to the OpenID token endpoint.\n    token_uri = request.route_url('openid_token', provider=provider) + '?'\n    params = dict(client_id=client_id, response_type='code', scope=scope,\n                  redirect_uri=token_uri, state=state)\n    redirect = '{}?{}'.format(auth_endpoint, urllib.parse.urlencode(params))\n    raise httpexceptions.HTTPTemporaryRedirect(redirect)\n\n\nclass TokenQuerystringSchema(colander.MappingSchema):\n    \"\"\"\n    Querystring schema for the token endpoint.\n    \"\"\"\n    code = colander.SchemaNode(colander.String())\n    state = colander.SchemaNode(colander.String())\n\n\nclass TokenSchema(colander.MappingSchema):\n    querystring = TokenQuerystringSchema()\n\n\ntoken = Service(name='openid_token',\n                path='/openid/{provider}/token',\n                description='')\n\n\n@token.get(schema=TokenSchema(),\n           validators=(colander_validator, provider_validator))\ndef get_token(request):\n    \"\"\"Trades the specified code and state against access and ID tokens.\n    The client is redirected to the original ``callback`` URI with the\n    result in querystring.\"\"\"\n\n    # Settings.\n    provider = request.matchdict['provider']\n    settings_prefix = 'multiauth.policy.%s.' % provider\n    issuer = request.registry.settings[settings_prefix + 'issuer']\n    client_id = request.registry.settings[settings_prefix + 'client_id']\n    client_secret = request.registry.settings[settings_prefix + 'client_secret']\n\n    # Read OpenID configuration (cached by issuer)\n    oid_config = fetch_openid_config(issuer)\n    token_endpoint = oid_config['token_endpoint']\n\n    code = request.GET['code']\n    state = request.GET['state']\n\n    # State can be used only once.\n    callback = request.registry.cache.delete('openid:state:' + state)\n    if callback is None:\n        error_details = {\n            'name': 'state',\n            'description': 'Invalid state',\n            'errno': ERRORS.INVALID_AUTH_TOKEN.value,\n        }\n        raise_invalid(request, **error_details)\n\n    # Trade the code for tokens on the Identity Provider.\n    # Google Identity requires to specify again redirect_uri.\n    redirect_uri = request.route_url('openid_token', provider=provider) + '?'\n    data = {\n        'code': code,\n        'client_id': client_id,\n        'client_secret': client_secret,\n        'redirect_uri': redirect_uri,\n        'grant_type': 'authorization_code',\n    }\n    resp = requests.post(token_endpoint, data=data)\n\n    # The IdP response is forwarded to the client in the querystring/location hash.\n    # (eg. callback=`http://localhost:3000/#tokens=`)\n    redirect = callback + urllib.parse.quote(resp.text)\n    raise httpexceptions.HTTPTemporaryRedirect(redirect)\n", "path": "kinto/plugins/openid/views.py"}], "after_files": [{"content": "import urllib.parse\n\nimport colander\nimport requests\nfrom pyramid import httpexceptions\n\nfrom cornice.validators import colander_validator\nfrom kinto.core import Service\nfrom kinto.core.errors import raise_invalid, ERRORS\nfrom kinto.core.utils import random_bytes_hex\nfrom kinto.core.resource.schema import ErrorResponseSchema\nfrom kinto.core.schema import URL\n\nfrom .utils import fetch_openid_config\n\n\nDEFAULT_STATE_TTL_SECONDS = 3600\nDEFAULT_STATE_LENGTH = 32\n\n\nclass RedirectHeadersSchema(colander.MappingSchema):\n    \"\"\"Redirect response headers.\"\"\"\n    location = colander.SchemaNode(colander.String(), name='Location')\n\n\nclass RedirectResponseSchema(colander.MappingSchema):\n    \"\"\"Redirect response schema.\"\"\"\n    headers = RedirectHeadersSchema()\n\n\nresponse_schemas = {\n    '307': RedirectResponseSchema(description='Successful redirection.'),\n    '400': ErrorResponseSchema(description='The request is invalid.'),\n}\n\n\ndef provider_validator(request, **kwargs):\n    \"\"\"\n    This validator verifies that the validator in URL (eg. /openid/auth0/login)\n    is a configured OpenIDConnect policy.\n    \"\"\"\n    provider = request.matchdict['provider']\n    used = request.registry.settings.get('multiauth.policy.%s.use' % provider, '')\n    if not used.endswith('OpenIDConnectPolicy'):\n        request.errors.add('path', 'provider', 'Unknow provider %r' % provider)\n\n\nclass LoginQuerystringSchema(colander.MappingSchema):\n    \"\"\"\n    Querystring schema for the login endpoint.\n    \"\"\"\n    callback = URL()\n    scope = colander.SchemaNode(colander.String())\n\n\nclass LoginSchema(colander.MappingSchema):\n    querystring = LoginQuerystringSchema()\n\n\nlogin = Service(name='openid_login',\n                path='/openid/{provider}/login',\n                description='Initiate the OAuth2 login')\n\n\n@login.get(schema=LoginSchema(),\n           validators=(colander_validator, provider_validator),\n           response_schemas=response_schemas)\ndef get_login(request):\n    \"\"\"Initiates to login dance for the specified scopes and callback URI\n    using appropriate redirections.\"\"\"\n\n    # Settings.\n    provider = request.matchdict['provider']\n    settings_prefix = 'multiauth.policy.%s.' % provider\n    issuer = request.registry.settings[settings_prefix + 'issuer']\n    client_id = request.registry.settings[settings_prefix + 'client_id']\n    userid_field = request.registry.settings.get(settings_prefix + 'userid_field')\n    state_ttl = int(request.registry.settings.get(settings_prefix + 'state_ttl_seconds',\n                                                  DEFAULT_STATE_TTL_SECONDS))\n    state_length = int(request.registry.settings.get(settings_prefix + 'state_length',\n                                                     DEFAULT_STATE_LENGTH))\n\n    # Read OpenID configuration (cached by issuer)\n    oid_config = fetch_openid_config(issuer)\n    auth_endpoint = oid_config['authorization_endpoint']\n\n    scope = request.GET['scope']\n    callback = request.GET['callback']\n\n    # Check that email scope is requested if userid field is configured as email.\n    if userid_field == 'email' and 'email' not in scope:\n        error_details = {\n            'name': 'scope',\n            'description': \"Provider %s requires 'email' scope\" % provider,\n        }\n        raise_invalid(request, **error_details)\n\n    # Generate a random string as state.\n    # And save it until code is traded.\n    state = random_bytes_hex(state_length)\n    request.registry.cache.set('openid:state:' + state, callback, ttl=state_ttl)\n\n    # Redirect the client to the Identity Provider that will eventually redirect\n    # to the OpenID token endpoint.\n    token_uri = request.route_url('openid_token', provider=provider) + '?'\n    params = dict(client_id=client_id, response_type='code', scope=scope,\n                  redirect_uri=token_uri, state=state)\n    redirect = '{}?{}'.format(auth_endpoint, urllib.parse.urlencode(params))\n    raise httpexceptions.HTTPTemporaryRedirect(redirect)\n\n\nclass TokenQuerystringSchema(colander.MappingSchema):\n    \"\"\"\n    Querystring schema for the token endpoint.\n    \"\"\"\n    code = colander.SchemaNode(colander.String())\n    state = colander.SchemaNode(colander.String())\n\n\nclass TokenSchema(colander.MappingSchema):\n    querystring = TokenQuerystringSchema()\n\n\ntoken = Service(name='openid_token',\n                path='/openid/{provider}/token',\n                description='')\n\n\n@token.get(schema=TokenSchema(),\n           validators=(colander_validator, provider_validator))\ndef get_token(request):\n    \"\"\"Trades the specified code and state against access and ID tokens.\n    The client is redirected to the original ``callback`` URI with the\n    result in querystring.\"\"\"\n\n    # Settings.\n    provider = request.matchdict['provider']\n    settings_prefix = 'multiauth.policy.%s.' % provider\n    issuer = request.registry.settings[settings_prefix + 'issuer']\n    client_id = request.registry.settings[settings_prefix + 'client_id']\n    client_secret = request.registry.settings[settings_prefix + 'client_secret']\n\n    # Read OpenID configuration (cached by issuer)\n    oid_config = fetch_openid_config(issuer)\n    token_endpoint = oid_config['token_endpoint']\n\n    code = request.GET['code']\n    state = request.GET['state']\n\n    # State can be used only once.\n    callback = request.registry.cache.delete('openid:state:' + state)\n    if callback is None:\n        error_details = {\n            'name': 'state',\n            'description': 'Invalid state',\n            'errno': ERRORS.INVALID_AUTH_TOKEN.value,\n        }\n        raise_invalid(request, **error_details)\n\n    # Trade the code for tokens on the Identity Provider.\n    # Google Identity requires to specify again redirect_uri.\n    redirect_uri = request.route_url('openid_token', provider=provider) + '?'\n    data = {\n        'code': code,\n        'client_id': client_id,\n        'client_secret': client_secret,\n        'redirect_uri': redirect_uri,\n        'grant_type': 'authorization_code',\n    }\n    resp = requests.post(token_endpoint, data=data)\n\n    # The IdP response is forwarded to the client in the querystring/location hash.\n    # (eg. callback=`http://localhost:3000/#tokens=`)\n    redirect = callback + urllib.parse.quote(resp.text)\n    raise httpexceptions.HTTPTemporaryRedirect(redirect)\n", "path": "kinto/plugins/openid/views.py"}]}