diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index d5265abf262..bdff78fac44 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,9 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20200613.0"], + "requirements": [ + "home-assistant-frontend==20200617.0" + ], "dependencies": [ "api", "auth", @@ -15,6 +17,8 @@ "system_log", "websocket_api" ], - "codeowners": ["@home-assistant/frontend"], + "codeowners": [ + "@home-assistant/frontend" + ], "quality_scale": "internal" -} +} \ No newline at end of file diff --git a/homeassistant/components/onboarding/views.py b/homeassistant/components/onboarding/views.py index f371c7aa9cb..a2a4fb15fd7 100644 --- a/homeassistant/components/onboarding/views.py +++ b/homeassistant/components/onboarding/views.py @@ -4,9 +4,10 @@ import asyncio import voluptuous as vol from homeassistant.auth.const import GROUP_ID_ADMIN +from homeassistant.components.auth import indieauth from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.components.http.view import HomeAssistantView -from homeassistant.const import HTTP_FORBIDDEN +from homeassistant.const import HTTP_BAD_REQUEST, HTTP_FORBIDDEN from homeassistant.core import callback from .const import ( @@ -168,7 +169,9 @@ class IntegrationOnboardingView(_BaseOnboardingView): name = "api:onboarding:integration" step = STEP_INTEGRATION - @RequestDataValidator(vol.Schema({vol.Required("client_id"): str})) + @RequestDataValidator( + vol.Schema({vol.Required("client_id"): str, vol.Required("redirect_uri"): str}) + ) async def post(self, request, data): """Handle token creation.""" hass = request.app["hass"] @@ -182,6 +185,14 @@ class IntegrationOnboardingView(_BaseOnboardingView): await self._async_mark_done(hass) + # Validate client ID and redirect uri + if not await indieauth.verify_redirect_uri( + request.app["hass"], data["client_id"], data["redirect_uri"] + ): + return self.json_message( + "invalid client id or redirect uri", HTTP_BAD_REQUEST + ) + # Return authorization code so we can redirect user and log them in auth_code = hass.components.auth.create_auth_code(data["client_id"], user) return self.json({"auth_code": auth_code}) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 9651be441a1..79e3cbe5b2d 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -12,7 +12,7 @@ cryptography==2.9.2 defusedxml==0.6.0 distro==1.5.0 hass-nabucasa==0.34.6 -home-assistant-frontend==20200613.0 +home-assistant-frontend==20200617.0 importlib-metadata==1.6.0;python_version<'3.8' jinja2>=2.11.1 netdisco==2.7.0 diff --git a/requirements_all.txt b/requirements_all.txt index a2cee2fa322..9f2cc03d75a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -735,7 +735,7 @@ hole==0.5.1 holidays==0.10.2 # homeassistant.components.frontend -home-assistant-frontend==20200613.0 +home-assistant-frontend==20200617.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c9cb5bc091e..16a9de1f43e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -331,7 +331,7 @@ hole==0.5.1 holidays==0.10.2 # homeassistant.components.frontend -home-assistant-frontend==20200613.0 +home-assistant-frontend==20200617.0 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 diff --git a/tests/components/onboarding/test_views.py b/tests/components/onboarding/test_views.py index 7deda0e7edc..0d425642622 100644 --- a/tests/components/onboarding/test_views.py +++ b/tests/components/onboarding/test_views.py @@ -11,7 +11,7 @@ from homeassistant.setup import async_setup_component from . import mock_storage from tests.async_mock import patch -from tests.common import CLIENT_ID, register_auth_provider +from tests.common import CLIENT_ID, CLIENT_REDIRECT_URI, register_auth_provider from tests.components.met.conftest import mock_weather # noqa: F401 @@ -192,7 +192,8 @@ async def test_onboarding_integration(hass, hass_storage, hass_client): client = await hass_client() resp = await client.post( - "/api/onboarding/integration", json={"client_id": CLIENT_ID} + "/api/onboarding/integration", + json={"client_id": CLIENT_ID, "redirect_uri": CLIENT_REDIRECT_URI}, ) assert resp.status == 200 @@ -217,6 +218,35 @@ async def test_onboarding_integration(hass, hass_storage, hass_client): await hass.auth.async_validate_access_token(tokens["access_token"]) is not None ) + # Onboarding refresh token and new refresh token + for user in await hass.auth.async_get_users(): + assert len(user.refresh_tokens) == 2, user + + +async def test_onboarding_integration_invalid_redirect_uri( + hass, hass_storage, hass_client +): + """Test finishing integration step.""" + mock_storage(hass_storage, {"done": [const.STEP_USER]}) + + assert await async_setup_component(hass, "onboarding", {}) + + client = await hass_client() + + resp = await client.post( + "/api/onboarding/integration", + json={"client_id": CLIENT_ID, "redirect_uri": "http://invalid-redirect.uri"}, + ) + + assert resp.status == 400 + + # We will still mark the last step as done because there is nothing left. + assert const.STEP_INTEGRATION in hass_storage[const.DOMAIN]["data"]["done"] + + # Only refresh token from onboarding should be there + for user in await hass.auth.async_get_users(): + assert len(user.refresh_tokens) == 1, user + async def test_onboarding_integration_requires_auth(hass, hass_storage, aiohttp_client): """Test finishing integration step."""