diff --git a/backend/app/routers/auth.py b/backend/app/routers/auth.py index 79a5f91..61e99ae 100644 --- a/backend/app/routers/auth.py +++ b/backend/app/routers/auth.py @@ -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 ) diff --git a/backend/tests/test_refresh_endpoint.py b/backend/tests/test_refresh_endpoint.py index 995bf0f..92e4322 100644 --- a/backend/tests/test_refresh_endpoint.py +++ b/backend/tests/test_refresh_endpoint.py @@ -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",