GayathriSrividya commented on code in PR #68499:
URL: https://github.com/apache/airflow/pull/68499#discussion_r3411054737


##########
airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_router.py:
##########
@@ -61,9 +120,49 @@ def test_expiring_token_is_reissued(
     lifespan.registry.register_value(JWTValidator, auth)
     # In order to test this we need any endpoint to hit. The easiest one to 
use is variable get
 
-    response = client.get("/execution/variables/key1", 
headers={"Authorization": "Bearer dummy"})
+    response = jwt_bearer_client.get("/execution/variables/key1", 
headers={"Authorization": "Bearer dummy"})
 
     if expect_refreshed_token:
         assert "Refreshed-API-Token" in response.headers
     else:
         assert "Refreshed-API-Token" not in response.headers
+    # avalidated_claims must be called exactly once — by JWTBearer only, not 
by the middleware.
+    auth.avalidated_claims.assert_awaited_once_with("dummy", {})
+
+
[email protected]_test
+def test_token_expiring_mid_request_is_reissued_without_revalidation(
+    jwt_bearer_client, exec_app: FastAPI, time_machine
+):
+    """Middleware reissues from cached JWTBearer claims without re-validating 
the token.
+
+    Regression test for the TOCTOU race in JWTReissueMiddleware: a heartbeat 
arrives with a
+    token that has ~0s left, JWTBearer validates it (still technically valid 
at that moment),
+    the request starts, and the middleware runs. In the old code the 
middleware would call
+    avalidated_claims a second time and get ExpiredSignatureError — no 
Refreshed-API-Token
+    header would be set, and the task would die on the next heartbeat.
+
+    With the fix the middleware reads claims from request.scope (set by 
JWTBearer) instead of
+    calling avalidated_claims again, so it still issues a fresh token even 
when the original
+    has since expired.
+    """
+    moment = 1743451846
+    auth = AsyncMock(spec=JWTValidator)
+    auth.avalidated_claims.return_value = {
+        "sub": "edb09971-4e0e-4221-ad3f-800852d38085",
+        "iat": moment,
+        "exp": moment + 600,
+    }
+
+    # Move time to 1 second past the token's expiry. JWTBearer already 
accepted the token
+    # (mocked); the middleware must still issue a refresh using the cached 
claims rather than
+    # silently dropping it.
+    time_machine.move_to(moment + 601, tick=False)
+
+    lifespan.registry.register_value(JWTValidator, auth)
+
+    response = jwt_bearer_client.get("/execution/variables/key1", 
headers={"Authorization": "Bearer dummy"})
+
+    assert "Refreshed-API-Token" in response.headers

Review Comment:
   @ashb 
   - Fixed! The test now properly validates the TOCTOU scenario:
   
   **Test:** test_router.py
   - Token expires mid-request (line 157: `time_machine.move_to(moment + 601)`)
   - Middleware still refreshes (line 163: `assert "Refreshed-API-Token" in 
response.headers`)
   - No re-validation happens (line 164: `assert_awaited_once_with` proves 
`avalidated_claims` called only once)
   
   **Fix:** app.py
   - Middleware reads from `request.scope` (cached by JWTBearer) instead of 
calling `avalidated_claims` again
   - Guarantees refresh even if token expired between request start and 
middleware execution
   
   Let me know if any other method is needed to address the issue.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to