Repository navigation
fix(security): sign JWTs with PyJWT, and drop python-jose and ecdsa (#422) - #423
Merged
Merged
Conversation
4 of 5 tasks
…422) python-jose was the only reason ecdsa, rsa and pyasn1 were in the backend image, and ecdsa carried PYSEC-2026-1325 (CVE-2024-23342, the Minerva timing attack on ECDSA signing): no version fixes it, so it sat in the pip-audit baseline as a permanent exception, re-justified every quarter for a library UKIP never used. UKIP signs HS256 only, which PyJWT covers with no dependencies. - backend/auth.py, routers/auth_users.py, routers/ws.py: import PyJWT, and PyJWTError under the JWTError name the six call sites already catch, so every refusal (expired, wrong key, tampered, unsigned, another algorithm, garbage) lands where it did. The key-rotation loop is unchanged. - requirements.txt: PyJWT instead of python-jose[cryptography]. requirements.lock: PyJWT==2.15.1 in, python-jose, ecdsa, rsa and pyasn1 out. Verified the way the lock's header requires: built Dockerfile.backend and compared its pip freeze. Nothing is installed that the lock does not pin, PyJWT is in, the four are out, and the only change to the lock-only set is those four leaving it. - security.yml and SECURITY_GATES.md §7e: PYSEC-2026-1325 removed rather than ignored; pip-audit reports "No known vulnerabilities found, 6 ignored" without it. Nobody is signed out: test_jwt_library.py pins an access token and a refresh token (the latter signed with a retiring key) minted by python-jose 3.5.0 with production's claims, and both decode under PyJWT. It also checks every refusal raises the type the call sites catch, that only HS256 is accepted, and that nothing in backend/ imports jose. The JWT, SSO and session tests (55) pass with python-jose uninstalled. Closes #422. Co-Authored-By: Claude Opus 5.5 <[email protected]>
keilynrp
force-pushed
the
fix/replace-python-jose-with-pyjwt
branch
from
September 30, 2026 19:50
3a3de91 to
cc3c232
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #422.
Why
python-josewas the only reasonecdsa,rsaandpyasn1were in the backend image.ecdsacarried PYSEC-2026-1325 (CVE-2024-23342, the Minerva timing attack on ECDSA signing), and no version fixes it. So it sat in the pip-audit baseline as a permanent exception, re-justified every quarter, for a library UKIP never used. UKIP signs HS256 only, which PyJWT covers with no dependencies.Changes
backend/auth.py,routers/auth_users.pyandrouters/ws.pyimport PyJWT.PyJWTErrorkeeps theJWTErrorname, so the sixexceptsites catch every refusal as before. The key-rotation loop is unchangedPyJWT==2.15.1in;python-jose,ecdsa,rsaandpyasn1outNo known vulnerabilities found, 6 ignored(chromadb and oauthlib only)Nobody is signed out
test_jwt_library.pypins an access token and a refresh token minted by python-jose 3.5.0 with production's claims; the refresh token is signed with a retiring key. Both decode under PyJWT.The same file checks that:
alg: none, HS512 with the real key, garbage);backend/importsjose.Verified
Dockerfile.backendand compared itspip freeze.🤖 Generated with Claude Code