From bdc41bf22a368174380f07b0c82fd6ae8c690463 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Fri, 3 Jun 2022 16:33:12 -0700 Subject: [PATCH] Fix google calendar bug where expired tokens are not refreshed (#72994) --- homeassistant/components/google/api.py | 7 +++++-- tests/components/google/conftest.py | 6 ++++-- tests/components/google/test_config_flow.py | 14 ++++++++------ 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/google/api.py b/homeassistant/components/google/api.py index 4bb9de5d581..a4cda1ff41a 100644 --- a/homeassistant/components/google/api.py +++ b/homeassistant/components/google/api.py @@ -5,7 +5,6 @@ from __future__ import annotations from collections.abc import Awaitable, Callable import datetime import logging -import time from typing import Any, cast import aiohttp @@ -50,12 +49,16 @@ class DeviceAuth(AuthImplementation): async def async_resolve_external_data(self, external_data: Any) -> dict: """Resolve a Google API Credentials object to Home Assistant token.""" creds: Credentials = external_data[DEVICE_AUTH_CREDS] + delta = creds.token_expiry.replace(tzinfo=datetime.timezone.utc) - dt.utcnow() + _LOGGER.debug( + "Token expires at %s (in %s)", creds.token_expiry, delta.total_seconds() + ) return { "access_token": creds.access_token, "refresh_token": creds.refresh_token, "scope": " ".join(creds.scopes), "token_type": "Bearer", - "expires_in": creds.token_expiry.timestamp() - time.time(), + "expires_in": delta.total_seconds(), } diff --git a/tests/components/google/conftest.py b/tests/components/google/conftest.py index b5566450913..68176493445 100644 --- a/tests/components/google/conftest.py +++ b/tests/components/google/conftest.py @@ -16,7 +16,6 @@ from homeassistant.components.google import CONF_TRACK_NEW, DOMAIN from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component -from homeassistant.util.dt import utcnow from tests.common import MockConfigEntry from tests.test_util.aiohttp import AiohttpClientMocker @@ -136,7 +135,10 @@ def token_scopes() -> list[str]: @pytest.fixture def token_expiry() -> datetime.datetime: """Expiration time for credentials used in the test.""" - return utcnow() + datetime.timedelta(days=7) + # OAuth library returns an offset-naive timestamp + return datetime.datetime.fromtimestamp( + datetime.datetime.utcnow().timestamp() + ) + datetime.timedelta(hours=1) @pytest.fixture diff --git a/tests/components/google/test_config_flow.py b/tests/components/google/test_config_flow.py index 8ac017fcba4..a346b02e6c2 100644 --- a/tests/components/google/test_config_flow.py +++ b/tests/components/google/test_config_flow.py @@ -8,6 +8,7 @@ from typing import Any from unittest.mock import Mock, patch from aiohttp.client_exceptions import ClientError +from freezegun.api import FrozenDateTimeFactory from oauth2client.client import ( FlowExchangeError, OAuth2Credentials, @@ -94,11 +95,13 @@ async def fire_alarm(hass, point_in_time): await hass.async_block_till_done() +@pytest.mark.freeze_time("2022-06-03 15:19:59-00:00") async def test_full_flow_yaml_creds( hass: HomeAssistant, mock_code_flow: Mock, mock_exchange: Mock, component_setup: ComponentSetup, + freezer: FrozenDateTimeFactory, ) -> None: """Test successful creds setup.""" assert await component_setup() @@ -115,8 +118,8 @@ async def test_full_flow_yaml_creds( "homeassistant.components.google.async_setup_entry", return_value=True ) as mock_setup: # Run one tick to invoke the credential exchange check - now = utcnow() - await fire_alarm(hass, now + CODE_CHECK_ALARM_TIMEDELTA) + freezer.tick(CODE_CHECK_ALARM_TIMEDELTA) + await fire_alarm(hass, datetime.datetime.utcnow()) await hass.async_block_till_done() result = await hass.config_entries.flow.async_configure( flow_id=result["flow_id"] @@ -127,12 +130,11 @@ async def test_full_flow_yaml_creds( assert "data" in result data = result["data"] assert "token" in data - assert 0 < data["token"]["expires_in"] < 8 * 86400 assert ( - datetime.datetime.now().timestamp() - <= data["token"]["expires_at"] - < (datetime.datetime.now() + datetime.timedelta(days=8)).timestamp() + data["token"]["expires_in"] + == 60 * 60 - CODE_CHECK_ALARM_TIMEDELTA.total_seconds() ) + assert data["token"]["expires_at"] == 1654273199.0 data["token"].pop("expires_at") data["token"].pop("expires_in") assert data == {