Fix Tado reusing an expired device code when retrying after login timeout (#176832)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Erwin Douna <e.douna@gmail.com>
This commit is contained in:
Nik Rahmel
2026-07-21 14:34:42 +01:00
committed by GitHub
co-authored by Claude Fable 5 Erwin Douna
parent ebcdf5ac26
commit c553bce202
2 changed files with 46 additions and 17 deletions
+2 -1
View File
@@ -167,7 +167,8 @@ class TadoConfigFlow(ConfigFlow, domain=DOMAIN):
return self.async_show_form(
step_id="timeout",
)
del self.login_task
self.login_task = None
self.tado = None
return await self.async_step_user()
@override
+44 -16
View File
@@ -110,26 +110,54 @@ async def test_auth_timeout(
mock_tado_api: MagicMock,
mock_setup_entry: AsyncMock,
) -> None:
"""Test the auth timeout."""
mock_tado_api.device_activation_status.return_value = DeviceActivationStatus.PENDING
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
"""Test that retrying after an auth timeout uses a fresh device code."""
expired_client = MagicMock()
expired_client.device_verification_url.return_value = (
"https://login.tado.com/oauth2/device?user_code=EXPIRED"
)
assert result["type"] is FlowResultType.SHOW_PROGRESS_DONE
assert result["step_id"] == "timeout"
mock_tado_api.device_activation_status.return_value = (
DeviceActivationStatus.COMPLETED
expired_client.device_activation_status.return_value = (
DeviceActivationStatus.PENDING
)
result = await hass.config_entries.flow.async_configure(result["flow_id"])
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "timeout"
event = threading.Event()
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={}
)
def mock_tado_api_device_activation() -> None:
# Simulate the device activation process
event.wait(timeout=5)
mock_tado_api.device_activation = mock_tado_api_device_activation
with patch(
"homeassistant.components.tado.config_flow.Tado",
side_effect=[expired_client, mock_tado_api],
) as mock_tado_create:
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.SHOW_PROGRESS_DONE
assert result["step_id"] == "timeout"
result = await hass.config_entries.flow.async_configure(result["flow_id"])
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "timeout"
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={}
)
# The retry must construct a new client and show its device code,
# not the expired one from the first attempt
assert mock_tado_create.call_count == 2
assert result["type"] is FlowResultType.SHOW_PROGRESS
assert result["description_placeholders"] == {
"url": "https://login.tado.com/oauth2/device?user_code=TEST",
"code": "TEST",
}
event.set()
await hass.async_block_till_done()
result = await hass.config_entries.flow.async_configure(result["flow_id"])
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "home name"