Problem with PyJWT 2.10: `{'msg': 'Subject must be a string'}`
amotl opened this issue · 13 comments
Bug description
When setting up a fresh sandbox environment, PyJWT 2.10 gets installed, released on Nov 17, i.e. three days ago. That breaks a little integration test suite we are running 1. This is the exception being raised:
AssertionError: {'msg': 'Subject must be a string'}
When downgrading to use pyjwt<2.10
, the test suite succeeds again.
You may want to accompany this by potentially adjusting dependencies or code in Apache Superset?
Superset version
3.x and 4.x
Additional context
The software test suite maintained here can be used to reproduce the problem.
We added relevant details to this ticket, where we started to investigate this issue.
Checklist
- I have searched Superset docs and Slack and didn't find a solution to my problem.
- I have searched the GitHub issue tracker and didn't find a similar bug report.
- I have checked Superset's logs for errors and if I found a relevant Python stacktrace, I included it here as text in the "additional context" section.
Footnotes
We just found this is most likely the root cause.
The canonical recommendation is to downgrade to PyJWT in the meanwhile.
pip install 'pyjwt<2.10'
That patch submitted by Dependabot also demonstrates the problem.
@jkogut: Do you have any idea why only we might be affected, but Superset's test suite seems to still succeed, and nobody else seems to be tripped? Is it related to the value of the SECRET_KEY maybe?
@amotl yes indeed that was caused by the incorrect SECRET_KEY. Happened during test migration of superset instance. So indeed please double check SECRET_KEY. 🙏
@amotl actually I was wrong, correct SECRET_KEY only allowed me to get access token but cannot proceed further with getting chart list for instance:
def get_chart_list():
headers = {
'Authorization': f"Bearer {get_bearer_token()}"
}
response = requests.get(f"{base_url}/chart/", headers=headers, verify=False)
return response.json()
problem observed as reported on Superset 3.1.3, and fixed as recommended with installing PyJWT==2.9.0.
So still looks like new PyJWT 2.10.0 release can cause some problems with Superset API access.
We re-generated the value for SECRET_KEY
using openssl rand -base64 42
, as advertised in Superset's documentation, but it did not improve the situation.
Summary
- Relevant change: jpadilla/pyjwt#1005
- Problem report: jpadilla/pyjwt#1017
- Solution: vimalloc/flask-jwt-extended#556
- Downstream reports:
- Downstream patches:
Evaluation
Because Apache Superset is based on Flask-AppBuilder, which in turn pulls in Flask-JWT-Extended, we guess the most recent release 4.7.1 resolves this problem already.
We can still observe the problem, even after upgrading to Flask-JWT-Extended 4.7.1.
$ uv pip install --upgrade "PyJWT>=2.10" "Flask-JWT-Extended>=4.7.1" "Flask<3"
- flask-jwt-extended==4.7.0
+ flask-jwt-extended==4.7.1
pytest -k api
response = requests.delete(resource_to_delete, headers=get_auth_headers())
> assert response.status_code in [200, 404], response.json()
E AssertionError: {'msg': 'Subject must be a string'}
E assert 422 in [200, 404]
E + where 422 = <Response [422]>.status_code
conftest.py:74: AssertionError
Have you configured flask-jwt-extended to not verify the sub claim? 'JWT_VERIFY_SUB=False'
Thank you so much for your quick response, @jlucier. Actually, we are just approaching this problem as users of Apache Superset, not having any knowledge about its code base and its dependencies at all, other than knowing a bit about Flask.
We found that Flask-JWT-Extended is being used by Flask-AppBuilder, so this is probably the right spot to address this configuration update needed for the most recent PyJWT library? In this spirit, we figure creating a corresponding issue over there makes sense?
Please correct me if I am wrong with my evaluations.
/cc @dpgaspar
Since my patch, this isn't a problem with flask-jwt-extended anymore. If you don't want enforce that the sub claim is a string, you just need to configure 'JWT_VERIFY_SUB' to be False. That's your choice to make, not the library's.
As a user of Apache Superset, I am was not touching JWT concerns at all, so [I thought] I can't make any choices about how JWT is used by its ingredients.
In this case, I guess it will be the concern of Flask-AppBuilder to configure Flask-JWT-Extended correctly, that's why I created a ticket there, but I may be wrong. Please educate me where you see a flaw in my thinking, as I don't have any strong knowledge of the subsystems we are talking about.
All right, I wasn't aware that JWT_VERIFY_SUB = False
could easily be configured within the Flask application configuration file, which is effectively the superset_config.py
file in our case.
After getting it right, this update perfectly resolves the problem we have been observing.
Thank you so much! 💯