diff --git a/homeassistant/components/withings/__init__.py b/homeassistant/components/withings/__init__.py index 9d1fe1a600f..47702090cc0 100644 --- a/homeassistant/components/withings/__init__.py +++ b/homeassistant/components/withings/__init__.py @@ -9,10 +9,13 @@ import asyncio from aiohttp.web import Request, Response import voluptuous as vol -from withings_api import AbstractWithingsApi, WithingsAuth from withings_api.common import NotifyAppli from homeassistant.components import webhook +from homeassistant.components.application_credentials import ( + ClientCredential, + async_import_client_credential, +) from homeassistant.components.webhook import ( async_unregister as async_unregister_webhook, ) @@ -28,10 +31,9 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers.event import async_call_later from homeassistant.helpers.typing import ConfigType -from . import config_flow, const +from . import const from .common import ( _LOGGER, - WithingsLocalOAuth2Implementation, async_get_data_manager, async_remove_data_manager, get_data_manager_by_webhook_id, @@ -45,10 +47,12 @@ CONFIG_SCHEMA = vol.Schema( { DOMAIN: vol.All( cv.deprecated(const.CONF_PROFILES), + cv.deprecated(CONF_CLIENT_ID), + cv.deprecated(CONF_CLIENT_SECRET), vol.Schema( { - vol.Required(CONF_CLIENT_ID): vol.All(cv.string, vol.Length(min=1)), - vol.Required(CONF_CLIENT_SECRET): vol.All( + vol.Optional(CONF_CLIENT_ID): vol.All(cv.string, vol.Length(min=1)), + vol.Optional(CONF_CLIENT_SECRET): vol.All( cv.string, vol.Length(min=1) ), vol.Optional(const.CONF_USE_WEBHOOK, default=False): cv.boolean, @@ -76,17 +80,22 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: hass.data[DOMAIN] = {const.CONFIG: conf} # Setup the oauth2 config flow. - config_flow.WithingsFlowHandler.async_register_implementation( - hass, - WithingsLocalOAuth2Implementation( + if CONF_CLIENT_ID in conf: + await async_import_client_credential( hass, - const.DOMAIN, - conf[CONF_CLIENT_ID], - conf[CONF_CLIENT_SECRET], - f"{WithingsAuth.URL}/oauth2_user/authorize2", - f"{AbstractWithingsApi.URL}/v2/oauth2", - ), - ) + DOMAIN, + ClientCredential( + conf[CONF_CLIENT_ID], + conf[CONF_CLIENT_SECRET], + ), + ) + _LOGGER.warning( + "Configuration of Withings integration OAuth2 credentials in YAML " + "is deprecated and will be removed in a future release; Your " + "existing OAuth Application Credentials have been imported into " + "the UI automatically and can be safely removed from your " + "configuration.yaml file" + ) return True diff --git a/homeassistant/components/withings/application_credentials.py b/homeassistant/components/withings/application_credentials.py new file mode 100644 index 00000000000..e5c401d5e74 --- /dev/null +++ b/homeassistant/components/withings/application_credentials.py @@ -0,0 +1,28 @@ +"""application_credentials platform for Withings.""" + +from withings_api import AbstractWithingsApi, WithingsAuth + +from homeassistant.components.application_credentials import ( + AuthorizationServer, + ClientCredential, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers import config_entry_oauth2_flow + +from .common import WithingsLocalOAuth2Implementation +from .const import DOMAIN + + +async def async_get_auth_implementation( + hass: HomeAssistant, auth_domain: str, credential: ClientCredential +) -> config_entry_oauth2_flow.AbstractOAuth2Implementation: + """Return auth implementation.""" + return WithingsLocalOAuth2Implementation( + hass, + DOMAIN, + credential, + authorization_server=AuthorizationServer( + authorize_url=f"{WithingsAuth.URL}/oauth2_user/authorize2", + token_url=f"{AbstractWithingsApi.URL}/v2/oauth2", + ), + ) diff --git a/homeassistant/components/withings/common.py b/homeassistant/components/withings/common.py index 78ae375b4bc..ad40378eafb 100644 --- a/homeassistant/components/withings/common.py +++ b/homeassistant/components/withings/common.py @@ -28,6 +28,7 @@ from withings_api.common import ( ) from homeassistant.components import webhook +from homeassistant.components.application_credentials import AuthImplementation from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN from homeassistant.components.http import HomeAssistantView from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN @@ -45,7 +46,6 @@ from homeassistant.helpers import config_entry_oauth2_flow, entity_registry as e from homeassistant.helpers.config_entry_oauth2_flow import ( AUTH_CALLBACK_PATH, AbstractOAuth2Implementation, - LocalOAuth2Implementation, OAuth2Session, ) from homeassistant.helpers.entity import Entity @@ -1106,7 +1106,7 @@ def get_platform_attributes(platform: str) -> tuple[WithingsAttribute, ...]: ) -class WithingsLocalOAuth2Implementation(LocalOAuth2Implementation): +class WithingsLocalOAuth2Implementation(AuthImplementation): """Oauth2 implementation that only uses the external url.""" @property diff --git a/homeassistant/components/withings/manifest.json b/homeassistant/components/withings/manifest.json index 2e089bdbcc9..1437b7f2198 100644 --- a/homeassistant/components/withings/manifest.json +++ b/homeassistant/components/withings/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/withings", "requirements": ["withings-api==2.4.0"], - "dependencies": ["auth", "http", "webhook"], + "dependencies": ["application_credentials", "http", "webhook"], "codeowners": ["@vangorra"], "iot_class": "cloud_polling", "loggers": ["withings_api"] diff --git a/homeassistant/generated/application_credentials.py b/homeassistant/generated/application_credentials.py index 41541943086..d7b3259a158 100644 --- a/homeassistant/generated/application_credentials.py +++ b/homeassistant/generated/application_credentials.py @@ -12,6 +12,7 @@ APPLICATION_CREDENTIALS = [ "neato", "netatmo", "spotify", + "withings", "xbox", "yolink" ] diff --git a/tests/components/withings/test_init.py b/tests/components/withings/test_init.py index 465c26deb32..db26f7328ce 100644 --- a/tests/components/withings/test_init.py +++ b/tests/components/withings/test_init.py @@ -59,7 +59,6 @@ def test_config_schema_basic_config() -> None: def test_config_schema_client_id() -> None: """Test schema.""" - config_schema_assert_fail({CONF_CLIENT_SECRET: "my_client_secret"}) config_schema_assert_fail( {CONF_CLIENT_SECRET: "my_client_secret", CONF_CLIENT_ID: ""} ) @@ -70,7 +69,6 @@ def test_config_schema_client_id() -> None: def test_config_schema_client_secret() -> None: """Test schema.""" - config_schema_assert_fail({CONF_CLIENT_ID: "my_client_id"}) config_schema_assert_fail({CONF_CLIENT_ID: "my_client_id", CONF_CLIENT_SECRET: ""}) config_schema_validate( {CONF_CLIENT_ID: "my_client_id", CONF_CLIENT_SECRET: "my_client_secret"}