Fix OAuth reauth in Tesla Fleet (#124744)

* Fix auth failure

* Test

* Fix test

* Only reauth on 401

* Cover 401 and others

* Update homeassistant/components/tesla_fleet/strings.json

Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>

---------

Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
This commit is contained in:
Brett Adams 2024-08-28 22:58:16 +10:00 committed by GitHub
parent 11370979e5
commit e39b3796f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 61 additions and 5 deletions

View file

@ -3,6 +3,7 @@
import asyncio
from typing import Final
from aiohttp.client_exceptions import ClientResponseError
import jwt
from tesla_fleet_api import EnergySpecific, TeslaFleetApi, VehicleSpecific
from tesla_fleet_api.const import Scope
@ -66,7 +67,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslaFleetConfigEntry) -
async def _refresh_token() -> str:
async with refresh_lock:
await oauth_session.async_ensure_token_valid()
try:
await oauth_session.async_ensure_token_valid()
except ClientResponseError as e:
if e.status == 401:
raise ConfigEntryAuthFailed from e
raise ConfigEntryNotReady from e
token: str = oauth_session.token[CONF_ACCESS_TOKEN]
return token

View file

@ -83,5 +83,8 @@ class OAuth2FlowHandler(
) -> ConfigFlowResult:
"""Confirm reauth dialog."""
if user_input is None:
return self.async_show_form(step_id="reauth_confirm")
return self.async_show_form(
step_id="reauth_confirm",
description_placeholders={"name": "Tesla Fleet"},
)
return await self.async_step_user()

View file

@ -19,7 +19,7 @@
},
"reauth_confirm": {
"title": "[%key:common::config_flow::title::reauth%]",
"description": "The Withings integration needs to re-authenticate your account"
"description": "The {name} integration needs to re-authenticate your account"
}
},
"create_entry": {

View file

@ -124,7 +124,7 @@ def mock_site_info() -> Generator[AsyncMock]:
yield mock_live_status
@pytest.fixture(autouse=True)
@pytest.fixture
def mock_find_server() -> Generator[AsyncMock]:
"""Mock Tesla Fleet find server method."""
with patch(

View file

@ -1,7 +1,9 @@
"""Test the Tesla Fleet init."""
from unittest.mock import AsyncMock
from unittest.mock import AsyncMock, patch
from aiohttp import RequestInfo
from aiohttp.client_exceptions import ClientResponseError
from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy.assertion import SnapshotAssertion
@ -16,6 +18,7 @@ from tesla_fleet_api.exceptions import (
VehicleOffline,
)
from homeassistant.components.tesla_fleet.const import AUTHORIZE_URL
from homeassistant.components.tesla_fleet.coordinator import (
ENERGY_INTERVAL,
ENERGY_INTERVAL_SECONDS,
@ -72,6 +75,50 @@ async def test_init_error(
assert normal_config_entry.state is state
async def test_oauth_refresh_expired(
hass: HomeAssistant,
normal_config_entry: MockConfigEntry,
mock_products: AsyncMock,
) -> None:
"""Test init with expired Oauth token."""
# Patch the token refresh to raise an error
with patch(
"homeassistant.components.tesla_fleet.OAuth2Session.async_ensure_token_valid",
side_effect=ClientResponseError(
RequestInfo(AUTHORIZE_URL, "POST", {}, AUTHORIZE_URL), None, status=401
),
) as mock_async_ensure_token_valid:
# Trigger an unmocked function call
mock_products.side_effect = InvalidRegion
await setup_platform(hass, normal_config_entry)
mock_async_ensure_token_valid.assert_called_once()
assert normal_config_entry.state is ConfigEntryState.SETUP_ERROR
async def test_oauth_refresh_error(
hass: HomeAssistant,
normal_config_entry: MockConfigEntry,
mock_products: AsyncMock,
) -> None:
"""Test init with Oauth refresh failure."""
# Patch the token refresh to raise an error
with patch(
"homeassistant.components.tesla_fleet.OAuth2Session.async_ensure_token_valid",
side_effect=ClientResponseError(
RequestInfo(AUTHORIZE_URL, "POST", {}, AUTHORIZE_URL), None, status=400
),
) as mock_async_ensure_token_valid:
# Trigger an unmocked function call
mock_products.side_effect = InvalidRegion
await setup_platform(hass, normal_config_entry)
mock_async_ensure_token_valid.assert_called_once()
assert normal_config_entry.state is ConfigEntryState.SETUP_RETRY
# Test devices
async def test_devices(
hass: HomeAssistant,