Compare commits

...

10 Commits

Author SHA1 Message Date
Viktor Fomin
8433b6931a Merge branch 'develop' into feature/tfa-sms 2023-09-13 11:42:07 +03:00
Viktor Fomin
0f748501d4 Client: Confirm: init phoneAuth page 2023-09-13 10:55:15 +03:00
Viktor Fomin
d540f9dee5 Client: Confirm: ChangePhone: add input phone, setMobilePhone method 2023-09-13 10:54:39 +03:00
Viktor Fomin
0c0a4982fd Common: add setMobilePhone method 2023-09-13 10:53:52 +03:00
Viktor Fomin
4df919c899 Merge branch 'develop' into feature/tfa-sms 2023-09-13 01:06:39 +03:00
Viktor Fomin
f3db257ee2 Client: add translates 2023-09-12 22:14:02 +03:00
Viktor Fomin
a8780eee28 Common: Utils: fix login 2023-09-12 22:12:29 +03:00
Viktor Fomin
b565459077 Components: InputPhone: add className prop 2023-09-12 13:43:17 +03:00
Viktor Fomin
219c9006da Client: PortalSettings: TFA: enable by sms 2023-09-12 13:11:36 +03:00
Viktor Fomin
dbf06ffdc7 Client: PortalSettings: Integration: enable sms services 2023-09-12 13:11:23 +03:00
9 changed files with 127 additions and 33 deletions

View File

@ -11,6 +11,8 @@
"EmailAndPasswordCopiedToClipboard": "Email and password copied to clipboard",
"EnterAppCodeDescription": "Enter 6-digit generated code from your app. If you don't have access to your phone, use the backup codes.",
"EnterAppCodeTitle": "Enter code from authentication app",
"EnterSmsCodeDescription": "An SMS with portal access code has been sent to your <1>{{ currentNumber }}</1> mobile number. Please enter the code and click the «Continue» button. If no message is received for more than three minutes, click the «Send code again» link.",
"EnterSmsCodeTitle": "Enter code from SMS",
"EnterCodePlaceholder": "Enter code",
"EnterPhone": "Enter mobile phone number",
"GetCode": "Get code",
@ -22,6 +24,7 @@
"PortalDeactivateTitle": "Please confirm that you want to deactivate your DocSpace.",
"PortalRemoveTitle": "Please confirm that you want to delete your DocSpace.",
"Reactivate": "Reactivate",
"SendCodeAgain": "Send code again",
"SetAppButton": "Connect app",
"SetAppDescription": "Two-factor authentication is enabled. Configure your authenticator app to continue working in the DocSpace. You can use Google Authenticator for <1>Android</1> and <4>iOS</4> or Authenticator for <8>Windows Phone</8>.",
"SetAppInstallDescription": "To connect the app, scan the QR code or manually enter your secret key <1>{{ secretKey }}</1>, and then enter a 6-digit code from your app in the field below.",

View File

@ -1,8 +1,8 @@
import React, { useState } from "react";
import { useNavigate, useLocation } from "react-router-dom";
import { withTranslation } from "react-i18next";
import Text from "@docspace/components/text";
import TextInput from "@docspace/components/text-input";
import InputPhone from "@docspace/components/input-phone";
import Button from "@docspace/components/button";
import { inject, observer } from "mobx-react";
import { StyledPage, StyledBody, StyledContent } from "./StyledConfirm";
@ -11,41 +11,44 @@ import FormWrapper from "@docspace/components/form-wrapper";
import DocspaceLogo from "../../../DocspaceLogo";
const ChangePhoneForm = (props) => {
const { t, greetingTitle } = props;
const [currentNumber, setCurrentNumber] = useState("+00000000000");
const { t, setMobilePhone } = props;
const [currentNumber, setCurrentNumber] = useState("");
const [phone, setPhone] = useState("");
const navigate = useNavigate();
const location = useLocation();
const onChange = (e) => {
const value = e.target.value.replace(/[^0-9]/g, "");
console.log(value);
setPhone(value);
};
const onSubmit = async () => {
const res = await setMobilePhone(phone);
console.log(res);
};
return (
<StyledPage>
<StyledContent>
<StyledBody>
<DocspaceLogo className="docspace-logo" />
<Text fontSize="23px" fontWeight="700" className="title">
{greetingTitle}
</Text>
<FormWrapper>
<div className="subtitle">
<Text fontSize="16px" fontWeight="600" className="phone-title">
{t("EnterPhone")}
</Text>
<Text>
{t("CurrentNumber")}: {currentNumber}
</Text>
{currentNumber && (
<Text>
{t("CurrentNumber")}: {currentNumber}
</Text>
)}
<Text>{t("PhoneSubtitle")}</Text>
</div>
<TextInput
className="phone-input"
id="phone"
name="phone"
type="phone"
size="large"
scale={true}
isAutoFocussed={true}
tabIndex={1}
hasError={false}
guide={false}
/>
<InputPhone className="phone-input" scaled onChange={onChange} />
<Button
primary
@ -54,6 +57,7 @@ const ChangePhoneForm = (props) => {
label={t("GetCode")}
tabIndex={2}
isDisabled={false}
onClick={onSubmit}
/>
</FormWrapper>
</StyledBody>
@ -62,6 +66,10 @@ const ChangePhoneForm = (props) => {
);
};
export default inject(({ auth }) => ({
greetingTitle: auth.settingsStore.greetingSettings,
}))(withTranslation("Confirm")(withLoader(observer(ChangePhoneForm))));
export default inject(({ auth }) => {
const { tfaStore } = auth;
const { setMobilePhone } = tfaStore;
return {
setMobilePhone,
};
})(withTranslation("Confirm")(withLoader(observer(ChangePhoneForm))));

View File

@ -0,0 +1,64 @@
import React, { useState } from "react";
import { withTranslation } from "react-i18next";
import Text from "@docspace/components/text";
import TextInput from "@docspace/components/text-input";
import Button from "@docspace/components/button";
import { inject, observer } from "mobx-react";
import { StyledPage, StyledBody, StyledContent } from "./StyledConfirm";
import withLoader from "../withLoader";
import FormWrapper from "@docspace/components/form-wrapper";
import DocspaceLogo from "../../../DocspaceLogo";
const PhoneAuth = (props) => {
const { t } = props;
const [currentNumber, setCurrentNumber] = useState("");
return (
<StyledPage>
<StyledContent>
<StyledBody>
<DocspaceLogo className="docspace-logo" />
<FormWrapper>
<div className="subtitle">
<Text fontSize="16px" fontWeight="600" className="phone-title">
{t("EnterSmsCodeTitle")}
</Text>
<Text>
<Trans
t={t}
i18nKey="EnterSmsCodeDescription"
ns="Confirm"
key={currentNumber}
>
An SMS with portal access code has been sent to your
<strong>{{ currentNumber }}</strong> mobile number. Please
enter the code and click the «Continue» button. If no message
is received for more than three minutes, click the «Send code
again» link.
</Trans>
</Text>
</div>
<TextInput className="phone-input" scaled />
<Button
primary
scale
size="medium"
label={t("Common:ContinueButton")}
tabIndex={2}
isDisabled={false}
/>
</FormWrapper>
</StyledBody>
</StyledContent>
</StyledPage>
);
};
export default inject(({ auth }) => ({
greetingTitle: auth.settingsStore.greetingSettings,
}))(withTranslation(["Confirm", "Common"])(withLoader(observer(PhoneAuth))));

View File

@ -164,9 +164,7 @@ class ThirdPartyServices extends React.Component {
(consumer) =>
consumer.title !== "Bitly" &&
consumer.title !== "WordPress" &&
consumer.title !== "DocuSign" &&
consumer.name !== "clickatell" && //TODO: hide while 2fa by sms is not working
consumer.name !== "twilio"
consumer.title !== "DocuSign"
);
const imgSrc = theme.isBase ? IntegrationSvgUrl : IntegrationDarkSvgUrl;

View File

@ -163,13 +163,12 @@ const TwoFactorAuth = (props) => {
label: t("Disabled"),
value: "none",
},
//TODO: hide while 2fa by sms is not working
/*{
{
id: "by-sms",
label: t("BySms"),
value: "sms",
disabled: !smsDisabled,
},*/
},
{
id: "by-app",
label: t("ByApp"),

View File

@ -298,3 +298,17 @@ export function getPortal() {
};
return request(options);
}
export function setMobilePhone(mobilePhone) {
const data = {
mobilePhone,
};
const options = {
method: "post",
url: "/authentication/setphone",
data,
};
return request(options);
}

View File

@ -69,6 +69,10 @@ class TfaStore {
unlinkApp = async (id) => {
return api.settings.unlinkTfaApp(id);
};
setMobilePhone = async (mobilePhone) => {
return await api.portal.setMobilePhone(mobilePhone);
};
}
export default TfaStore;

View File

@ -9,10 +9,10 @@ export async function login(
try {
const response = await api.user.login(user, hash, session, captchaToken);
if (!response || (!response.token && !response.tfa))
if (!response || (!response.token && !response.tfa && !response.sms))
throw response.error.message;
if (response.tfa && response.confirmUrl) {
if ((response.tfa || response.sms) && response.confirmUrl) {
const url = response.confirmUrl.replace(window.location.origin, "");
return url;
}

View File

@ -27,6 +27,7 @@ const InputPhone = ({
searchPlaceholderText,
searchEmptyMessage,
errorMessage,
className,
} = props) => {
const [country, setCountry] = useState(defaultCountry);
const [phoneValue, setPhoneValue] = useState(country.dialCode);
@ -140,6 +141,7 @@ const InputPhone = ({
return (
<StyledBox
className={className}
hasError={!isValid}
displayProp="flex"
alignItems="center"
@ -224,6 +226,8 @@ const InputPhone = ({
};
InputPhone.propTypes = {
/** Accepts class */
className: PropTypes.string,
/** Default selected country */
defaultCountry: PropTypes.object.isRequired,
/** Text displayed on the Input placeholder */