Signature verification failed with just generated tokens
flixman opened this issue · 1 comments
I have flask-jwt-extended configured with the following settings, with an app running on a docker container behind nginx:
JWT_SECRET_KEY = secrets.token_urlsafe(24)
JWT_ACCESS_TOKEN_EXPIRES = timedelta(hours=1)
JWT_TOKEN_LOCATION = ['cookies']
JWT_COOKIE_CSRF_PROTECT = False
JWT_CSRF_CHECK_FORM = False
JWT_COOKIE_SECURE = True
JWT_COOKIE_SAMESITE = "Strict"
In my locust test I have the following:
def on_start(self):
response = self.client.get('http://127.0.0.1/api/csrf')
self.headers = {'X-CSRF-Token': response.json()['csrf']}
self.cookies = dict(response.cookies.iteritems())
response = self.client.post('http://127.0.0.1/api/auth/login', json=user_credentials.pop(), headers=self.headers, cookies=self.cookies)
if response.status_code != HTTPStatus.OK:
raise RuntimeError('Authentication did not succeed')
self.cookies |= dict(response.cookies.iteritems())
# get all the lists for the user
lists = self.client.get('http://127.0.0.1/api/todolists', headers=self.headers, cookies=self.cookies)
self.cookies |= dict(lists.cookies.iteritems())
# request their deletion
for l in lists.json():
response = self.client.get('http://127.0.0.1/api/csrf', cookies=self.cookies)
self.headers |= {'X-CSRF-Token': response.json()['csrf']}
self.cookies |= dict(response.cookies.iteritems())
response = self.client.delete(f'http://127.0.0.1/api/todolists/{l["id"]}', headers=self.headers, cookies=self.cookies)
self.cookies |= dict(response.cookies.iteritems())
when running this test against the server, I see the following:
nginx-1 | 172.19.0.1 - - [23/Sep/2023:19:14:20 +0000] "GET /api/csrf HTTP/1.1" 200 103 "-" "python-requests/2.31.0" "-"
nginx-1 | 172.19.0.1 - - [23/Sep/2023:19:14:20 +0000] "POST /api/auth/login HTTP/1.1" 200 0 "-" "python-requests/2.31.0" "-"
nginx-1 | 172.19.0.1 - - [23/Sep/2023:19:14:20 +0000] "GET /api/todolists HTTP/1.1" 200 172 "-" "python-requests/2.31.0" "-"
nginx-1 | 172.19.0.1 - - [23/Sep/2023:19:14:20 +0000] "GET /api/csrf HTTP/1.1" 200 103 "-" "python-requests/2.31.0" "-"
nginx-1 | 172.19.0.1 - - [23/Sep/2023:19:14:20 +0000] "DELETE /api/todolists/2c16ce48-3e7e-4e46-8982-5ae64d418d56 HTTP/1.1" 200 0 "-" "python-requests/2.31.0" "-"
nginx-1 | 172.19.0.1 - - [23/Sep/2023:19:14:20 +0000] "GET /api/csrf HTTP/1.1" 200 103 "-" "python-requests/2.31.0" "-"
nginx-1 | 172.19.0.1 - - [23/Sep/2023:19:14:20 +0000] "DELETE /api/todolists/90f863aa-4178-4295-8ef8-2b03898fcbb6 HTTP/1.1" 200 0 "-" "python-requests/2.31.0" "-"
nginx-1 | 172.19.0.1 - - [23/Sep/2023:19:14:20 +0000] "GET /api/csrf HTTP/1.1" 200 103 "-" "python-requests/2.31.0" "-"
nginx-1 | 172.19.0.1 - - [23/Sep/2023:19:14:20 +0000] "POST /api/todolists/add HTTP/1.1" 201 85 "-" "python-requests/2.31.0" "-"
nginx-1 | 172.19.0.1 - - [23/Sep/2023:19:14:23 +0000] "GET /api/csrf HTTP/1.1" 200 103 "-" "python-requests/2.31.0" "-"
internal-1 | Signature verification failed
nginx-1 | 172.19.0.1 - - [23/Sep/2023:19:14:23 +0000] "POST /api/todolists/add HTTP/1.1" 302 199 "-" "python-requests/2.31.0" "-"
internal-1 | Signature verification failed
the first GET for todolists as well as the DELETES are OK, but when the next phase goes on (so, getting another csrf and trying to POST a request to /api/todolists/add) then I get Signature verification failed. Few queries later the verification succeeds, and then fails again.
For the verification I am doing the following:
@app.before_request
def before_request():
# the only routes that do not require authentication are the endpoint to login and to retrieve the csrf
if request.path in [url_for("api.auth.login"), url_for("api.get_csrf")]:
return None
try:
verify_jwt_in_request()
except (NoAuthorizationError, ExpiredSignatureError, InvalidSignatureError) as _exc:
# return static files
return None
and for the refreshing of the token I have the following:
@app.after_request
def refresh_expiring_jwts(response):
if not request.blueprint or not request.blueprint.startswith('api'):
return response
try:
verify_jwt_in_request(refresh=True)
access_token = create_access_token(identity=get_jwt_identity())
set_access_cookies(response, access_token)
return response
except (RuntimeError, NoAuthorizationError, InvalidSignatureError):
pass
finally:
return response
Am I doing something wrong?
And the answer is: yes, I was doing something wrong. The problem was that I was running flask threaded, and I was setting the variables SECRET_KEY and JWT_SECRET_KEY to a random value generated at boot time. Should I change that to a random string that is constant, then everything works (because all the threads are getting the same value).