diff --git a/msal/throttled_http_client.py b/msal/throttled_http_client.py index 8cabe261..eddcc9bb 100644 --- a/msal/throttled_http_client.py +++ b/msal/throttled_http_client.py @@ -164,6 +164,10 @@ def __init__(self, *args, default_throttle_time=None, **kwargs): isinstance(kwargs.get("data"), dict) and kwargs["data"].get("grant_type") == DEVICE_AUTH_GRANT ) + and not( # Exclude ROPC, otherwise its key would be a password hash + isinstance(kwargs.get("data"), dict) + and kwargs["data"].get("grant_type") == "password" + ) and RetryAfterParser.FIELD_NAME_LOWER not in set( # Otherwise leave it to the Retry-After decorator h.lower() for h in _get_headers(result)) else 0, diff --git a/tests/test_throttled_http_client.py b/tests/test_throttled_http_client.py index f8fa6551..97b63ec6 100644 --- a/tests/test_throttled_http_client.py +++ b/tests/test_throttled_http_client.py @@ -250,6 +250,18 @@ def test_device_flow_retry_should_not_be_cached(self): logger.debug(http_cache) self.assertNotEqual(resp1.text, resp2.text, "Should return a new response") + def test_ropc_error_should_not_be_cached(self): + http_cache = {} + http_client = DummyHttpClient(status_code=400) + http_client = ThrottledHttpClient(http_client, http_cache=http_cache) + data = {"grant_type": "password", "username": "user", "password": "secret"} + resp1 = http_client.post("https://example.com", data=data) + resp2 = http_client.post("https://example.com", data=data) + logger.debug(http_cache) + self.assertNotEqual(resp1.text, resp2.text, "Should return a new response") + self.assertEqual({}, http_cache, + "A key derived from the password should not be stored in http_cache") + def test_throttled_http_client_should_provide_close(self): http_client = DummyHttpClient(status_code=200) http_client = ThrottledHttpClient(http_client)