Redirect native-picker click on OIDC provider to /auth/oidc/welcome (fix dead-end) (#266)
Fixes #252
This commit is contained in:
@@ -65,8 +65,11 @@ class OpenIDAuthProvider(AuthProvider):
|
|||||||
{
|
{
|
||||||
# Currently register as default, might be used when we have multiple OIDC providers
|
# Currently register as default, might be used when we have multiple OIDC providers
|
||||||
CONF_ID: "default",
|
CONF_ID: "default",
|
||||||
# Name displayed in the UI
|
# Stable label for HA's native auth-picker row. Kept fixed so the
|
||||||
CONF_NAME: config.get("display_name", DEFAULT_TITLE),
|
# frontend-injection script can match it without threading the
|
||||||
|
# user-configurable display_name through. The user's display_name
|
||||||
|
# is still rendered on the welcome page.
|
||||||
|
CONF_NAME: DEFAULT_TITLE,
|
||||||
# Type
|
# Type
|
||||||
CONF_TYPE: PROVIDER_TYPE,
|
CONF_TYPE: PROVIDER_TYPE,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,37 +1,55 @@
|
|||||||
/**
|
/**
|
||||||
* hass-oidc-auth - UX script to automatically select the Home Assistant auth provider when the "Login aborted" message is shown.
|
* Frontend helpers for /auth/authorize: auto-select on aborted login,
|
||||||
|
* and route picker clicks on our provider through /auth/oidc/welcome.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const OIDC_PROVIDER_NAME = "OpenID Connect (SSO)" // matches provider.py CONF_NAME
|
||||||
|
const OIDC_WELCOME_PATH = "/auth/oidc/welcome"
|
||||||
|
|
||||||
let authFlowElement = null
|
let authFlowElement = null
|
||||||
|
let pickerIntercepted = false
|
||||||
|
|
||||||
|
function interceptPickerRow(authProviderElement) {
|
||||||
|
if (pickerIntercepted) return
|
||||||
|
if (!authProviderElement) return
|
||||||
|
if (!authProviderElement.shadowRoot) {
|
||||||
|
console.warn("[OIDC] ha-pick-auth-provider has no shadowRoot; HA frontend may have changed.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const items = authProviderElement.shadowRoot.querySelectorAll('ha-list-item')
|
||||||
|
if (items.length === 0) return // not yet populated; retry on next mutation
|
||||||
|
for (const item of items) {
|
||||||
|
if ((item.innerText || '').trim() !== OIDC_PROVIDER_NAME) continue
|
||||||
|
item.addEventListener('click', (e) => {
|
||||||
|
e.stopImmediatePropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
window.location.href =
|
||||||
|
OIDC_WELCOME_PATH +
|
||||||
|
'?redirect_uri=' + encodeURIComponent(btoa(window.location.href))
|
||||||
|
}, true)
|
||||||
|
pickerIntercepted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
// Find ha-auth-flow
|
authFlowElement = document.querySelector('ha-auth-flow')
|
||||||
authFlowElement = document.querySelector('ha-auth-flow');
|
if (!authFlowElement) return
|
||||||
|
|
||||||
if (!authFlowElement) {
|
const authProviderElement = document.querySelector('ha-pick-auth-provider')
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the text "Login aborted" is present on the page
|
// Intercept picker clicks so the OIDC cookie is set before submit.
|
||||||
if (!authFlowElement.innerText.includes('Login aborted')) {
|
interceptPickerRow(authProviderElement)
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the ha-pick-auth-provider element
|
// Auto-select on "Login aborted".
|
||||||
const authProviderElement = document.querySelector('ha-pick-auth-provider');
|
if (!authFlowElement.innerText.includes('Login aborted')) return
|
||||||
|
if (!authProviderElement) return
|
||||||
if (!authProviderElement) {
|
const firstListItem = authProviderElement.shadowRoot?.querySelector('ha-list-item')
|
||||||
return;
|
if (!firstListItem) {
|
||||||
}
|
console.warn("[OIDC] No ha-list-item found inside ha-pick-auth-provider. Not automatically selecting OIDC provider.")
|
||||||
|
return
|
||||||
// Click the first ha-list-item element inside the ha-pick-auth-provider
|
}
|
||||||
const firstListItem = authProviderElement.shadowRoot?.querySelector('ha-list-item');
|
firstListItem.click()
|
||||||
if (!firstListItem) {
|
|
||||||
console.warn("[OIDC] No ha-list-item found inside ha-pick-auth-provider. Not automatically selecting HA provider.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstListItem.click();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide the content until ready
|
// Hide the content until ready
|
||||||
@@ -58,4 +76,5 @@ setTimeout(() => {
|
|||||||
|
|
||||||
// Force display the content
|
// Force display the content
|
||||||
document.querySelector(".content").style.display = "";
|
document.querySelector(".content").style.display = "";
|
||||||
}, 300)
|
update();
|
||||||
|
}, 300)
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ from custom_components.auth_oidc import DOMAIN
|
|||||||
from custom_components.auth_oidc.config.const import (
|
from custom_components.auth_oidc.config.const import (
|
||||||
DISCOVERY_URL,
|
DISCOVERY_URL,
|
||||||
CLIENT_ID,
|
CLIENT_ID,
|
||||||
|
DEFAULT_TITLE,
|
||||||
|
DISPLAY_NAME,
|
||||||
FEATURES,
|
FEATURES,
|
||||||
FEATURES_AUTOMATIC_PERSON_CREATION,
|
FEATURES_AUTOMATIC_PERSON_CREATION,
|
||||||
FEATURES_AUTOMATIC_USER_LINKING,
|
FEATURES_AUTOMATIC_USER_LINKING,
|
||||||
@@ -58,6 +60,24 @@ async def test_setup_success_auth_provider_registration(hass: HomeAssistant):
|
|||||||
assert auth_providers[0].support_mfa is False
|
assert auth_providers[0].support_mfa is False
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_provider_name_is_stable_regardless_of_display_name(hass: HomeAssistant):
|
||||||
|
"""CONF_NAME stays at DEFAULT_TITLE so injection.js can match the picker
|
||||||
|
row regardless of the configured display_name."""
|
||||||
|
await setup(
|
||||||
|
hass,
|
||||||
|
{
|
||||||
|
CLIENT_ID: "dummy",
|
||||||
|
DISCOVERY_URL: "https://example.com/.well-known/openid-configuration",
|
||||||
|
DISPLAY_NAME: "Custom / Branded IdP",
|
||||||
|
},
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
provider = hass.auth.get_auth_providers(DOMAIN)[0]
|
||||||
|
assert provider.name == DEFAULT_TITLE
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_provider_ip_fallback_fails_closed_without_request_context(
|
async def test_provider_ip_fallback_fails_closed_without_request_context(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
|||||||
Reference in New Issue
Block a user