From f9b1fb5906d5ac56fb55523bd0368d20a343766e Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Wed, 29 Aug 2018 01:16:54 -0700 Subject: [PATCH] Tweak MFA login flow (#16254) * Tweak MFA login flow * Fix typo --- homeassistant/auth/mfa_modules/totp.py | 3 ++- homeassistant/auth/providers/__init__.py | 20 +++++++++++++------ .../auth/mfa_modules/test_insecure_example.py | 2 +- tests/auth/mfa_modules/test_totp.py | 2 +- tests/auth/test_init.py | 19 +++++------------- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/homeassistant/auth/mfa_modules/totp.py b/homeassistant/auth/mfa_modules/totp.py index 48531863c1a..0914658a655 100644 --- a/homeassistant/auth/mfa_modules/totp.py +++ b/homeassistant/auth/mfa_modules/totp.py @@ -137,8 +137,9 @@ class TotpAuthModule(MultiFactorAuthModule): await self._async_load() # user_input has been validate in caller + # set INPUT_FIELD_CODE as vol.Required is not user friendly return await self.hass.async_add_executor_job( - self._validate_2fa, user_id, user_input[INPUT_FIELD_CODE]) + self._validate_2fa, user_id, user_input.get(INPUT_FIELD_CODE, '')) def _validate_2fa(self, user_id: str, code: str) -> bool: """Validate two factor authentication code.""" diff --git a/homeassistant/auth/providers/__init__.py b/homeassistant/auth/providers/__init__.py index 370391d57cd..3cb1c6b121e 100644 --- a/homeassistant/auth/providers/__init__.py +++ b/homeassistant/auth/providers/__init__.py @@ -224,19 +224,27 @@ class LoginFlow(data_entry_flow.FlowHandler): if user_input is not None: expires = self.created_at + SESSION_EXPIRATION if dt_util.utcnow() > expires: - errors['base'] = 'login_expired' - else: - result = await auth_module.async_validation( - self.user.id, user_input) # type: ignore - if not result: - errors['base'] = 'invalid_auth' + return self.async_abort( + reason='login_expired' + ) + + result = await auth_module.async_validation( + self.user.id, user_input) # type: ignore + if not result: + errors['base'] = 'invalid_code' if not errors: return await self.async_finish(self.user) + description_placeholders = { + 'mfa_module_name': auth_module.name, + 'mfa_module_id': auth_module.id + } # type: Dict[str, str] + return self.async_show_form( step_id='mfa', data_schema=auth_module.input_schema, + description_placeholders=description_placeholders, errors=errors, ) diff --git a/tests/auth/mfa_modules/test_insecure_example.py b/tests/auth/mfa_modules/test_insecure_example.py index e6f83762cd7..80109627140 100644 --- a/tests/auth/mfa_modules/test_insecure_example.py +++ b/tests/auth/mfa_modules/test_insecure_example.py @@ -119,7 +119,7 @@ async def test_login(hass): result = await hass.auth.login_flow.async_configure( result['flow_id'], {'pin': 'invalid-code'}) assert result['type'] == data_entry_flow.RESULT_TYPE_FORM - assert result['errors']['base'] == 'invalid_auth' + assert result['errors']['base'] == 'invalid_code' result = await hass.auth.login_flow.async_configure( result['flow_id'], {'pin': '123456'}) diff --git a/tests/auth/mfa_modules/test_totp.py b/tests/auth/mfa_modules/test_totp.py index 28e6c949bc4..6e3558ec549 100644 --- a/tests/auth/mfa_modules/test_totp.py +++ b/tests/auth/mfa_modules/test_totp.py @@ -121,7 +121,7 @@ async def test_login_flow_validates_mfa(hass): result['flow_id'], {'code': 'invalid-code'}) assert result['type'] == data_entry_flow.RESULT_TYPE_FORM assert result['step_id'] == 'mfa' - assert result['errors']['base'] == 'invalid_auth' + assert result['errors']['base'] == 'invalid_code' with patch('pyotp.TOTP.verify', return_value=True): result = await hass.auth.login_flow.async_configure( diff --git a/tests/auth/test_init.py b/tests/auth/test_init.py index d9e7a50410f..63b2b4408dd 100644 --- a/tests/auth/test_init.py +++ b/tests/auth/test_init.py @@ -428,10 +428,10 @@ async def test_login_with_auth_module(mock_hass): 'pin': 'invalid-pin', }) - # Invalid auth error + # Invalid code error assert step['type'] == data_entry_flow.RESULT_TYPE_FORM assert step['step_id'] == 'mfa' - assert step['errors'] == {'base': 'invalid_auth'} + assert step['errors'] == {'base': 'invalid_code'} step = await manager.login_flow.async_configure(step['flow_id'], { 'pin': 'test-pin', @@ -571,18 +571,9 @@ async def test_auth_module_expired_session(mock_hass): step = await manager.login_flow.async_configure(step['flow_id'], { 'pin': 'test-pin', }) - # Invalid auth due session timeout - assert step['type'] == data_entry_flow.RESULT_TYPE_FORM - assert step['step_id'] == 'mfa' - assert step['errors']['base'] == 'login_expired' - - # The second try will fail as well - step = await manager.login_flow.async_configure(step['flow_id'], { - 'pin': 'test-pin', - }) - assert step['type'] == data_entry_flow.RESULT_TYPE_FORM - assert step['step_id'] == 'mfa' - assert step['errors']['base'] == 'login_expired' + # login flow abort due session timeout + assert step['type'] == data_entry_flow.RESULT_TYPE_ABORT + assert step['reason'] == 'login_expired' async def test_enable_mfa_for_user(hass, hass_storage):