Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add multi-factor auth module setup flow #16141

Merged
merged 8 commits into from
Aug 24, 2018

Conversation

awarecan
Copy link
Contributor

@awarecan awarecan commented Aug 22, 2018

Description:

Add websocket command to setup or remove current user for mfa module

Frontend: home-assistant/frontend#1590

Related issue (if applicable): fixes #

Pull request in developers.home-assistant with documentation (if applicable): home-assistant/developers.home-assistant#79

Example entry for configuration.yaml (if applicable):

Checklist:

  • The code change is tested and works locally.
  • Local tests pass with tox. Your PR cannot be merged unless tests pass

If user exposed functionality or configuration variables are added/changed:

If the code does not interact with devices:

  • Tests have been added to verify that the new code works.

from . import indieauth
from . import login_flow
from . import mfa_setup_flow

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

multiple spaces after keyword

"""Get current user."""
enabled_modules = await hass.auth.async_get_enabled_mfa(user)

connection.to_write.put_nowait(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be connection.send_message_outside(

async def async_setup_flow(msg):
"""Helper to return a setup flow for mfa auth module."""
flow_manager = hass.data.get(DATA_SETUP_FLOW_MGR)
if flow_manager is None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is impossible?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed init order, not possible now

if flow_id is not None:
result = await flow_manager.async_configure(
flow_id, msg.get('user_input'))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inside this if, add the write and return. That way you can drop the else

mfa_module_id = msg.get('mfa_module_id')
mfa_module = hass.auth.get_auth_mfa_module(mfa_module_id)
if mfa_module is None:
connection.to_write.put_nowait(websocket_api.error_message(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these writes need to be connection.send_message_outside(

websocket_api.result_message(
msg['id'], _prepare_result_json(result)))

hass.async_add_job(async_setup_flow(msg))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hass.async_create_task

connection.to_write.put_nowait(websocket_api.error_message(
msg['id'], 'no_user', 'Not authenticated as a user'))
return
if user.system_generated:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if we should add this in a decorator.

' {}: {}'.format(mfa_module_id, err)))
return

connection.to_write.put_nowait(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

send_message_outside


return self.async_show_form(
step_id='init',
data_schema=self._auth_module.setup_schema,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that all MFA modules need to have their own flows instead of a generic flow wrapped around a setup_schema property. Because in the case of TOTP, the flow will need to be:

  • Start login flow
  • Login flow generates a secret and stores it on instance of login flow
  • Returns show_form with a QR code in the description (injected as base64 via description_placeholder)
  • User scans code and enters a code to verify it scanned it correctly
  • TOTP module is enabled for user

This can only be done if each MFA module has their own setup flow.

Example of how GitHub does this:

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generic SetupFlow is default implement, other module can implement its own if need.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, in that case we should not have it be the default method, because now we are requiring an extra property setup_schema to be optionally defined.

Instead, if an MFA module wants to use the default config flow, they can just do:

setup_schema = vol.Schema({})
return SetupFlow(self.name, setup_schema, user_id)

That way we keep the API for MFA modules simpler while providing clean API that is also similar to Auth Providers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


Optional
Mfa module should extend SetupFlow
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is optional, no?

Copy link
Contributor Author

@awarecan awarecan Aug 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edit: it is required. MFA module has to be set up to enable.

"""Handle the first step of setup flow.

Return self.async_show_form(step_id='init') if user_input == None.
Return await self.async_finish(flow_result) if finish.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment?


DOMAIN = 'auth'
DEPENDENCIES = ['http']
DEPENDENCIES = ['http', 'websocket_api']
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Websocket API commands can be registered without loading the websocket API. It is not a dependency.

If the user wants the websocket API, the commands will be available.

from homeassistant.core import HomeAssistant


def validate_current_user(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have ws in the name. What about ws_require_user ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this method should be part of the websocket component.

Copy link
Member

@balloob balloob left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few minor comments. Ok to merge when addressed.

@awarecan awarecan requested a review from a team as a code owner August 24, 2018 16:52
@awarecan awarecan merged commit e8775ba into home-assistant:dev Aug 24, 2018
@awarecan awarecan deleted the profile-mfa-v2 branch August 24, 2018 17:17
@ghost ghost removed the in progress label Aug 24, 2018
@awarecan awarecan added this to the 0.77 milestone Aug 24, 2018
balloob pushed a commit that referenced this pull request Aug 25, 2018
* Add mfa setup flow

* Lint

* Address code review comment

* Fix unit test

* Add assertion for WS response ordering

* Missed a return

* Remove setup_schema from MFA base class

* Move auth.util.validate_current_user -> webscoket_api.ws_require_user
@balloob balloob mentioned this pull request Aug 29, 2018
girlpunk pushed a commit to girlpunk/home-assistant that referenced this pull request Sep 4, 2018
* Add mfa setup flow

* Lint

* Address code review comment

* Fix unit test

* Add assertion for WS response ordering

* Missed a return

* Remove setup_schema from MFA base class

* Move auth.util.validate_current_user -> webscoket_api.ws_require_user
@home-assistant home-assistant locked and limited conversation to collaborators Dec 10, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants