Fix access token collision and cross-file username collision

- auth.py: _create_access_token had no jti claim, so two refreshes
  within the same wall-clock second produced byte-identical access
  tokens (same failure mode already fixed for refresh tokens earlier)
- test_refresh_endpoint.py: usernames (jwttoken, refreshuser, etc.)
  collided with test_auth_endpoints.py's own test usernames once run
  in the same pytest session against a shared DB with no per-test
  isolation -- exactly how run_checkpoint_tests.py invokes pytest.
  Namespaced this file's usernames with a refep_ prefix.

Full backend suite (63 tests) passes clean end-to-end against a real
Postgres with real migrations applied.
This commit is contained in:
2026-08-19 22:15:55 +00:00
parent 3e81d60268
commit 3cb4d01073
2 changed files with 14 additions and 7 deletions
+8 -1
View File
@@ -66,8 +66,15 @@ class UserResponse(BaseModel):
# --- Helpers ---
def _create_access_token(data: dict) -> str:
"""Create a signed access token using RS256."""
"""Create a signed access token using RS256.
Includes a unique ``jti`` claim so that concurrent/same-second calls
(e.g. back-to-back /refresh requests) still produce distinct tokens —
without it, two tokens minted for the same user within the same
second are byte-identical since ``sub``/``exp`` are the only claims.
"""
to_encode = data.copy()
to_encode["jti"] = str(uuid.uuid4())
expire = datetime.now(timezone.utc) + timedelta(
minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
)
+6 -6
View File
@@ -67,7 +67,7 @@ class TestRefresh:
def test_refresh_success(self):
"""A valid refresh token returns a new access token."""
refresh_token = self._register_user("refreshuser", "secret123")
refresh_token = self._register_user("refep_refreshuser", "secret123")
client = TestClient(app)
resp = client.post(
"/api/auth/refresh",
@@ -82,7 +82,7 @@ class TestRefresh:
def test_refresh_returns_different_access_token(self):
"""Refreshing produces a new access token (different from any previous)."""
refresh_token = self._register_user("refreshtoken", "secret123")
refresh_token = self._register_user("refep_refreshtoken", "secret123")
client = TestClient(app)
# First refresh
@@ -140,7 +140,7 @@ class TestRefresh:
client.post(
"/api/auth/register",
json={
"username": "loginrefuser",
"username": "refep_loginrefuser",
"email": "loginref@example.com",
"password": "secret123",
"date_of_birth": "2000-01-01",
@@ -149,7 +149,7 @@ class TestRefresh:
# Login to get a refresh token
login_resp = client.post(
"/api/auth/login",
data={"username": "loginrefuser", "password": "secret123"},
data={"username": "refep_loginrefuser", "password": "secret123"},
)
assert login_resp.status_code == 200
refresh_token = login_resp.json()["refresh_token"]
@@ -166,7 +166,7 @@ class TestRefresh:
"""The access token returned by /refresh is a valid JWT."""
from jose import jwt
refresh_token = self._register_user("jwttoken", "secret123")
refresh_token = self._register_user("refep_jwttoken", "secret123")
client = TestClient(app)
resp = client.post(
"/api/auth/refresh",
@@ -190,7 +190,7 @@ class TestRefresh:
def test_refresh_expires_minutes_in_response(self):
"""The response includes access_token_expires_minutes."""
refresh_token = self._register_user("expiretest", "secret123")
refresh_token = self._register_user("refep_expiretest", "secret123")
client = TestClient(app)
resp = client.post(
"/api/auth/refresh",