diff --git a/src/auth/ha-auth-flow.ts b/src/auth/ha-auth-flow.ts
index 85dcf053e9..c04f91acc9 100644
--- a/src/auth/ha-auth-flow.ts
+++ b/src/auth/ha-auth-flow.ts
@@ -7,6 +7,7 @@ import {
PropertyValues,
TemplateResult,
} from "lit";
+import "./ha-password-manager-polyfill";
import { property, state } from "lit/decorators";
import "../components/ha-form/ha-form";
import "../components/ha-markdown";
@@ -20,7 +21,7 @@ import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
type State = "loading" | "error" | "step";
class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
- @property() public authProvider?: AuthProvider;
+ @property({ attribute: false }) public authProvider?: AuthProvider;
@property() public clientId?: string;
@@ -37,7 +38,15 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
@state() private _errorMessage?: string;
protected render() {
- return html`
`;
+ return html`
+
+
+ `;
}
protected firstUpdated(changedProps: PropertyValues) {
@@ -231,11 +240,17 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
await this.updateComplete;
// 100ms to give all the form elements time to initialize.
setTimeout(() => {
- const form = this.shadowRoot!.querySelector("ha-form");
+ const form = this.renderRoot.querySelector("ha-form");
if (form) {
(form as any).focus();
}
}, 100);
+
+ setTimeout(() => {
+ this.renderRoot.querySelector(
+ "ha-password-manager-polyfill"
+ )!.boundingRect = this.getBoundingClientRect();
+ }, 500);
}
private _stepDataChanged(ev: CustomEvent) {
@@ -329,3 +344,9 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
}
}
customElements.define("ha-auth-flow", HaAuthFlow);
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-auth-flow": HaAuthFlow;
+ }
+}
diff --git a/src/auth/ha-password-manager-polyfill.ts b/src/auth/ha-password-manager-polyfill.ts
new file mode 100644
index 0000000000..1a2e765e05
--- /dev/null
+++ b/src/auth/ha-password-manager-polyfill.ts
@@ -0,0 +1,110 @@
+import { html, LitElement, TemplateResult } from "lit";
+import { customElement, property } from "lit/decorators";
+import { fireEvent } from "../common/dom/fire_event";
+import { HaFormSchema } from "../components/ha-form/ha-form";
+import { DataEntryFlowStep } from "../data/data_entry_flow";
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-password-manager-polyfill": HaPasswordManagerPolyfill;
+ }
+ interface HASSDomEvents {
+ "form-submitted": undefined;
+ }
+}
+
+const ENABLED_HANDLERS = [
+ "homeassistant",
+ "legacy_api_password",
+ "command_line",
+];
+
+@customElement("ha-password-manager-polyfill")
+export class HaPasswordManagerPolyfill extends LitElement {
+ @property({ attribute: false }) public step?: DataEntryFlowStep;
+
+ @property({ attribute: false }) public stepData: any;
+
+ @property({ attribute: false }) public boundingRect?: DOMRect;
+
+ protected createRenderRoot() {
+ // Add under document body so the element isn't placed inside any shadow roots
+ return document.body;
+ }
+
+ private get styles() {
+ return `
+ .password-manager-polyfill {
+ position: absolute;
+ top: ${this.boundingRect?.y || 148}px;
+ left: calc(50% - ${(this.boundingRect?.width || 360) / 2}px);
+ width: ${this.boundingRect?.width || 360}px;
+ opacity: 0;
+ z-index: -1;
+ }
+ .password-manager-polyfill input {
+ width: 100%;
+ height: 62px;
+ padding: 0;
+ border: 0;
+ }
+ .password-manager-polyfill input[type="submit"] {
+ width: 0;
+ height: 0;
+ }
+ `;
+ }
+
+ protected render(): TemplateResult {
+ if (
+ this.step &&
+ this.step.type === "form" &&
+ this.step.step_id === "init" &&
+ ENABLED_HANDLERS.includes(this.step.handler[0])
+ ) {
+ return html`
+
+ `;
+ }
+ return html``;
+ }
+
+ private render_input(schema: HaFormSchema): TemplateResult | string {
+ const inputType = schema.name.includes("password") ? "password" : "text";
+ if (schema.type !== "string") {
+ return "";
+ }
+ return html`
+
+ `;
+ }
+
+ private _handleSubmit(ev: Event) {
+ ev.preventDefault();
+ fireEvent(this, "form-submitted");
+ }
+
+ private _valueChanged(ev: Event) {
+ const target = ev.target! as HTMLInputElement;
+ this.stepData = { ...this.stepData, [target.id]: target.value };
+ fireEvent(this, "value-changed", {
+ value: this.stepData,
+ });
+ }
+}