diff --git a/homeassistant/components/coinbase/config_flow.py b/homeassistant/components/coinbase/config_flow.py index 5901aeeed9a..37311bbd1af 100644 --- a/homeassistant/components/coinbase/config_flow.py +++ b/homeassistant/components/coinbase/config_flow.py @@ -51,6 +51,15 @@ async def validate_api(hass: core.HomeAssistant, data): get_user_from_client, data[CONF_API_KEY], data[CONF_API_TOKEN] ) except AuthenticationError as error: + if "api key" in str(error): + _LOGGER.debug("Coinbase rejected API credentials due to an invalid API key") + raise InvalidKey from error + if "invalid signature" in str(error): + _LOGGER.debug( + "Coinbase rejected API credentials due to an invalid API secret" + ) + raise InvalidSecret from error + _LOGGER.debug("Coinbase rejected API credentials due to an unknown error") raise InvalidAuth from error except ConnectionError as error: raise CannotConnect from error @@ -110,6 +119,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): info = await validate_api(self.hass, user_input) except CannotConnect: errors["base"] = "cannot_connect" + except InvalidKey: + errors["base"] = "invalid_auth_key" + except InvalidSecret: + errors["base"] = "invalid_auth_secret" except InvalidAuth: errors["base"] = "invalid_auth" except Exception: # pylint: disable=broad-except @@ -218,6 +231,14 @@ class InvalidAuth(exceptions.HomeAssistantError): """Error to indicate there is invalid auth.""" +class InvalidSecret(exceptions.HomeAssistantError): + """Error to indicate auth failed due to invalid secret.""" + + +class InvalidKey(exceptions.HomeAssistantError): + """Error to indicate auth failed due to invalid key.""" + + class AlreadyConfigured(exceptions.HomeAssistantError): """Error to indicate Coinbase API Key is already configured.""" diff --git a/homeassistant/components/coinbase/strings.json b/homeassistant/components/coinbase/strings.json index ce80db35918..3e0b986365c 100644 --- a/homeassistant/components/coinbase/strings.json +++ b/homeassistant/components/coinbase/strings.json @@ -13,6 +13,8 @@ "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", + "invalid_auth_key": "API credentials rejected by Coinbase due to an invalid API Key.", + "invalid_auth_secret": "API credentials rejected by Coinbase due to an invalid API Secret.", "unknown": "[%key:common::config_flow::error::unknown%]" }, "abort": { diff --git a/homeassistant/components/coinbase/translations/en.json b/homeassistant/components/coinbase/translations/en.json index 0c5b296bce0..fe7370cb016 100644 --- a/homeassistant/components/coinbase/translations/en.json +++ b/homeassistant/components/coinbase/translations/en.json @@ -6,15 +6,15 @@ "error": { "cannot_connect": "Failed to connect", "invalid_auth": "Invalid authentication", + "invalid_auth_key": "API credentials rejected by Coinbase due to an invalid API Key.", + "invalid_auth_secret": "API credentials rejected by Coinbase due to an invalid API Secret.", "unknown": "Unexpected error" }, "step": { "user": { "data": { "api_key": "API Key", - "api_token": "API Secret", - "currencies": "Account Balance Currencies", - "exchange_rates": "Exchange Rates" + "api_token": "API Secret" }, "description": "Please enter the details of your API key as provided by Coinbase.", "title": "Coinbase API Key Details" diff --git a/tests/components/coinbase/test_config_flow.py b/tests/components/coinbase/test_config_flow.py index e487cb5d837..fff03797fbb 100644 --- a/tests/components/coinbase/test_config_flow.py +++ b/tests/components/coinbase/test_config_flow.py @@ -1,4 +1,5 @@ """Test the Coinbase config flow.""" +import logging from unittest.mock import patch from coinbase.wallet.error import AuthenticationError @@ -63,23 +64,25 @@ async def test_form(hass): assert len(mock_setup_entry.mock_calls) == 1 -async def test_form_invalid_auth(hass): +async def test_form_invalid_auth(hass, caplog): """Test we handle invalid auth.""" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) + caplog.set_level(logging.DEBUG) + response = Response() response.status_code = 401 - api_auth_error = AuthenticationError( + api_auth_error_unknown = AuthenticationError( response, "authentication_error", - "invalid signature", - [{"id": "authentication_error", "message": "invalid signature"}], + "unknown error", + [{"id": "authentication_error", "message": "unknown error"}], ) with patch( "coinbase.wallet.client.Client.get_current_user", - side_effect=api_auth_error, + side_effect=api_auth_error_unknown, ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], @@ -91,6 +94,53 @@ async def test_form_invalid_auth(hass): assert result2["type"] == "form" assert result2["errors"] == {"base": "invalid_auth"} + assert "Coinbase rejected API credentials due to an unknown error" in caplog.text + + api_auth_error_key = AuthenticationError( + response, + "authentication_error", + "invalid api key", + [{"id": "authentication_error", "message": "invalid api key"}], + ) + with patch( + "coinbase.wallet.client.Client.get_current_user", + side_effect=api_auth_error_key, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_API_KEY: "123456", + CONF_API_TOKEN: "AbCDeF", + }, + ) + + assert result2["type"] == "form" + assert result2["errors"] == {"base": "invalid_auth_key"} + assert "Coinbase rejected API credentials due to an invalid API key" in caplog.text + + api_auth_error_secret = AuthenticationError( + response, + "authentication_error", + "invalid signature", + [{"id": "authentication_error", "message": "invalid signature"}], + ) + with patch( + "coinbase.wallet.client.Client.get_current_user", + side_effect=api_auth_error_secret, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_API_KEY: "123456", + CONF_API_TOKEN: "AbCDeF", + }, + ) + + assert result2["type"] == "form" + assert result2["errors"] == {"base": "invalid_auth_secret"} + assert ( + "Coinbase rejected API credentials due to an invalid API secret" in caplog.text + ) async def test_form_cannot_connect(hass):