mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 01:38:02 +00:00
Handle OAuth2 rejection (#72040)
This commit is contained in:
parent
7d391846ff
commit
6a3d2e54a2
@ -271,9 +271,10 @@ class AbstractOAuth2FlowHandler(config_entries.ConfigFlow, metaclass=ABCMeta):
|
||||
) -> FlowResult:
|
||||
"""Create an entry for auth."""
|
||||
# Flow has been triggered by external data
|
||||
if user_input:
|
||||
if user_input is not None:
|
||||
self.external_data = user_input
|
||||
return self.async_external_step_done(next_step_id="creation")
|
||||
next_step = "authorize_rejected" if "error" in user_input else "creation"
|
||||
return self.async_external_step_done(next_step_id=next_step)
|
||||
|
||||
try:
|
||||
async with async_timeout.timeout(10):
|
||||
@ -311,6 +312,13 @@ class AbstractOAuth2FlowHandler(config_entries.ConfigFlow, metaclass=ABCMeta):
|
||||
{"auth_implementation": self.flow_impl.domain, "token": token}
|
||||
)
|
||||
|
||||
async def async_step_authorize_rejected(self, data: None = None) -> FlowResult:
|
||||
"""Step to handle flow rejection."""
|
||||
return self.async_abort(
|
||||
reason="user_rejected_authorize",
|
||||
description_placeholders={"error": self.external_data["error"]},
|
||||
)
|
||||
|
||||
async def async_oauth_create_entry(self, data: dict) -> FlowResult:
|
||||
"""Create an entry for the flow.
|
||||
|
||||
@ -400,10 +408,8 @@ class OAuth2AuthorizeCallbackView(http.HomeAssistantView):
|
||||
|
||||
async def get(self, request: web.Request) -> web.Response:
|
||||
"""Receive authorization code."""
|
||||
if "code" not in request.query or "state" not in request.query:
|
||||
return web.Response(
|
||||
text=f"Missing code or state parameter in {request.url}"
|
||||
)
|
||||
if "state" not in request.query:
|
||||
return web.Response(text="Missing state parameter")
|
||||
|
||||
hass = request.app["hass"]
|
||||
|
||||
@ -412,9 +418,17 @@ class OAuth2AuthorizeCallbackView(http.HomeAssistantView):
|
||||
if state is None:
|
||||
return web.Response(text="Invalid state")
|
||||
|
||||
user_input: dict[str, Any] = {"state": state}
|
||||
|
||||
if "code" in request.query:
|
||||
user_input["code"] = request.query["code"]
|
||||
elif "error" in request.query:
|
||||
user_input["error"] = request.query["error"]
|
||||
else:
|
||||
return web.Response(text="Missing code or error parameter")
|
||||
|
||||
await hass.config_entries.flow.async_configure(
|
||||
flow_id=state["flow_id"],
|
||||
user_input={"state": state, "code": request.query["code"]},
|
||||
flow_id=state["flow_id"], user_input=user_input
|
||||
)
|
||||
|
||||
return web.Response(
|
||||
|
@ -74,6 +74,7 @@
|
||||
"oauth2_missing_credentials": "The integration requires application credentials.",
|
||||
"oauth2_authorize_url_timeout": "Timeout generating authorize URL.",
|
||||
"oauth2_no_url_available": "No URL available. For information about this error, [check the help section]({docs_url})",
|
||||
"oauth2_user_rejected_authorize": "Account linking rejected: {error}",
|
||||
"reauth_successful": "Re-authentication was successful",
|
||||
"unknown_authorize_url_generation": "Unknown error generating an authorize URL.",
|
||||
"cloud_not_connected": "Not connected to Home Assistant Cloud."
|
||||
|
@ -188,6 +188,7 @@ def _custom_tasks(template, info: Info) -> None:
|
||||
"missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]",
|
||||
"authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]",
|
||||
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]",
|
||||
"user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]",
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "[%key:common::config_flow::create_entry::authenticated%]"
|
||||
|
@ -223,6 +223,61 @@ async def test_abort_if_oauth_error(
|
||||
assert result["reason"] == "oauth_error"
|
||||
|
||||
|
||||
async def test_abort_if_oauth_rejected(
|
||||
hass,
|
||||
flow_handler,
|
||||
local_impl,
|
||||
hass_client_no_auth,
|
||||
aioclient_mock,
|
||||
current_request_with_host,
|
||||
):
|
||||
"""Check bad oauth token."""
|
||||
flow_handler.async_register_implementation(hass, local_impl)
|
||||
config_entry_oauth2_flow.async_register_implementation(
|
||||
hass, TEST_DOMAIN, MockOAuth2Implementation()
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
TEST_DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "pick_implementation"
|
||||
|
||||
# Pick implementation
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={"implementation": TEST_DOMAIN}
|
||||
)
|
||||
|
||||
state = config_entry_oauth2_flow._encode_jwt(
|
||||
hass,
|
||||
{
|
||||
"flow_id": result["flow_id"],
|
||||
"redirect_uri": "https://example.com/auth/external/callback",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_EXTERNAL_STEP
|
||||
assert result["url"] == (
|
||||
f"{AUTHORIZE_URL}?response_type=code&client_id={CLIENT_ID}"
|
||||
"&redirect_uri=https://example.com/auth/external/callback"
|
||||
f"&state={state}&scope=read+write"
|
||||
)
|
||||
|
||||
client = await hass_client_no_auth()
|
||||
resp = await client.get(
|
||||
f"/auth/external/callback?error=access_denied&state={state}"
|
||||
)
|
||||
assert resp.status == 200
|
||||
assert resp.headers["content-type"] == "text/html; charset=utf-8"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "user_rejected_authorize"
|
||||
assert result["description_placeholders"] == {"error": "access_denied"}
|
||||
|
||||
|
||||
async def test_step_discovery(hass, flow_handler, local_impl):
|
||||
"""Check flow triggers from discovery."""
|
||||
flow_handler.async_register_implementation(hass, local_impl)
|
||||
|
Loading…
x
Reference in New Issue
Block a user