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
14 changes: 5 additions & 9 deletions plugins/dashboard_auth/self_hosted/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,21 +610,17 @@ def _validate_redirect_uri(self, redirect_uri: str) -> None:
"""Fast-fail obviously-broken redirect_uris before bouncing to the IDP.

The IDP's own allowlist is authoritative; this just catches the common
operator-error case with a clear message. Mirrors the nous provider.
operator-error case with a clear message. We allow any ``http://`` host
(not just localhost) so self-hosted dashboards reached over plain HTTP β€”
LAN IPs, internal hostnames, reverse proxies that terminate TLS upstream
β€” are not rejected here; the IDP makes the final call on which
redirect_uris are permitted. Mirrors the nous provider.
"""
parsed = urllib.parse.urlparse(redirect_uri)
if parsed.scheme not in ("https", "http"):
raise ProviderError(
f"redirect_uri must be http(s), got {redirect_uri!r}"
)
if parsed.scheme == "http" and parsed.hostname not in (
"localhost",
"127.0.0.1",
):
raise ProviderError(
"redirect_uri may only use http:// for localhost/127.0.0.1, "
f"got {redirect_uri!r}"
)
if not parsed.path or not parsed.path.endswith("/auth/callback"):
raise ProviderError(
"redirect_uri path must end with '/auth/callback', "
Expand Down
10 changes: 10 additions & 0 deletions tests/plugins/dashboard_auth/test_self_hosted_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,16 @@ def test_rejects_wrong_callback_path(self, provider):
with pytest.raises(ProviderError, match="/auth/callback"):
provider.start_login(redirect_uri="https://x.example/oauth/cb")

def test_allows_http_with_arbitrary_host(self, provider):
# http:// is permitted for any host now, not just localhost β€” the
# IDP-side allowlist is authoritative on which redirect_uris are
# accepted; this client-side fast-fail must not reject self-hosted
# dashboards reached over plain HTTP (LAN IPs, internal hostnames,
# TLS-terminating reverse proxies). Should not raise.
provider.start_login(redirect_uri="http://hermes.example/auth/callback")
provider.start_login(redirect_uri="http://192.168.1.50:9119/auth/callback")
provider.start_login(redirect_uri="http://my-internal-host/auth/callback")

def test_allows_http_localhost_redirect(self, provider):
provider.start_login(redirect_uri="http://localhost:8080/auth/callback")
provider.start_login(redirect_uri="http://127.0.0.1:8080/auth/callback")
Expand Down