From 90d0196d471e04f0ec7b2dfc47117b1baf144449 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Thu, 23 May 2024 19:06:06 +0530 Subject: [PATCH] Extract logic --- web/apps/auth/src/pages/auth.tsx | 55 +++++++++--------------------- web/apps/auth/src/services/code.ts | 41 ++++++++++++++++++++++ 2 files changed, 58 insertions(+), 38 deletions(-) diff --git a/web/apps/auth/src/pages/auth.tsx b/web/apps/auth/src/pages/auth.tsx index e628050ea..4ad1b9920 100644 --- a/web/apps/auth/src/pages/auth.tsx +++ b/web/apps/auth/src/pages/auth.tsx @@ -15,10 +15,9 @@ import MoreHoriz from "@mui/icons-material/MoreHoriz"; 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 { Code } from "services/code"; +import { generateOTPs, type Code } from "services/code"; import { getAuthCodes } from "services/remote"; const AuthenticatorCodesPage = () => { @@ -172,33 +171,13 @@ const CodeDisplay: React.FC = ({ codeInfo }) => { const [codeErr, setCodeErr] = useState(""); const [hasCopied, setHasCopied] = useState(false); - const generateCodes = () => { + const regen = () => { try { - const currentTime = new Date().getTime(); - if (codeInfo.type === "totp") { - const totp = new TOTP({ - secret: codeInfo.secret, - algorithm: codeInfo.algorithm, - period: codeInfo.period, - digits: codeInfo.digits, - }); - setOTP(totp.generate()); - setNextOTP( - totp.generate({ - timestamp: currentTime + codeInfo.period * 1000, - }), - ); - } else if (codeInfo.type === "hotp") { - const hotp = new HOTP({ - secret: codeInfo.secret, - counter: 0, - algorithm: codeInfo.algorithm, - }); - setOTP(hotp.generate()); - setNextOTP(hotp.generate({ counter: 1 })); - } - } catch (err) { - setCodeErr(err.message); + const [m, n] = generateOTPs(codeInfo); + setOTP(m); + setNextOTP(n); + } catch (e) { + setCodeErr(e.message); } }; @@ -211,29 +190,29 @@ const CodeDisplay: React.FC = ({ codeInfo }) => { }; useEffect(() => { - // this is to set the initial code and nextCode on component mount - generateCodes(); + // Generate to set the initial otp and nextOTP on component mount. + regen(); 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 + const interval = 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(); + // We need to call regen() once before the interval loop to set the + // initial otp and nextOTP. + regen(); codeType.toLowerCase() === "totp" || codeType.toLowerCase() === "hotp" ? setInterval(() => { - generateCodes(); + regen(); }, codePeriodInMs) : null; }, timeToNextCode); return () => { - if (intervalId) clearInterval(intervalId); + if (interval) clearInterval(interval); }; }, [codeInfo]); diff --git a/web/apps/auth/src/services/code.ts b/web/apps/auth/src/services/code.ts index 87e8887ef..15eb15aaf 100644 --- a/web/apps/auth/src/services/code.ts +++ b/web/apps/auth/src/services/code.ts @@ -1,3 +1,4 @@ +import { HOTP, TOTP } from "otpauth"; import { URI } from "vscode-uri"; /** @@ -145,3 +146,43 @@ const _getType = (uriPath: string): Code["type"] => { const getSanitizedSecret = (uriParams): string => { return uriParams["secret"].replace(/ /g, "").toUpperCase(); }; + +/** + * Generate a pair of OTPs (one time passwords) from the given {@link code}. + * + * @param code The parsed code data, including the secret and code type. + * + * @returns a pair of OTPs, the current one and the next one, using the given + * {@link code}. + */ +export const generateOTPs = (code: Code): [otp: string, nextOTP: string] => { + let otp: string; + let nextOTP: string; + switch (code.type) { + case "totp": { + const totp = new TOTP({ + secret: code.secret, + algorithm: code.algorithm, + period: code.period, + digits: code.digits, + }); + otp = totp.generate(); + nextOTP = totp.generate({ + timestamp: new Date().getTime() + code.period * 1000, + }); + break; + } + + case "hotp": { + const hotp = new HOTP({ + secret: code.secret, + counter: 0, + algorithm: code.algorithm, + }); + otp = hotp.generate(); + nextOTP = hotp.generate({ counter: 1 }); + break; + } + } + return [otp, nextOTP]; +};