Implement trusted_networks support (#283)

* Implement bypass for trusted_networks

* Trusted Network tests

* Test cleanup

* Improve integration tests

* Defensive programming

* Fix wrong import issue
This commit is contained in:
Christiaan Goossens
2026-05-01 14:03:14 +02:00
committed by GitHub
parent 04abb0fdb3
commit c7370ed266
6 changed files with 386 additions and 54 deletions

View File

@@ -11,6 +11,7 @@ from homeassistant.components.http import HomeAssistantView, StaticPathConfig
from homeassistant.core import HomeAssistant
from .welcome import PATH as WELCOME_PATH
from ..provider import OpenIDAuthProvider
from ..tools.helpers import get_url
PATH = "/auth/authorize"
@@ -24,7 +25,12 @@ async def read_file(path: str) -> str:
return await f.read()
async def frontend_injection(hass: HomeAssistant, force_https: bool) -> None:
async def frontend_injection(
hass: HomeAssistant,
provider: OpenIDAuthProvider,
force_https: bool,
has_trusted_networks_provider_first: bool,
) -> None:
"""Inject new frontend code into /auth/authorize."""
router = hass.http.app.router
frontend_path = None
@@ -81,7 +87,11 @@ async def frontend_injection(hass: HomeAssistant, force_https: bool) -> None:
)
# If everything is succesful, register a fake view that just returns the modified HTML
hass.http.register_view(OIDCInjectedAuthPage(frontend_code, force_https))
hass.http.register_view(
OIDCInjectedAuthPage(
frontend_code, provider, force_https, has_trusted_networks_provider_first
)
)
_LOGGER.info("Performed OIDC frontend injection")
@@ -92,21 +102,36 @@ class OIDCInjectedAuthPage(HomeAssistantView):
url = PATH
name = "auth:oidc:authorize_page"
def __init__(self, html: str, force_https: bool) -> None:
def __init__(
self,
html: str,
provider: OpenIDAuthProvider,
force_https: bool,
has_trusted_networks_provider_first: bool,
) -> None:
"""Initialize the injected auth page."""
self.html = html
self.provider = provider
self.force_https = force_https
self.has_trusted_networks_provider_first = has_trusted_networks_provider_first
@staticmethod
async def inject(hass: HomeAssistant, force_https: bool) -> None:
async def inject(
hass: HomeAssistant,
provider: OpenIDAuthProvider,
force_https: bool,
has_trusted_networks_provider_first: bool,
) -> None:
"""Inject the OIDC auth page into the frontend."""
try:
await frontend_injection(hass, force_https)
await frontend_injection(
hass, provider, force_https, has_trusted_networks_provider_first
)
except Exception as e: # pylint: disable=broad-except
_LOGGER.error("Failed to inject OIDC auth page: %s", e)
@staticmethod
def _should_do_oidc_redirect(req: web.Request) -> bool:
def _should_do_oidc_redirect(self, req: web.Request) -> bool:
"""Check if we should redirect to the OIDC flow."""
# Set when we return from finish
if req.query.get("skip_oidc_redirect") == "true":
@@ -118,6 +143,13 @@ class OIDCInjectedAuthPage(HomeAssistantView):
if not redirect_uri:
return False
# Check if we are on a trusted network if we have trusted networks registered first
if (
self.has_trusted_networks_provider_first
and self.provider.is_trusted_network_host()
):
return False
# Handle both encoded and plain redirect_uri values.
decoded_redirect_uri = unquote(redirect_uri)
return "skip_oidc_redirect=true" not in decoded_redirect_uri