Skip to content

Commit 00048a7

Browse files
authored
Merge pull request #35 from tunisiano187/claude/gifted-babbage-hhngmb
fix(deps): replace python-jose with PyJWT, bump aiohttp/cryptography/react-router
2 parents e55893e + 4f1ce6d commit 00048a7

8 files changed

Lines changed: 113 additions & 194 deletions

File tree

.github/workflows/ci.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ jobs:
2525
pip install --upgrade pip
2626
# Install only what the tests need — avoids building heavy native
2727
# extensions (asyncpg, cryptography/bcrypt, etc.) that are mocked.
28-
# python-jose and passlib are installed without extras to avoid cffi/C-extension issues.
29-
# HS256 only needs Python stdlib hmac; bcrypt is handled by passlib's pbkdf2 fallback.
28+
# PyJWT uses stdlib hmac for HS256 — no cffi/C-extension needed.
29+
# bcrypt is handled by passlib's pbkdf2 fallback in test environments.
3030
pip install \
3131
pytest>=8 pytest-asyncio>=0.23 httpx>=0.27 ruff>=0.4 \
3232
fastapi>=0.111 pydantic>=2.7 "pydantic-settings>=2.3" \
3333
"sqlalchemy[asyncio]>=2.0" pyyaml>=6.0 \
3434
jinja2>=3.1 \
35-
"passlib>=1.7" "python-jose>=3.3" "ecdsa>=0.18" "rsa>=4.0"
35+
"passlib>=1.7" "PyJWT>=2.9"
3636
3737
- name: Lint (ruff)
3838
run: ruff check .
@@ -194,8 +194,8 @@ jobs:
194194
fastapi>=0.111 pydantic>=2.7 "pydantic-settings>=2.3" \
195195
"sqlalchemy[asyncio]>=2.0" "asyncpg>=0.29" "alembic>=1.13" \
196196
pyyaml>=6.0 jinja2>=3.1 \
197-
"passlib>=1.7" "python-jose>=3.3" "ecdsa>=0.18" "rsa>=4.0" \
198-
"cryptography>=42"
197+
"passlib>=1.7" "PyJWT>=2.9" \
198+
"cryptography>=50"
199199
200200
- name: Run Alembic migrations
201201
run: alembic upgrade head

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ dependencies = [
2525
"httpx>=0.27",
2626

2727
# Auth
28-
"python-jose[cryptography]>=3.3",
28+
"PyJWT>=2.9",
2929
"passlib[bcrypt]>=1.7",
3030

3131
# Crypto (CA / mTLS)
32-
"cryptography>=42",
32+
"cryptography>=50",
3333

3434
# Redis
3535
"redis>=5.0",

server/services/auth.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ async def delete_user(user: str = Depends(require_min_role("admin"))):
3434
from fastapi import Depends, HTTPException, status
3535
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
3636

37-
from jose import JWTError, jwt
37+
import jwt
38+
from jwt.exceptions import InvalidTokenError as JWTError
3839
from passlib.context import CryptContext
3940

4041
logger = logging.getLogger(__name__)

tests/conftest.py

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,17 @@ def _stub_module(name: str) -> MagicMock:
2727
if _name not in sys.modules:
2828
_stub_module(_name)
2929

30-
# Stub jose and passlib ONLY when the native cryptography extension is broken.
30+
# Stub passlib ONLY when the native cryptography extension is broken.
3131
#
3232
# In CI (GitHub Actions) all packages are properly installed and working.
3333
# In the dev container the `cryptography` Rust extension (loaded via _cffi_backend)
34-
# is broken — importing jose triggers a Rust thread panic (PanicException).
34+
# is broken — importing passlib[bcrypt] triggers a Rust thread panic (PanicException).
35+
#
36+
# PyJWT (our JWT library) uses stdlib hmac for HS256 and does NOT require cffi,
37+
# so it works even in broken-cffi environments — no stub needed for jwt.
3538
#
3639
# We detect the broken environment by probing _cffi_backend with a plain
37-
# ImportError (not a Rust panic) BEFORE touching jose/cryptography.
40+
# ImportError (not a Rust panic) BEFORE touching passlib/cryptography.
3841

3942
def _cffi_available() -> bool:
4043
"""Return True if the _cffi_backend C extension loads cleanly."""
@@ -46,27 +49,8 @@ def _cffi_available() -> bool:
4649

4750

4851
if not _cffi_available():
49-
# Native crypto stack is broken — stub jose and passlib so auth-service
50-
# imports work without the cryptography native extension.
51-
import json as _json
52-
from datetime import datetime as _datetime
53-
54-
_jose = _stub_module("jose")
55-
_jose.JWTError = Exception
56-
57-
def _fake_jwt_encode(payload, *args, **kwargs):
58-
safe = {k: v.isoformat() if isinstance(v, _datetime) else v for k, v in payload.items()}
59-
return _json.dumps(safe)
60-
61-
def _fake_jwt_decode(token, *args, **kwargs):
62-
return _json.loads(token)
63-
64-
_jose.jwt = MagicMock()
65-
_jose.jwt.encode = _fake_jwt_encode
66-
_jose.jwt.decode = _fake_jwt_decode
67-
_stub_module("jose.jwt")
68-
_stub_module("jose.exceptions")
69-
52+
# Native crypto stack is broken — stub passlib so auth-service imports
53+
# work without the cryptography native extension.
7054
_passlib = _stub_module("passlib")
7155
_passlib_ctx = _stub_module("passlib.context")
7256
_ctx_cls = MagicMock()

tests/test_auth.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def test_role_preserved(self) -> None:
4444

4545
def test_legacy_token_defaults_to_admin(self) -> None:
4646
"""Tokens without a role claim (pre-RBAC) are treated as admin."""
47-
from jose import jwt
47+
import jwt
4848
from datetime import datetime, timedelta, timezone
4949

5050
payload = {

ui/package-lock.json

Lines changed: 40 additions & 54 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"dependencies": {
1212
"react": "^18.3.1",
1313
"react-dom": "^18.3.1",
14-
"react-router-dom": "^6.30.4"
14+
"react-router-dom": "^7.18.2"
1515
},
1616
"devDependencies": {
1717
"@types/react": "^18.3.28",

0 commit comments

Comments
 (0)