| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  | import React, { SyntheticEvent, useState } from "react"; | 
					
						
							| 
									
										
										
										
											2021-09-22 19:52:38 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-16 23:36:43 +00:00
										 |  |  | import Button from "@calcom/ui/Button"; | 
					
						
							|  |  |  | import { Dialog, DialogContent } from "@calcom/ui/Dialog"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  | import { ErrorCode } from "@lib/auth"; | 
					
						
							| 
									
										
										
										
											2021-10-08 11:43:48 +00:00
										 |  |  | import { useLocale } from "@lib/hooks/useLocale"; | 
					
						
							| 
									
										
										
										
											2021-09-22 19:52:38 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  | import TwoFactorAuthAPI from "./TwoFactorAuthAPI"; | 
					
						
							|  |  |  | import TwoFactorModalHeader from "./TwoFactorModalHeader"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | interface EnableTwoFactorModalProps { | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Called when the user closes the modal without disabling two-factor auth | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   onCancel: () => void; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Called when the user enables two-factor auth | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   onEnable: () => void; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | enum SetupStep { | 
					
						
							|  |  |  |   ConfirmPassword, | 
					
						
							|  |  |  |   DisplayQrCode, | 
					
						
							|  |  |  |   EnterTotpCode, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const WithStep = ({ | 
					
						
							|  |  |  |   step, | 
					
						
							|  |  |  |   current, | 
					
						
							|  |  |  |   children, | 
					
						
							|  |  |  | }: { | 
					
						
							|  |  |  |   step: SetupStep; | 
					
						
							|  |  |  |   current: SetupStep; | 
					
						
							|  |  |  |   children: JSX.Element; | 
					
						
							|  |  |  | }) => { | 
					
						
							|  |  |  |   return step === current ? children : null; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-12 13:11:33 +00:00
										 |  |  | const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps) => { | 
					
						
							| 
									
										
										
										
											2021-10-15 10:53:42 +00:00
										 |  |  |   const { t } = useLocale(); | 
					
						
							|  |  |  |   const setupDescriptions = { | 
					
						
							|  |  |  |     [SetupStep.ConfirmPassword]: t("2fa_confirm_current_password"), | 
					
						
							|  |  |  |     [SetupStep.DisplayQrCode]: t("2fa_scan_image_or_use_code"), | 
					
						
							|  |  |  |     [SetupStep.EnterTotpCode]: t("2fa_enter_six_digit_code"), | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |   const [step, setStep] = useState(SetupStep.ConfirmPassword); | 
					
						
							|  |  |  |   const [password, setPassword] = useState(""); | 
					
						
							|  |  |  |   const [totpCode, setTotpCode] = useState(""); | 
					
						
							|  |  |  |   const [dataUri, setDataUri] = useState(""); | 
					
						
							| 
									
										
										
										
											2021-09-25 15:53:12 +00:00
										 |  |  |   const [secret, setSecret] = useState(""); | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |   const [isSubmitting, setIsSubmitting] = useState(false); | 
					
						
							|  |  |  |   const [errorMessage, setErrorMessage] = useState<string | null>(null); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async function handleSetup(e: SyntheticEvent) { | 
					
						
							|  |  |  |     e.preventDefault(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (isSubmitting) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     setIsSubmitting(true); | 
					
						
							|  |  |  |     setErrorMessage(null); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       const response = await TwoFactorAuthAPI.setup(password); | 
					
						
							|  |  |  |       const body = await response.json(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (response.status === 200) { | 
					
						
							|  |  |  |         setDataUri(body.dataUri); | 
					
						
							| 
									
										
										
										
											2021-09-25 15:53:12 +00:00
										 |  |  |         setSecret(body.secret); | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |         setStep(SetupStep.DisplayQrCode); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (body.error === ErrorCode.IncorrectPassword) { | 
					
						
							| 
									
										
										
										
											2021-10-08 11:43:48 +00:00
										 |  |  |         setErrorMessage(t("incorrect_password")); | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2021-10-08 11:43:48 +00:00
										 |  |  |         setErrorMessage(t("something_went_wrong")); | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							| 
									
										
										
										
											2021-10-08 11:43:48 +00:00
										 |  |  |       setErrorMessage(t("something_went_wrong")); | 
					
						
							|  |  |  |       console.error(t("error_enabling_2fa"), e); | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |     } finally { | 
					
						
							|  |  |  |       setIsSubmitting(false); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   async function handleEnable(e: SyntheticEvent) { | 
					
						
							|  |  |  |     e.preventDefault(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (isSubmitting || totpCode.length !== 6) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     setIsSubmitting(true); | 
					
						
							|  |  |  |     setErrorMessage(null); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       const response = await TwoFactorAuthAPI.enable(totpCode); | 
					
						
							|  |  |  |       const body = await response.json(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (response.status === 200) { | 
					
						
							|  |  |  |         onEnable(); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (body.error === ErrorCode.IncorrectTwoFactorCode) { | 
					
						
							| 
									
										
										
										
											2021-10-08 11:43:48 +00:00
										 |  |  |         setErrorMessage(`${t("code_is_incorrect")} ${t("please_try_again")}`); | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2021-10-08 11:43:48 +00:00
										 |  |  |         setErrorMessage(t("something_went_wrong")); | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							| 
									
										
										
										
											2021-10-08 11:43:48 +00:00
										 |  |  |       setErrorMessage(t("something_went_wrong")); | 
					
						
							|  |  |  |       console.error(t("error_enabling_2fa"), e); | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |     } finally { | 
					
						
							|  |  |  |       setIsSubmitting(false); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return ( | 
					
						
							|  |  |  |     <Dialog open={true}> | 
					
						
							|  |  |  |       <DialogContent> | 
					
						
							| 
									
										
										
										
											2021-10-08 11:43:48 +00:00
										 |  |  |         <TwoFactorModalHeader title={t("enable_2fa")} description={setupDescriptions[step]} /> | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         <WithStep step={SetupStep.ConfirmPassword} current={step}> | 
					
						
							|  |  |  |           <form onSubmit={handleSetup}> | 
					
						
							|  |  |  |             <div className="mb-4"> | 
					
						
							|  |  |  |               <label htmlFor="password" className="mt-4 block text-sm font-medium text-gray-700"> | 
					
						
							| 
									
										
										
										
											2021-10-08 11:43:48 +00:00
										 |  |  |                 {t("password")} | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |               </label> | 
					
						
							|  |  |  |               <div className="mt-1"> | 
					
						
							|  |  |  |                 <input | 
					
						
							|  |  |  |                   type="password" | 
					
						
							|  |  |  |                   name="password" | 
					
						
							|  |  |  |                   id="password" | 
					
						
							|  |  |  |                   required | 
					
						
							|  |  |  |                   value={password} | 
					
						
							|  |  |  |                   onInput={(e) => setPassword(e.currentTarget.value)} | 
					
						
							| 
									
										
										
										
											2022-02-09 00:05:13 +00:00
										 |  |  |                   className="block w-full rounded-sm border-gray-300 shadow-sm focus:border-neutral-900 focus:ring-neutral-900 sm:text-sm" | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |                 /> | 
					
						
							|  |  |  |               </div> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               {errorMessage && <p className="mt-1 text-sm text-red-700">{errorMessage}</p>} | 
					
						
							|  |  |  |             </div> | 
					
						
							|  |  |  |           </form> | 
					
						
							|  |  |  |         </WithStep> | 
					
						
							|  |  |  |         <WithStep step={SetupStep.DisplayQrCode} current={step}> | 
					
						
							| 
									
										
										
										
											2021-09-25 15:53:12 +00:00
										 |  |  |           <> | 
					
						
							|  |  |  |             <div className="flex justify-center"> | 
					
						
							|  |  |  |               <img src={dataUri} /> | 
					
						
							|  |  |  |             </div> | 
					
						
							| 
									
										
										
										
											2022-02-09 22:32:31 +00:00
										 |  |  |             <p className="text-center font-mono text-xs">{secret}</p> | 
					
						
							| 
									
										
										
										
											2021-09-25 15:53:12 +00:00
										 |  |  |           </> | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |         </WithStep> | 
					
						
							|  |  |  |         <WithStep step={SetupStep.EnterTotpCode} current={step}> | 
					
						
							|  |  |  |           <form onSubmit={handleEnable}> | 
					
						
							|  |  |  |             <div className="mb-4"> | 
					
						
							|  |  |  |               <label htmlFor="code" className="mt-4 block text-sm font-medium text-gray-700"> | 
					
						
							| 
									
										
										
										
											2021-10-08 11:43:48 +00:00
										 |  |  |                 {t("code")} | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |               </label> | 
					
						
							|  |  |  |               <div className="mt-1"> | 
					
						
							|  |  |  |                 <input | 
					
						
							|  |  |  |                   type="text" | 
					
						
							|  |  |  |                   name="code" | 
					
						
							|  |  |  |                   id="code" | 
					
						
							|  |  |  |                   required | 
					
						
							|  |  |  |                   value={totpCode} | 
					
						
							|  |  |  |                   maxLength={6} | 
					
						
							|  |  |  |                   minLength={6} | 
					
						
							|  |  |  |                   inputMode="numeric" | 
					
						
							|  |  |  |                   onInput={(e) => setTotpCode(e.currentTarget.value)} | 
					
						
							| 
									
										
										
										
											2022-02-09 00:05:13 +00:00
										 |  |  |                   className="block w-full rounded-sm border-gray-300 shadow-sm focus:border-neutral-900 focus:ring-neutral-900 sm:text-sm" | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |                 /> | 
					
						
							|  |  |  |               </div> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               {errorMessage && <p className="mt-1 text-sm text-red-700">{errorMessage}</p>} | 
					
						
							|  |  |  |             </div> | 
					
						
							|  |  |  |           </form> | 
					
						
							|  |  |  |         </WithStep> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         <div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse"> | 
					
						
							|  |  |  |           <WithStep step={SetupStep.ConfirmPassword} current={step}> | 
					
						
							|  |  |  |             <Button | 
					
						
							|  |  |  |               type="submit" | 
					
						
							| 
									
										
										
										
											2022-02-01 22:17:37 +00:00
										 |  |  |               className="ltr:ml-2 rtl:mr-2" | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |               onClick={handleSetup} | 
					
						
							|  |  |  |               disabled={password.length === 0 || isSubmitting}> | 
					
						
							| 
									
										
										
										
											2021-10-08 11:43:48 +00:00
										 |  |  |               {t("continue")} | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |             </Button> | 
					
						
							|  |  |  |           </WithStep> | 
					
						
							|  |  |  |           <WithStep step={SetupStep.DisplayQrCode} current={step}> | 
					
						
							| 
									
										
										
										
											2022-02-01 22:17:37 +00:00
										 |  |  |             <Button | 
					
						
							|  |  |  |               type="submit" | 
					
						
							|  |  |  |               className="ltr:ml-2 rtl:mr-2" | 
					
						
							|  |  |  |               onClick={() => setStep(SetupStep.EnterTotpCode)}> | 
					
						
							| 
									
										
										
										
											2021-10-08 11:43:48 +00:00
										 |  |  |               {t("continue")} | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |             </Button> | 
					
						
							|  |  |  |           </WithStep> | 
					
						
							|  |  |  |           <WithStep step={SetupStep.EnterTotpCode} current={step}> | 
					
						
							|  |  |  |             <Button | 
					
						
							|  |  |  |               type="submit" | 
					
						
							| 
									
										
										
										
											2022-02-01 22:17:37 +00:00
										 |  |  |               className="ltr:ml-2 rtl:mr-2" | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |               onClick={handleEnable} | 
					
						
							|  |  |  |               disabled={totpCode.length !== 6 || isSubmitting}> | 
					
						
							| 
									
										
										
										
											2021-10-08 11:43:48 +00:00
										 |  |  |               {t("enable")} | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |             </Button> | 
					
						
							|  |  |  |           </WithStep> | 
					
						
							|  |  |  |           <Button color="secondary" onClick={onCancel}> | 
					
						
							| 
									
										
										
										
											2021-10-08 11:43:48 +00:00
										 |  |  |             {t("cancel")} | 
					
						
							| 
									
										
										
										
											2021-09-21 09:29:20 +00:00
										 |  |  |           </Button> | 
					
						
							|  |  |  |         </div> | 
					
						
							|  |  |  |       </DialogContent> | 
					
						
							|  |  |  |     </Dialog> | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export default EnableTwoFactorModal; |