diff --git a/web/apps/auth/src/components/OTPDisplay.tsx b/web/apps/auth/src/components/OTPDisplay.tsx
index 1168fe8cb5..e69de29bb2 100644
--- a/web/apps/auth/src/components/OTPDisplay.tsx
+++ b/web/apps/auth/src/components/OTPDisplay.tsx
@@ -1,274 +0,0 @@
-import { ButtonBase, Snackbar } from "@mui/material";
-import { t } from "i18next";
-import { HOTP, TOTP } from "otpauth";
-import { useEffect, useState } from "react";
-import { Code } from "types/code";
-
-const TOTPDisplay = ({ issuer, account, code, nextCode, period }) => {
- return (
-
-
-
-
-
- {issuer}
-
-
- {account}
-
-
- {code}
-
-
-
-
-
- {t("AUTH_NEXT")}
-
-
- {nextCode}
-
-
-
-
- );
-};
-
-function BadCodeInfo({ codeInfo, codeErr }) {
- const [showRawData, setShowRawData] = useState(false);
-
- return (
-
-
{codeInfo.title}
-
{codeErr}
-
- {showRawData ? (
-
setShowRawData(false)}>
- {codeInfo.rawData ?? "no raw data"}
-
- ) : (
-
setShowRawData(true)}>Show rawData
- )}
-
-
- );
-}
-
-interface OTPDisplayProps {
- codeInfo: Code;
-}
-
-const OTPDisplay = (props: OTPDisplayProps) => {
- const { codeInfo } = props;
- const [code, setCode] = useState("");
- const [nextCode, setNextCode] = useState("");
- const [codeErr, setCodeErr] = useState("");
- const [hasCopied, setHasCopied] = useState(false);
-
- const generateCodes = () => {
- try {
- const currentTime = new Date().getTime();
- if (codeInfo.type.toLowerCase() === "totp") {
- const totp = new TOTP({
- secret: codeInfo.secret,
- algorithm: codeInfo.algorithm ?? Code.defaultAlgo,
- period: codeInfo.period ?? Code.defaultPeriod,
- digits: codeInfo.digits ?? Code.defaultDigits,
- });
- setCode(totp.generate());
- setNextCode(
- totp.generate({
- timestamp: currentTime + codeInfo.period * 1000,
- }),
- );
- } else if (codeInfo.type.toLowerCase() === "hotp") {
- const hotp = new HOTP({
- secret: codeInfo.secret,
- counter: 0,
- algorithm: codeInfo.algorithm,
- });
- setCode(hotp.generate());
- setNextCode(hotp.generate({ counter: 1 }));
- }
- } catch (err) {
- setCodeErr(err.message);
- }
- };
-
- const copyCode = () => {
- navigator.clipboard.writeText(code);
- setHasCopied(true);
- setTimeout(() => {
- setHasCopied(false);
- }, 2000);
- };
-
- useEffect(() => {
- // this is to set the initial code and nextCode on component mount
- generateCodes();
- const codeType = codeInfo.type;
- const codePeriodInMs = codeInfo.period * 1000;
- const timeToNextCode =
- codePeriodInMs - (new Date().getTime() % codePeriodInMs);
- const intervalId = null;
- // wait until we are at the start of the next code period,
- // and then start the interval loop
- setTimeout(() => {
- // we need to call generateCodes() once before the interval loop
- // to set the initial code and nextCode
- generateCodes();
- codeType.toLowerCase() === "totp" ||
- codeType.toLowerCase() === "hotp"
- ? setInterval(() => {
- generateCodes();
- }, codePeriodInMs)
- : null;
- }, timeToNextCode);
-
- return () => {
- if (intervalId) clearInterval(intervalId);
- };
- }, [codeInfo]);
-
- return (
-
- {codeErr === "" ? (
- {
- copyCode();
- }}
- >
-
-
-
- ) : (
-
- )}
-
- );
-};
-
-export default OTPDisplay;
-
-const TimerProgress = ({ period }) => {
- const [progress, setProgress] = useState(0);
- const [ticker, setTicker] = useState(null);
- const microSecondsInPeriod = period * 1000000;
-
- const startTicker = () => {
- const ticker = setInterval(() => {
- updateTimeRemaining();
- }, 10);
- setTicker(ticker);
- };
-
- const updateTimeRemaining = () => {
- const timeRemaining =
- microSecondsInPeriod -
- ((new Date().getTime() * 1000) % microSecondsInPeriod);
- setProgress(timeRemaining / microSecondsInPeriod);
- };
-
- useEffect(() => {
- startTicker();
- return () => clearInterval(ticker);
- }, []);
-
- const color = progress > 0.4 ? "green" : "orange";
-
- return (
-
- );
-};
diff --git a/web/apps/auth/src/pages/auth.tsx b/web/apps/auth/src/pages/auth.tsx
index 14135e20df..ad614c2f4a 100644
--- a/web/apps/auth/src/pages/auth.tsx
+++ b/web/apps/auth/src/pages/auth.tsx
@@ -12,13 +12,14 @@ import { CustomError } from "@ente/shared/error";
import InMemoryStore, { MS_KEYS } from "@ente/shared/storage/InMemoryStore";
import LogoutOutlined from "@mui/icons-material/LogoutOutlined";
import MoreHoriz from "@mui/icons-material/MoreHoriz";
-import { Button, TextField } from "@mui/material";
-import OTPDisplay from "components/OTPDisplay";
+import { Button, ButtonBase, Snackbar, TextField } from "@mui/material";
import { t } from "i18next";
import { useRouter } from "next/router";
+import { HOTP, TOTP } from "otpauth";
import { AppContext } from "pages/_app";
import React, { useContext, useEffect, useState } from "react";
import { getAuthCodes } from "services";
+import { Code } from "types/code";
const AuthenticatorCodesPage = () => {
const appContext = useContext(AppContext);
@@ -181,3 +182,273 @@ const AuthFooter: React.FC = () => {
);
};
+
+const TOTPDisplay = ({ issuer, account, code, nextCode, period }) => {
+ return (
+
+
+
+
+
+ {issuer}
+
+
+ {account}
+
+
+ {code}
+
+
+
+
+
+ {t("AUTH_NEXT")}
+
+
+ {nextCode}
+
+
+
+
+ );
+};
+
+function BadCodeInfo({ codeInfo, codeErr }) {
+ const [showRawData, setShowRawData] = useState(false);
+
+ return (
+
+
{codeInfo.title}
+
{codeErr}
+
+ {showRawData ? (
+
setShowRawData(false)}>
+ {codeInfo.rawData ?? "no raw data"}
+
+ ) : (
+
setShowRawData(true)}>Show rawData
+ )}
+
+
+ );
+}
+
+interface OTPDisplayProps {
+ codeInfo: Code;
+}
+
+const OTPDisplay: React.FC = ({ codeInfo }) => {
+ const [code, setCode] = useState("");
+ const [nextCode, setNextCode] = useState("");
+ const [codeErr, setCodeErr] = useState("");
+ const [hasCopied, setHasCopied] = useState(false);
+
+ const generateCodes = () => {
+ try {
+ const currentTime = new Date().getTime();
+ if (codeInfo.type.toLowerCase() === "totp") {
+ const totp = new TOTP({
+ secret: codeInfo.secret,
+ algorithm: codeInfo.algorithm ?? Code.defaultAlgo,
+ period: codeInfo.period ?? Code.defaultPeriod,
+ digits: codeInfo.digits ?? Code.defaultDigits,
+ });
+ setCode(totp.generate());
+ setNextCode(
+ totp.generate({
+ timestamp: currentTime + codeInfo.period * 1000,
+ }),
+ );
+ } else if (codeInfo.type.toLowerCase() === "hotp") {
+ const hotp = new HOTP({
+ secret: codeInfo.secret,
+ counter: 0,
+ algorithm: codeInfo.algorithm,
+ });
+ setCode(hotp.generate());
+ setNextCode(hotp.generate({ counter: 1 }));
+ }
+ } catch (err) {
+ setCodeErr(err.message);
+ }
+ };
+
+ const copyCode = () => {
+ navigator.clipboard.writeText(code);
+ setHasCopied(true);
+ setTimeout(() => {
+ setHasCopied(false);
+ }, 2000);
+ };
+
+ useEffect(() => {
+ // this is to set the initial code and nextCode on component mount
+ generateCodes();
+ const codeType = codeInfo.type;
+ const codePeriodInMs = codeInfo.period * 1000;
+ const timeToNextCode =
+ codePeriodInMs - (new Date().getTime() % codePeriodInMs);
+ const intervalId = null;
+ // wait until we are at the start of the next code period,
+ // and then start the interval loop
+ setTimeout(() => {
+ // we need to call generateCodes() once before the interval loop
+ // to set the initial code and nextCode
+ generateCodes();
+ codeType.toLowerCase() === "totp" ||
+ codeType.toLowerCase() === "hotp"
+ ? setInterval(() => {
+ generateCodes();
+ }, codePeriodInMs)
+ : null;
+ }, timeToNextCode);
+
+ return () => {
+ if (intervalId) clearInterval(intervalId);
+ };
+ }, [codeInfo]);
+
+ return (
+
+ {codeErr === "" ? (
+ {
+ copyCode();
+ }}
+ >
+
+
+
+ ) : (
+
+ )}
+
+ );
+};
+
+interface TimerProgressProps {
+ period: number;
+}
+
+const TimerProgress: React.FC = ({ period }) => {
+ const [progress, setProgress] = useState(0);
+ const [ticker, setTicker] = useState(null);
+ const microSecondsInPeriod = period * 1000000;
+
+ const startTicker = () => {
+ const ticker = setInterval(() => {
+ updateTimeRemaining();
+ }, 10);
+ setTicker(ticker);
+ };
+
+ const updateTimeRemaining = () => {
+ const timeRemaining =
+ microSecondsInPeriod -
+ ((new Date().getTime() * 1000) % microSecondsInPeriod);
+ setProgress(timeRemaining / microSecondsInPeriod);
+ };
+
+ useEffect(() => {
+ startTicker();
+ return () => clearInterval(ticker);
+ }, []);
+
+ const color = progress > 0.4 ? "green" : "orange";
+
+ return (
+
+ );
+};