Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion homeassistant/auth/mfa_modules/totp.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,9 @@ async def async_validation(
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."""
Expand Down
20 changes: 14 additions & 6 deletions homeassistant/auth/providers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,19 +224,27 @@ async def async_step_mfa(
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,
)

Expand Down
2 changes: 1 addition & 1 deletion tests/auth/mfa_modules/test_insecure_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'})
Expand Down
2 changes: 1 addition & 1 deletion tests/auth/mfa_modules/test_totp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
19 changes: 5 additions & 14 deletions tests/auth/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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):
Expand Down