Improve the JS for SSO (#83)
* Tweak code field error status * Add a toggle for SSO vs Code and show a proper error when code fails * Refactor SSO button handling and improve error message display * Update timeout warning message duration in UI injection
This commit is contained in:
@@ -1,45 +1,138 @@
|
|||||||
function safeSetTextContent(element, value) {
|
function safeSetTextContent(element, value) {
|
||||||
if (!element) return
|
if (!element) return
|
||||||
var textNode = Array.from(element.childNodes).find(node => node.nodeType === Node.TEXT_NODE && node.textContent.trim().length > 0)
|
var textNode = Array.from(element.childNodes).find(node => node.nodeType === Node.TEXT_NODE && node.textContent.trim().length > 0)
|
||||||
if (!textNode) return
|
if (!textNode || textNode.textContent === value) return
|
||||||
textNode.textContent = value
|
textNode.textContent = value
|
||||||
}
|
}
|
||||||
|
|
||||||
function addSSOButton() {
|
const SSO_NAME = window.sso_name || "Single Sign-On"
|
||||||
const loginHeader = document.querySelector(".card-content > ha-auth-flow > form > h1")
|
|
||||||
safeSetTextContent(loginHeader, "Log in to Home Assistant")
|
|
||||||
|
|
||||||
|
let firstFocus = true
|
||||||
|
let showCodeOverride = null
|
||||||
|
|
||||||
|
function showCode() {
|
||||||
|
if (showCodeOverride !== null) return showCodeOverride
|
||||||
|
|
||||||
|
const clientId = new URL(location.href).searchParams.get("client_id")
|
||||||
|
return clientId && clientId.startsWith("https://home-assistant.io/iOS") || clientId.startsWith("https://home-assistant.io/android")
|
||||||
|
}
|
||||||
|
|
||||||
|
let ssoButton = null
|
||||||
|
let codeMessage = null
|
||||||
|
let codeToggle = null
|
||||||
|
let codeToggleText = null
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
const loginHeader = document.querySelector(".card-content > ha-auth-flow > form > h1")
|
||||||
|
const authForm = document.querySelector("ha-auth-form")
|
||||||
const codeField = document.querySelector(".mdc-text-field__input[name=code]")
|
const codeField = document.querySelector(".mdc-text-field__input[name=code]")
|
||||||
const loginButton = document.querySelector("mwc-button:not(.sso)")
|
const loginButton = document.querySelector("mwc-button:not(.sso)")
|
||||||
|
const errorAlert = document.querySelector("ha-auth-form ha-alert[alert-type=error]")
|
||||||
|
const loginOptionList = document.querySelector("ha-pick-auth-provider")?.shadowRoot?.querySelector("mwc-list")
|
||||||
|
|
||||||
|
safeSetTextContent(loginHeader, "Log in to Home Assistant")
|
||||||
|
|
||||||
if (codeField) {
|
if (codeField) {
|
||||||
|
if (codeField.placeholder !== "One-time code") {
|
||||||
codeField.placeholder = "One-time code"
|
codeField.placeholder = "One-time code"
|
||||||
codeField.autofocus = false
|
codeField.autofocus = false
|
||||||
codeField.autocomplete = "off"
|
codeField.autocomplete = "off"
|
||||||
|
if (firstFocus) {
|
||||||
|
firstFocus = false
|
||||||
|
if (document.activeElement === codeField) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
codeField.blur()
|
codeField.blur()
|
||||||
|
let check = setInterval(() => {
|
||||||
|
const helperText = document.querySelector("#helper-text")
|
||||||
|
const invalidTextField = document.querySelector(".mdc-text-field--invalid")
|
||||||
|
const validationMsg = document.querySelector(".mdc-text-field-helper-text--validation-msg")
|
||||||
|
if (helperText && invalidTextField && validationMsg) {
|
||||||
|
clearInterval(check)
|
||||||
|
safeSetTextContent(helperText, "")
|
||||||
|
invalidTextField.classList.remove("mdc-text-field--invalid")
|
||||||
|
validationMsg.classList.remove("mdc-text-field-helper-text--validation-msg")
|
||||||
|
}
|
||||||
|
}, 1)
|
||||||
}, 0)
|
}, 0)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (errorAlert && errorAlert.textContent.trim().length === 0) {
|
||||||
|
errorAlert.setAttribute("title", "Invalid Code")
|
||||||
|
}
|
||||||
|
authForm.style.display = showCode() ? "" : "none"
|
||||||
|
loginButton.style.display = showCode() ? "" : "none"
|
||||||
|
}
|
||||||
|
|
||||||
var ssoButton = document.querySelector("mwc-button.sso")
|
if (authForm && !codeMessage) {
|
||||||
if (!ssoButton) {
|
codeMessage = document.createElement("p")
|
||||||
|
codeMessage.innerText = `Login to Home Assistant in a web browser and enter the code you are given here`
|
||||||
|
authForm.parentElement.insertBefore(codeMessage, authForm)
|
||||||
|
}
|
||||||
|
if (codeMessage) {
|
||||||
|
codeMessage.style.display = showCode() ? "" : "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loginOptionList && !codeToggle) {
|
||||||
|
codeToggle = document.createElement("ha-list-item")
|
||||||
|
codeToggle.setAttribute("hasmeta", "")
|
||||||
|
codeToggleText = document.createTextNode("")
|
||||||
|
codeToggle.appendChild(codeToggleText)
|
||||||
|
const codeToggleIcon = document.createElement("ha-icon-next")
|
||||||
|
codeToggleIcon.setAttribute("slot", "meta")
|
||||||
|
codeToggle.appendChild(codeToggleIcon)
|
||||||
|
|
||||||
|
codeToggle.addEventListener("click", () => {
|
||||||
|
showCodeOverride = !showCode()
|
||||||
|
update()
|
||||||
|
})
|
||||||
|
loginOptionList.appendChild(codeToggle)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codeToggle) {
|
||||||
|
codeToggle.style.display = codeField ? "" : "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codeToggleText) {
|
||||||
|
codeToggleText.textContent = showCode() ? SSO_NAME : "Login Code"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loginButton && !ssoButton) {
|
||||||
ssoButton = document.createElement("mwc-button")
|
ssoButton = document.createElement("mwc-button")
|
||||||
ssoButton.classList.add("sso")
|
ssoButton.classList.add("sso")
|
||||||
ssoButton.innerText = "Log in with " + window.sso_name
|
ssoButton.innerText = "Log in with " + SSO_NAME
|
||||||
ssoButton.setAttribute("raised", "")
|
ssoButton.setAttribute("raised", "")
|
||||||
ssoButton.style.marginRight = "1em"
|
ssoButton.style.marginRight = "1em"
|
||||||
ssoButton.style.display = "none"
|
|
||||||
ssoButton.addEventListener("click", () => {
|
ssoButton.addEventListener("click", () => {
|
||||||
location.href = "/auth/oidc/redirect"
|
location.href = "/auth/oidc/redirect"
|
||||||
})
|
})
|
||||||
loginButton.parentElement.prepend(ssoButton)
|
loginButton.parentElement.prepend(ssoButton)
|
||||||
}
|
}
|
||||||
|
if (ssoButton) {
|
||||||
safeSetTextContent(loginButton, codeField ? "Log in with code" : "Log in")
|
ssoButton.style.display = (!showCode() && codeField) ? "" : "none"
|
||||||
ssoButton.style.display = codeField ? "" : "none"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
safeSetTextContent(loginButton, codeField ? "Log in with code" : "Log in")
|
||||||
|
}
|
||||||
|
|
||||||
|
let ready = false
|
||||||
|
document.querySelector(".content").style.display = "none"
|
||||||
|
|
||||||
const observer = new MutationObserver((mutationsList, observer) => {
|
const observer = new MutationObserver((mutationsList, observer) => {
|
||||||
addSSOButton()
|
update()
|
||||||
|
|
||||||
|
if (!ready) {
|
||||||
|
ready = Boolean(ssoButton && codeMessage && codeToggle && codeToggleText)
|
||||||
|
if (ready) document.querySelector(".content").style.display = ""
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
observer.observe(document.body, { childList: true, subtree: true })
|
observer.observe(document.body, { childList: true, subtree: true })
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!ready) {
|
||||||
|
console.warn("hass-oidc-auth: Document was not ready after 300ms seconds, force displaying. This may indicate a problem with the UI injection.")
|
||||||
|
}
|
||||||
|
document.querySelector(".content").style.display = "";
|
||||||
|
update();
|
||||||
|
}, 300)
|
||||||
Reference in New Issue
Block a user