mirror of
https://github.com/home-assistant/frontend.git
synced 2025-06-06 10:16:34 +00:00
Add MFA login flow support for cloud component (#23188)
* Add MFA login flow support for cloud component * Update MFA login in voice assistant setup flow * Sync errors with core * Add translations to the TOTP dialog
This commit is contained in:
parent
0ecdae2551
commit
1c076d22a6
@ -70,18 +70,27 @@ export interface CloudWebhook {
|
||||
managed?: boolean;
|
||||
}
|
||||
|
||||
export const cloudLogin = (
|
||||
hass: HomeAssistant,
|
||||
email: string,
|
||||
password: string
|
||||
) =>
|
||||
interface CloudLoginBase {
|
||||
hass: HomeAssistant;
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface CloudLoginPassword extends CloudLoginBase {
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface CloudLoginMFA extends CloudLoginBase {
|
||||
code: string;
|
||||
}
|
||||
|
||||
export const cloudLogin = ({
|
||||
hass,
|
||||
...rest
|
||||
}: CloudLoginPassword | CloudLoginMFA) =>
|
||||
hass.callApi<{ success: boolean; cloud_pipeline?: string }>(
|
||||
"POST",
|
||||
"cloud/login",
|
||||
{
|
||||
email,
|
||||
password,
|
||||
}
|
||||
rest
|
||||
);
|
||||
|
||||
export const cloudLogout = (hass: HomeAssistant) =>
|
||||
|
@ -11,7 +11,10 @@ import "../../../components/ha-textfield";
|
||||
import type { HaTextField } from "../../../components/ha-textfield";
|
||||
import { cloudLogin } from "../../../data/cloud";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { showAlertDialog } from "../../generic/show-dialog-box";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showPromptDialog,
|
||||
} from "../../generic/show-dialog-box";
|
||||
import { AssistantSetupStyles } from "../styles";
|
||||
|
||||
@customElement("cloud-step-signin")
|
||||
@ -106,12 +109,36 @@ export class CloudStepSignin extends LitElement {
|
||||
|
||||
this._requestInProgress = true;
|
||||
|
||||
const doLogin = async (username: string) => {
|
||||
const doLogin = async (username: string, code?: string) => {
|
||||
try {
|
||||
await cloudLogin(this.hass, username, password);
|
||||
await cloudLogin({
|
||||
hass: this.hass,
|
||||
email: username,
|
||||
...(code ? { code } : { password }),
|
||||
});
|
||||
} catch (err: any) {
|
||||
const errCode = err && err.body && err.body.code;
|
||||
|
||||
if (errCode === "mfarequired") {
|
||||
const totpCode = await showPromptDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.cloud.login.totp_code_prompt_title"
|
||||
),
|
||||
inputLabel: this.hass.localize(
|
||||
"ui.panel.config.cloud.login.totp_code"
|
||||
),
|
||||
inputType: "text",
|
||||
defaultValue: "",
|
||||
confirmText: this.hass.localize(
|
||||
"ui.panel.config.cloud.login.submit"
|
||||
),
|
||||
});
|
||||
if (totpCode !== null && totpCode !== "") {
|
||||
await doLogin(username, totpCode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (errCode === "usernotfound" && username !== username.toLowerCase()) {
|
||||
await doLogin(username.toLowerCase());
|
||||
return;
|
||||
@ -130,15 +157,33 @@ export class CloudStepSignin extends LitElement {
|
||||
|
||||
this._requestInProgress = false;
|
||||
|
||||
if (errCode === "UserNotConfirmed") {
|
||||
this._error = this.hass.localize(
|
||||
"ui.panel.config.cloud.login.alert_email_confirm_necessary"
|
||||
);
|
||||
} else {
|
||||
this._error =
|
||||
err && err.body && err.body.message
|
||||
? err.body.message
|
||||
: "Unknown error";
|
||||
switch (errCode) {
|
||||
case "UserNotConfirmed":
|
||||
this._error = this.hass.localize(
|
||||
"ui.panel.config.cloud.login.alert_email_confirm_necessary"
|
||||
);
|
||||
break;
|
||||
case "mfarequired":
|
||||
this._error = this.hass.localize(
|
||||
"ui.panel.config.cloud.login.alert_mfa_code_required"
|
||||
);
|
||||
break;
|
||||
case "mfaexpiredornotstarted":
|
||||
this._error = this.hass.localize(
|
||||
"ui.panel.config.cloud.login.alert_mfa_expired_or_not_started"
|
||||
);
|
||||
break;
|
||||
case "invalidtotpcode":
|
||||
this._error = this.hass.localize(
|
||||
"ui.panel.config.cloud.login.alert_totp_code_invalid"
|
||||
);
|
||||
break;
|
||||
default:
|
||||
this._error =
|
||||
err && err.body && err.body.message
|
||||
? err.body.message
|
||||
: "Unknown error";
|
||||
break;
|
||||
}
|
||||
|
||||
emailField.focus();
|
||||
|
@ -190,7 +190,11 @@ export class CloudStepSignup extends LitElement {
|
||||
}
|
||||
|
||||
try {
|
||||
await cloudLogin(this.hass, this._email, this._password);
|
||||
await cloudLogin({
|
||||
hass: this.hass,
|
||||
email: this._email,
|
||||
password: this._password,
|
||||
});
|
||||
fireEvent(this, "cloud-step", { step: "DONE" });
|
||||
} catch (e: any) {
|
||||
if (e?.body?.code === "usernotconfirmed") {
|
||||
|
@ -21,6 +21,7 @@ import { cloudLogin, removeCloudData } from "../../../../data/cloud";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
showPromptDialog,
|
||||
} from "../../../../dialogs/generic/show-dialog-box";
|
||||
import "../../../../layouts/hass-subpage";
|
||||
import { haStyle } from "../../../../resources/styles";
|
||||
@ -230,9 +231,13 @@ export class CloudLogin extends LitElement {
|
||||
|
||||
this._requestInProgress = true;
|
||||
|
||||
const doLogin = async (username: string) => {
|
||||
const doLogin = async (username: string, code?: string) => {
|
||||
try {
|
||||
const result = await cloudLogin(this.hass, username, password);
|
||||
const result = await cloudLogin({
|
||||
hass: this.hass,
|
||||
email: username,
|
||||
...(code ? { code } : { password }),
|
||||
});
|
||||
this.email = "";
|
||||
this._password = "";
|
||||
if (result.cloud_pipeline) {
|
||||
@ -252,6 +257,25 @@ export class CloudLogin extends LitElement {
|
||||
fireEvent(this, "ha-refresh-cloud-status");
|
||||
} catch (err: any) {
|
||||
const errCode = err && err.body && err.body.code;
|
||||
if (errCode === "mfarequired") {
|
||||
const totpCode = await showPromptDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.cloud.login.totp_code_prompt_title"
|
||||
),
|
||||
inputLabel: this.hass.localize(
|
||||
"ui.panel.config.cloud.login.totp_code"
|
||||
),
|
||||
inputType: "text",
|
||||
defaultValue: "",
|
||||
confirmText: this.hass.localize(
|
||||
"ui.panel.config.cloud.login.submit"
|
||||
),
|
||||
});
|
||||
if (totpCode !== null && totpCode !== "") {
|
||||
await doLogin(username, totpCode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (errCode === "PasswordChangeRequired") {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
@ -269,15 +293,33 @@ export class CloudLogin extends LitElement {
|
||||
this._password = "";
|
||||
this._requestInProgress = false;
|
||||
|
||||
if (errCode === "UserNotConfirmed") {
|
||||
this._error = this.hass.localize(
|
||||
"ui.panel.config.cloud.login.alert_email_confirm_necessary"
|
||||
);
|
||||
} else {
|
||||
this._error =
|
||||
err && err.body && err.body.message
|
||||
? err.body.message
|
||||
: "Unknown error";
|
||||
switch (errCode) {
|
||||
case "UserNotConfirmed":
|
||||
this._error = this.hass.localize(
|
||||
"ui.panel.config.cloud.login.alert_email_confirm_necessary"
|
||||
);
|
||||
break;
|
||||
case "mfarequired":
|
||||
this._error = this.hass.localize(
|
||||
"ui.panel.config.cloud.login.alert_mfa_code_required"
|
||||
);
|
||||
break;
|
||||
case "mfaexpiredornotstarted":
|
||||
this._error = this.hass.localize(
|
||||
"ui.panel.config.cloud.login.alert_mfa_expired_or_not_started"
|
||||
);
|
||||
break;
|
||||
case "invalidtotpcode":
|
||||
this._error = this.hass.localize(
|
||||
"ui.panel.config.cloud.login.alert_totp_code_invalid"
|
||||
);
|
||||
break;
|
||||
default:
|
||||
this._error =
|
||||
err && err.body && err.body.message
|
||||
? err.body.message
|
||||
: "Unknown error";
|
||||
break;
|
||||
}
|
||||
|
||||
emailField.focus();
|
||||
|
@ -4016,11 +4016,17 @@
|
||||
"email_error_msg": "Invalid email",
|
||||
"password": "Password",
|
||||
"password_error_msg": "Passwords are at least 8 characters",
|
||||
"totp_code_prompt_title": "Two-factor authentication",
|
||||
"totp_code": "TOTP code",
|
||||
"submit": "Submit",
|
||||
"forgot_password": "Forgot password?",
|
||||
"start_trial": "Start your free 1 month trial",
|
||||
"trial_info": "No payment information necessary",
|
||||
"alert_password_change_required": "You need to change your password before logging in.",
|
||||
"alert_email_confirm_necessary": "You need to confirm your email before logging in.",
|
||||
"alert_mfa_code_required": "You need to enter your two-factor authentication code.",
|
||||
"alert_mfa_expired_or_not_started": "Multi-factor authentication expired, or not started. Please try again.",
|
||||
"alert_totp_code_invalid": "Invalid two-factor authentication code.",
|
||||
"cloud_pipeline_title": "Want to use Home Assistant Cloud for your voice assistant?",
|
||||
"cloud_pipeline_text": "We created a new assistant for you, using the superior text-to-speech and speech-to-text engines from Home Assistant Cloud. Would you like to set this assistant as the preferred assistant?"
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user