web/flows: fix helper form not being removed from identification stage (improve password manager compatibility)

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2022-01-02 20:03:34 +01:00
parent ab17a12184
commit e72097292c
3 changed files with 33 additions and 9 deletions

View File

@ -131,7 +131,7 @@ export class FlowExecutor extends LitElement implements StageHost {
}); });
} }
submit(payload?: FlowChallengeResponseRequest): Promise<void> { submit(payload?: FlowChallengeResponseRequest): Promise<boolean> {
if (!payload) return Promise.reject(); if (!payload) return Promise.reject();
if (!this.challenge) return Promise.reject(); if (!this.challenge) return Promise.reject();
// @ts-ignore // @ts-ignore
@ -153,12 +153,18 @@ export class FlowExecutor extends LitElement implements StageHost {
); );
} }
this.challenge = data; this.challenge = data;
if (this.challenge.responseErrors) {
return false;
}
return true;
}) })
.catch((e: Error | Response) => { .catch((e: Error | Response) => {
this.errorMessage(e); this.errorMessage(e);
return false;
}) })
.finally(() => { .finally(() => {
this.loading = false; this.loading = false;
return false;
}); });
} }

View File

@ -7,7 +7,7 @@ export interface StageHost {
challenge?: unknown; challenge?: unknown;
flowSlug: string; flowSlug: string;
loading: boolean; loading: boolean;
submit(payload: unknown): Promise<void>; submit(payload: unknown): Promise<boolean>;
} }
export class BaseStage<Tin, Tout> extends LitElement { export class BaseStage<Tin, Tout> extends LitElement {
@ -16,14 +16,19 @@ export class BaseStage<Tin, Tout> extends LitElement {
@property({ attribute: false }) @property({ attribute: false })
challenge!: Tin; challenge!: Tin;
submitForm(e: Event): void { async submitForm(e: Event): Promise<boolean> {
e.preventDefault(); e.preventDefault();
const object: { const object: {
[key: string]: unknown; [key: string]: unknown;
} = {}; } = {};
const form = new FormData(this.shadowRoot?.querySelector("form") || undefined); const form = new FormData(this.shadowRoot?.querySelector("form") || undefined);
form.forEach((value, key) => (object[key] = value)); form.forEach((value, key) => (object[key] = value));
this.host?.submit(object as unknown as Tout); return this.host?.submit(object as unknown as Tout).then((successful) => {
if (successful) {
this.cleanup();
}
return successful;
});
} }
renderNonFieldErrors(errors: ErrorDetail[]): TemplateResult { renderNonFieldErrors(errors: ErrorDetail[]): TemplateResult {
@ -41,4 +46,9 @@ export class BaseStage<Tin, Tout> extends LitElement {
})} })}
</div>`; </div>`;
} }
cleanup(): void {
// Method that can be overridden by stages
return;
}
} }

View File

@ -41,6 +41,8 @@ export class IdentificationStage extends BaseStage<
IdentificationChallenge, IdentificationChallenge,
IdentificationChallengeResponseRequest IdentificationChallengeResponseRequest
> { > {
form?: HTMLFormElement;
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return [ return [
PFBase, PFBase,
@ -72,8 +74,8 @@ export class IdentificationStage extends BaseStage<
} }
firstUpdated(): void { firstUpdated(): void {
const wrapperForm = document.createElement("form"); this.form = document.createElement("form");
document.documentElement.appendChild(wrapperForm); document.documentElement.appendChild(this.form);
// This is a workaround for the fact that we're in a shadow dom // This is a workaround for the fact that we're in a shadow dom
// adapted from https://github.com/home-assistant/frontend/issues/3133 // adapted from https://github.com/home-assistant/frontend/issues/3133
const username = document.createElement("input"); const username = document.createElement("input");
@ -91,7 +93,7 @@ export class IdentificationStage extends BaseStage<
input.focus(); input.focus();
}); });
}; };
wrapperForm.appendChild(username); this.form.appendChild(username);
const password = document.createElement("input"); const password = document.createElement("input");
password.setAttribute("type", "password"); password.setAttribute("type", "password");
password.setAttribute("name", "password"); password.setAttribute("name", "password");
@ -115,7 +117,7 @@ export class IdentificationStage extends BaseStage<
input.focus(); input.focus();
}); });
}; };
wrapperForm.appendChild(password); this.form.appendChild(password);
const totp = document.createElement("input"); const totp = document.createElement("input");
totp.setAttribute("type", "text"); totp.setAttribute("type", "text");
totp.setAttribute("name", "code"); totp.setAttribute("name", "code");
@ -139,7 +141,13 @@ export class IdentificationStage extends BaseStage<
input.focus(); input.focus();
}); });
}; };
wrapperForm.appendChild(totp); this.form.appendChild(totp);
}
cleanup(): void {
if (this.form) {
document.documentElement.removeChild(this.form);
}
} }
renderSource(source: LoginSource): TemplateResult { renderSource(source: LoginSource): TemplateResult {