From 4fade93b431eb16957c445b47bcbddbb68fb791a Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sun, 19 Mar 2023 15:51:04 +0530 Subject: [PATCH 01/38] Fix compilation --- src/utils/strings/frenchConstants.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/strings/frenchConstants.tsx b/src/utils/strings/frenchConstants.tsx index e1f236cc7..a073dd56f 100644 --- a/src/utils/strings/frenchConstants.tsx +++ b/src/utils/strings/frenchConstants.tsx @@ -553,7 +553,7 @@ const frenchConstants = { LOCAL_STORAGE_NOT_ACCESSIBLE_MESSAGE: 'Votre navigateur ou un complément bloque ente qui ne peut sauvegarder les données sur votre stockage local. Veuillez relancer cette page après avoir changé de mode de navigation.', RETRY: 'Réessayer', - SEND_OTT: 'Envoyer l'OTP', + SEND_OTT: "Envoyer l'OTP", EMAIl_ALREADY_OWNED: 'Cet e-mail est déjà pris', EMAIL_UDPATE_SUCCESSFUL: 'Votre e-mail a été mis à jour', UPLOAD_FAILED: 'Échec du chargement', From 6da5198c749f9d1679be6eda0589cd3187c090eb Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sun, 19 Mar 2023 15:54:23 +0530 Subject: [PATCH 02/38] Add TimerProgress componenet --- src/components/Authenicator/TimerProgress.tsx | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/components/Authenicator/TimerProgress.tsx diff --git a/src/components/Authenicator/TimerProgress.tsx b/src/components/Authenicator/TimerProgress.tsx new file mode 100644 index 000000000..c9234f4ac --- /dev/null +++ b/src/components/Authenicator/TimerProgress.tsx @@ -0,0 +1,46 @@ +import React, { useState, useEffect } from 'react'; + +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 ( +
+
+
+ ); +}; + +export default TimerProgress; From 54ee00574a6c38fe1a2318f80cf69bfade6077f3 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sun, 19 Mar 2023 15:59:20 +0530 Subject: [PATCH 03/38] Add OTPDisplay component --- src/components/Authenicator/OTPDisplay.tsx | 145 +++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 src/components/Authenicator/OTPDisplay.tsx diff --git a/src/components/Authenicator/OTPDisplay.tsx b/src/components/Authenicator/OTPDisplay.tsx new file mode 100644 index 000000000..b0c1faec3 --- /dev/null +++ b/src/components/Authenicator/OTPDisplay.tsx @@ -0,0 +1,145 @@ +import React, { useState, useEffect } from 'react'; +import { TOTP, HOTP } from 'otpauth'; +import TimerProgress from './TimerProgress'; + +const TOTPDisplay = ({ issuer, account, code, nextCode }) => { + return ( +
+
+

+ {issuer} +

+

+ {account} +

+

+ {code} +

+
+
+
+

+ next +

+

+ {nextCode} +

+
+
+ ); +}; + +const OTPDisplay = ({ + secret, + type, + algorithm, + timePeriod, + issuer, + account, +}) => { + const [code, setCode] = useState(''); + const [nextcode, setNextCode] = useState(''); + + const generateCodes = () => { + const currentTime = new Date().getTime(); + if (type.toLowerCase() === 'totp') { + const totp = new TOTP({ + secret, + algorithm, + period: timePeriod ?? 30, + }); + setCode(totp.generate()); + setNextCode( + totp.generate({ timestamp: currentTime + timePeriod * 1000 }) + ); + } else if (type.toLowerCase() === 'hotp') { + const hotp = new HOTP({ secret, counter: 0, algorithm }); + setCode(hotp.generate()); + setNextCode(hotp.generate({ counter: 1 })); + } + }; + + useEffect(() => { + let intervalId; + // compare case insensitive type + + if (type.toLowerCase() === 'totp') { + intervalId = setInterval(() => { + generateCodes(); + }, 1000); + } else if (type.toLowerCase() === 'hotp') { + intervalId = setInterval(() => { + generateCodes(); + }, 1000); + } + + return () => clearInterval(intervalId); + }, [secret, type, algorithm, timePeriod]); + + return ( +
+ + +
+ ); +}; + +export default OTPDisplay; From 1abfd2ab2b4570eb87beadffcbfada82ba2c9fdf Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sun, 19 Mar 2023 16:00:00 +0530 Subject: [PATCH 04/38] Add dependency on otpauth and vs-uri --- package.json | 2 ++ yarn.lock | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 415452280..0065d48c7 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "ml-matrix": "^6.8.2", "next": "^13.1.2", "next-transpile-modules": "^10.0.0", + "otpauth": "^9.0.2", "p-queue": "^7.1.0", "photoswipe": "file:./thirdparty/photoswipe", "piexifjs": "^1.0.6", @@ -72,6 +73,7 @@ "similarity-transformation": "^0.0.1", "styled-components": "^5.3.5", "transformation-matrix": "^2.10.0", + "vscode-uri": "^3.0.7", "workbox-precaching": "^6.1.5", "workbox-recipes": "^6.1.5", "workbox-routing": "^6.1.5", diff --git a/yarn.lock b/yarn.lock index e23f97854..35ce5a83d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3007,6 +3007,11 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +jssha@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/jssha/-/jssha-3.3.0.tgz#44b5531bcf55a12f4a388476c647a9a1cca92839" + integrity sha512-w9OtT4ALL+fbbwG3gw7erAO0jvS5nfvrukGPMWIAoea359B26ALXGpzy4YJSp9yGnpUvuvOw1nSjSoHDfWSr1w== + "jsx-ast-utils@^2.4.1 || ^3.0.0": version "3.2.0" resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz" @@ -3516,6 +3521,13 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +otpauth@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/otpauth/-/otpauth-9.0.2.tgz#5f369bdeb74513fb6c0f25c5ae5ac6b3780ebc89" + integrity sha512-0TzpkJYg24VvIK3/K91HKpTtMlwm73UoThhcGY8fZsXcwHDrqf008rfdOjj3NnQuyuT11+vHyyO//qRzi6OZ9A== + dependencies: + jssha "~3.3.0" + p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" @@ -4426,7 +4438,6 @@ tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== - text-table@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" @@ -4607,6 +4618,11 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +vscode-uri@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.7.tgz#6d19fef387ee6b46c479e5fb00870e15e58c1eb8" + integrity sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA== + warning@^4.0.0, warning@^4.0.2, warning@^4.0.3: version "4.0.3" resolved "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz" From 50e375ebdf8173a9b31cb8c517c6d6b1a05ae3d0 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sun, 19 Mar 2023 16:05:10 +0530 Subject: [PATCH 05/38] Add ente Authenticator page with dummy data --- src/components/Sidebar/UtilitySection.tsx | 7 ++ src/constants/pages/index.ts | 1 + src/pages/authenticator/index.tsx | 130 ++++++++++++++++++++++ src/utils/strings/englishConstants.tsx | 1 + 4 files changed, 139 insertions(+) create mode 100644 src/pages/authenticator/index.tsx diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx index 44f6f1031..f797b92cb 100644 --- a/src/components/Sidebar/UtilitySection.tsx +++ b/src/components/Sidebar/UtilitySection.tsx @@ -62,6 +62,8 @@ export default function UtilitySection({ closeSidebar }) { const redirectToDeduplicatePage = () => router.push(PAGES.DEDUPLICATE); + const redirectToAuthenticatorPage = () => router.push(PAGES.AUTHENICATOR); + const somethingWentWrong = () => setDialogMessage({ title: constants.ERROR, @@ -97,6 +99,11 @@ export default function UtilitySection({ closeSidebar }) { {constants.DEDUPLICATE_FILES} + {isInternalUser() && ( + + {constants.AUTHENTICATOR_SECTION} + + )} {constants.PREFERENCES} diff --git a/src/constants/pages/index.ts b/src/constants/pages/index.ts index e8612b3fd..38f2b3a6a 100644 --- a/src/constants/pages/index.ts +++ b/src/constants/pages/index.ts @@ -14,4 +14,5 @@ export enum PAGES { SHARED_ALBUMS = '/shared-albums', // ML_DEBUG = '/ml-debug', DEDUPLICATE = '/deduplicate', + AUTHENICATOR = '/authenticator', } diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx new file mode 100644 index 000000000..40421483a --- /dev/null +++ b/src/pages/authenticator/index.tsx @@ -0,0 +1,130 @@ +import React, { useEffect, useState } from 'react'; +import OTPDisplay from 'components/Authenicator/OTPDisplay'; +const random = [ + { + issuer: 'Google', + account: 'example@gmail.com', + secret: '6GJ2E2RQKJ36BY6A', + type: 'TOTP', + algorithm: 'SHA1', + period: 30, + }, + { + issuer: 'Facebook', + account: 'example@gmail.com', + secret: 'RVZJ7N6KJKJGQ2VX', + type: 'TOTP', + algorithm: 'SHA256', + period: 60, + }, + { + issuer: 'Twitter', + account: 'example@gmail.com', + secret: 'ZPUE6KJ3WGZ3HPKJ', + type: 'TOTP', + algorithm: 'SHA256', + period: 60, + }, + { + issuer: 'GitHub', + account: 'example@gmail.com', + secret: 'AG6U5KJYHPRRNRZI', + type: 'TOTP', + algorithm: 'SHA1', + period: 30, + }, + { + issuer: 'Amazon', + account: 'example@gmail.com', + secret: 'Q2FR2KJVKJFFKMWZ', + type: 'TOTP', + algorithm: 'SHA256', + period: 60, + }, + { + issuer: 'LinkedIn', + account: 'example@gmail.com', + secret: 'SWRG4KJ4J3LNDW2Z', + type: 'TOTP', + algorithm: 'SHA256', + period: 60, + }, + { + issuer: 'Dropbox', + account: 'example@gmail.com', + secret: 'G5U6OKJU3JRM72ZK', + type: 'TOTP', + algorithm: 'SHA1', + period: 30, + }, +]; + +const OTPPage = () => { + const [secrets, setSecrets] = useState([]); + const [searchTerm, setSearchTerm] = useState(''); + + useEffect(() => { + const fetchSecrets = async () => { + try { + setSecrets(random); + } catch (error) { + console.error(error); + } + }; + fetchSecrets(); + }, []); + + const filteredSecrets = secrets.filter( + (secret) => + secret.issuer.toLowerCase().includes(searchTerm.toLowerCase()) || + secret.account.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + return ( + // center the page +
+

ente Authenticator

+ setSearchTerm(e.target.value)} + /> + +
+ {filteredSecrets.length === 0 ? ( +
+

No results found.

+ {/*

Add a new secret to get started.

+

Download ente auth mobile app to manage your secrets

*/} +
+ ) : ( + filteredSecrets.map((secret) => ( + + )) + )} +
+ ); +}; + +export default OTPPage; diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index a59c44932..7d1c99965 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -800,6 +800,7 @@ const englishConstants = { UPLOAD_GOOGLE_TAKEOUT: 'Google takeout', CANCEL_UPLOADS: 'Cancel uploads', DEDUPLICATE_FILES: 'Deduplicate files', + AUTHENTICATOR_SECTION: 'Authenticator', NO_DUPLICATES_FOUND: "You've no duplicate files that can be cleared", CLUB_BY_CAPTURE_TIME: 'Club by capture time', FILES: 'Files', From 4faa05a465afff422ff2731ac95da7466ee7cf43 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 20 Mar 2023 09:09:20 +0530 Subject: [PATCH 06/38] [ente Authenticator] Add types --- src/types/authenticator/auth_entity.ts | 23 ++++ src/types/authenticator/code.ts | 166 +++++++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 src/types/authenticator/auth_entity.ts create mode 100644 src/types/authenticator/code.ts diff --git a/src/types/authenticator/auth_entity.ts b/src/types/authenticator/auth_entity.ts new file mode 100644 index 000000000..083651d31 --- /dev/null +++ b/src/types/authenticator/auth_entity.ts @@ -0,0 +1,23 @@ +export class AuthEntity { + id: string; + encryptedData: string | null; + header: string | null; + isDeleted: boolean; + createdAt: number; + updatedAt: number; + constructor( + id: string, + encryptedData: string | null, + header: string | null, + isDeleted: boolean, + createdAt: number, + updatedAt: number + ) { + this.id = id; + this.encryptedData = encryptedData; + this.header = header; + this.isDeleted = isDeleted; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } +} diff --git a/src/types/authenticator/code.ts b/src/types/authenticator/code.ts new file mode 100644 index 000000000..8e94d7b45 --- /dev/null +++ b/src/types/authenticator/code.ts @@ -0,0 +1,166 @@ +import { URI } from 'vscode-uri'; + +type Type = 'totp' | 'TOTP' | 'hotp' | 'HOTP'; + +type AlgorithmType = + | 'sha1' + | 'SHA1' + | 'sha256' + | 'SHA256' + | 'sha512' + | 'SHA512'; + +export class Code { + static readonly defaultDigits = 6; + static readonly defaultPeriod = 30; + + // id for the corresponding auth entity + id?: String; + account: string; + issuer: string; + digits?: number; + period: number; + secret: string; + algorithm: AlgorithmType; + type: Type; + rawData?: string; + + constructor( + account: string, + issuer: string, + digits: number | undefined, + period: number, + secret: string, + algorithm: AlgorithmType, + type: Type, + rawData?: string, + id?: string + ) { + this.account = account; + this.issuer = issuer; + this.digits = digits; + this.period = period; + this.secret = secret; + this.algorithm = algorithm; + this.type = type; + this.rawData = rawData; + this.id = id; + } + + static fromRawData(id: string, rawData: string): Code { + let santizedRawData = rawData + .replace(/\+/g, '%2B') + .replace(/:/g, '%3A'); + if (santizedRawData.startsWith('"')) { + santizedRawData = santizedRawData.substring(1); + } + if (santizedRawData.endsWith('"')) { + santizedRawData = santizedRawData.substring( + 0, + santizedRawData.length - 1 + ); + } + + const uriParams = {}; + const searchParamsString = + decodeURIComponent(santizedRawData).split('?')[1]; + searchParamsString.split('&').forEach((pair) => { + const [key, value] = pair.split('='); + uriParams[key] = value; + }); + + const uri = URI.parse(santizedRawData); + let uriPath = uri.path; + if ( + uriPath.startsWith('/otpauth://') || + uriPath.startsWith('otpauth://') + ) { + uriPath = uriPath.split('otpauth://')[1]; + } + + return new Code( + Code._getAccount(uriPath), + Code._getIssuer(uriPath, uriParams), + Code._getDigits(uriParams), + Code._getPeriod(uriParams), + Code.getSanitizedSecret(uriParams), + Code._getAlgorithm(uriParams), + Code._getType(uriPath), + rawData, + id + ); + } + + private static _getAccount(uriPath: string): string { + try { + const path = decodeURIComponent(uriPath); + return path.split(':')[1]; + } catch (e) { + return ''; + } + } + + private static _getIssuer( + uriPath: string, + uriParams: { get?: any } + ): string { + try { + if (uriParams['issuer'] !== undefined) { + let issuer = uriParams['issuer']; + // This is to handle bug in the ente auth app + if (issuer.endsWith('period')) { + issuer = issuer.substring(0, issuer.length - 6); + } + return issuer; + } + const path = decodeURIComponent(uriPath); + return path.split(':')[0].substring(1); + } catch (e) { + return ''; + } + } + + private static _getDigits(uriParams): number { + try { + return parseInt(uriParams['digits'], 10); + } catch (e) { + return Code.defaultDigits; + } + } + + private static _getPeriod(uriParams): number { + try { + return parseInt(uriParams['period'], 10) || Code.defaultPeriod; + } catch (e) { + return Code.defaultPeriod; + } + } + + private static _getAlgorithm(uriParams): AlgorithmType { + try { + const algorithm = uriParams['algorithm'].toLowerCase(); + if (algorithm === 'sha256') { + return algorithm; + } else if (algorithm === 'sha512') { + return algorithm; + } + } catch (e) { + // nothing + } + return 'sha1'; + } + + private static _getType(uriPath: string): Type { + const oauthType = uriPath.split('/')[0].substring(0); + if (oauthType === 'totp') { + return 'totp'; + } else if (oauthType === 'hotp') { + return 'hotp'; + } + throw new Error(`Unsupported format with host ${oauthType}`); + } + + static getSanitizedSecret(uriParams): string { + return uriParams['secret'].replace(/ /g, '').toUpperCase(); + } +} From 1454ac6bf2f4acd06300f7bef64e71a1829a364f Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 20 Mar 2023 09:26:04 +0530 Subject: [PATCH 07/38] Add authenticator service to fetch and decrypt codes --- .../authenticator/authenticatorService.ts | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/services/authenticator/authenticatorService.ts diff --git a/src/services/authenticator/authenticatorService.ts b/src/services/authenticator/authenticatorService.ts new file mode 100644 index 000000000..ebe80149e --- /dev/null +++ b/src/services/authenticator/authenticatorService.ts @@ -0,0 +1,91 @@ +import HTTPService from 'services/HTTPService'; +import { AuthEntity } from 'types/authenticator/auth_entity'; +import { Code } from 'types/authenticator/code'; +import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker'; +import { getEndpoint } from 'utils/common/apiUtil'; +import { getActualKey, getToken } from 'utils/common/key'; +import { logError } from 'utils/sentry'; + +const ENDPOINT = getEndpoint(); +export const getAuthCodes = async (): Promise => { + const masterKey = await getActualKey(); + try { + const authKeyData = await getAuthKey(); + const cryptoWorker = await ComlinkCryptoWorker.getInstance(); + const authentitorKey = await cryptoWorker.decryptB64( + authKeyData.encryptedKey, + authKeyData.header, + masterKey + ); + // always fetch all data from server for now + const authEntity: AuthEntity[] = await getDiff(0); + const authCodes = await Promise.all( + authEntity + .filter((f) => !f.isDeleted) + .map(async (entity) => { + const decryptedCode = await cryptoWorker.decryptMetadata( + entity.encryptedData, + entity.header, + authentitorKey + ); + return Code.fromRawData(entity.id, decryptedCode); + }) + ); + // sort by issuer name which can be undefined also + authCodes.sort((a, b) => { + if (a.issuer && b.issuer) { + return a.issuer.localeCompare(b.issuer); + } + if (a.issuer) { + return -1; + } + if (b.issuer) { + return 1; + } + return 0; + }); + return authCodes; + } catch (e) { + logError(e, 'get authenticator entities failed'); + throw e; + } +}; + +export const getAuthKey = async () => { + try { + const resp = await HTTPService.get( + `${ENDPOINT}/authenticator/key`, + {}, + { + 'X-Auth-Token': getToken(), + } + ); + return resp.data; + } catch (e) { + logError(e, 'Get key failed'); + throw e; + } +}; + +// return a promise which resolves to list of AuthEnitity +export const getDiff = async ( + sinceTime: number, + limit = 2500 +): Promise => { + try { + const resp = await HTTPService.get( + `${ENDPOINT}/authenticator/entity/diff`, + { + sinceTime, + limit, + }, + { + 'X-Auth-Token': getToken(), + } + ); + return resp.data.diff; + } catch (e) { + logError(e, 'Get diff failed'); + throw e; + } +}; From 208e6c07fe80a54b61917b7ae4a609b49e2762fc Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 20 Mar 2023 09:26:29 +0530 Subject: [PATCH 08/38] Refactor --- src/components/Authenicator/OTPDisplay.tsx | 50 ++++++++++++---------- src/components/Sidebar/UtilitySection.tsx | 2 +- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/components/Authenicator/OTPDisplay.tsx b/src/components/Authenicator/OTPDisplay.tsx index b0c1faec3..9be78988c 100644 --- a/src/components/Authenicator/OTPDisplay.tsx +++ b/src/components/Authenicator/OTPDisplay.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect } from 'react'; import { TOTP, HOTP } from 'otpauth'; +import { Code } from 'types/authenticator/code'; import TimerProgress from './TimerProgress'; const TOTPDisplay = ({ issuer, account, code, nextCode }) => { @@ -82,59 +83,64 @@ const TOTPDisplay = ({ issuer, account, code, nextCode }) => { ); }; -const OTPDisplay = ({ - secret, - type, - algorithm, - timePeriod, - issuer, - account, -}) => { +interface OTPDisplayProps { + codeInfo: Code; +} + +const OTPDisplay = (props: OTPDisplayProps) => { + const { codeInfo } = props; const [code, setCode] = useState(''); const [nextcode, setNextCode] = useState(''); const generateCodes = () => { const currentTime = new Date().getTime(); - if (type.toLowerCase() === 'totp') { + if (codeInfo.type.toLowerCase() === 'totp') { const totp = new TOTP({ - secret, - algorithm, - period: timePeriod ?? 30, + secret: codeInfo.secret, + algorithm: codeInfo.algorithm, + period: codeInfo.period ?? Code.defaultPeriod, + digits: codeInfo.digits, }); setCode(totp.generate()); setNextCode( - totp.generate({ timestamp: currentTime + timePeriod * 1000 }) + totp.generate({ + timestamp: currentTime + codeInfo.period * 1000, + }) ); - } else if (type.toLowerCase() === 'hotp') { - const hotp = new HOTP({ secret, counter: 0, algorithm }); + } 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 })); } }; useEffect(() => { + generateCodes(); let intervalId; - // compare case insensitive type - if (type.toLowerCase() === 'totp') { + if (codeInfo.type.toLowerCase() === 'totp') { intervalId = setInterval(() => { generateCodes(); }, 1000); - } else if (type.toLowerCase() === 'hotp') { + } else if (codeInfo.type.toLowerCase() === 'hotp') { intervalId = setInterval(() => { generateCodes(); }, 1000); } return () => clearInterval(intervalId); - }, [secret, type, algorithm, timePeriod]); + }, [codeInfo]); return (
- + diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx index f797b92cb..5d1ff9142 100644 --- a/src/components/Sidebar/UtilitySection.tsx +++ b/src/components/Sidebar/UtilitySection.tsx @@ -99,7 +99,7 @@ export default function UtilitySection({ closeSidebar }) { {constants.DEDUPLICATE_FILES} - {isInternalUser() && ( + {!isInternalUser() && ( {constants.AUTHENTICATOR_SECTION} From 5618e65fe10e9057eb7cf5d257dcb78be2d503db Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 20 Mar 2023 09:31:12 +0530 Subject: [PATCH 09/38] [ente Authenticator] Remove hardcoded codes --- src/pages/authenticator/index.tsx | 93 ++++++------------------------- 1 file changed, 17 insertions(+), 76 deletions(-) diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx index 40421483a..05022424a 100644 --- a/src/pages/authenticator/index.tsx +++ b/src/pages/authenticator/index.tsx @@ -1,83 +1,32 @@ import React, { useEffect, useState } from 'react'; import OTPDisplay from 'components/Authenicator/OTPDisplay'; -const random = [ - { - issuer: 'Google', - account: 'example@gmail.com', - secret: '6GJ2E2RQKJ36BY6A', - type: 'TOTP', - algorithm: 'SHA1', - period: 30, - }, - { - issuer: 'Facebook', - account: 'example@gmail.com', - secret: 'RVZJ7N6KJKJGQ2VX', - type: 'TOTP', - algorithm: 'SHA256', - period: 60, - }, - { - issuer: 'Twitter', - account: 'example@gmail.com', - secret: 'ZPUE6KJ3WGZ3HPKJ', - type: 'TOTP', - algorithm: 'SHA256', - period: 60, - }, - { - issuer: 'GitHub', - account: 'example@gmail.com', - secret: 'AG6U5KJYHPRRNRZI', - type: 'TOTP', - algorithm: 'SHA1', - period: 30, - }, - { - issuer: 'Amazon', - account: 'example@gmail.com', - secret: 'Q2FR2KJVKJFFKMWZ', - type: 'TOTP', - algorithm: 'SHA256', - period: 60, - }, - { - issuer: 'LinkedIn', - account: 'example@gmail.com', - secret: 'SWRG4KJ4J3LNDW2Z', - type: 'TOTP', - algorithm: 'SHA256', - period: 60, - }, - { - issuer: 'Dropbox', - account: 'example@gmail.com', - secret: 'G5U6OKJU3JRM72ZK', - type: 'TOTP', - algorithm: 'SHA1', - period: 30, - }, -]; +import { getAuthCodes } from 'services/authenticator/authenticatorService'; const OTPPage = () => { - const [secrets, setSecrets] = useState([]); + const [codes, setCodes] = useState([]); const [searchTerm, setSearchTerm] = useState(''); useEffect(() => { - const fetchSecrets = async () => { + const fetchCodes = async () => { try { - setSecrets(random); + getAuthCodes().then((res) => { + setCodes(res); + }); } catch (error) { console.error(error); } }; - fetchSecrets(); + fetchCodes(); }, []); - const filteredSecrets = secrets.filter( + const filteredCodes = codes.filter( (secret) => - secret.issuer.toLowerCase().includes(searchTerm.toLowerCase()) || - secret.account.toLowerCase().includes(searchTerm.toLowerCase()) + (secret.issuer ?? '') + .toLowerCase() + .includes(searchTerm.toLowerCase()) || + (secret.account ?? '') + .toLowerCase() + .includes(searchTerm.toLowerCase()) ); return ( @@ -98,7 +47,7 @@ const OTPPage = () => { />
- {filteredSecrets.length === 0 ? ( + {filteredCodes.length === 0 ? (
{

Download ente auth mobile app to manage your secrets

*/}
) : ( - filteredSecrets.map((secret) => ( - + filteredCodes.map((code) => ( + )) )}
From 937d114f7c220b8a75ad3f4adc71bf3f79952b2d Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 20 Mar 2023 10:23:14 +0530 Subject: [PATCH 10/38] Add hook to download ente auth app --- src/pages/authenticator/index.tsx | 37 +++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx index 05022424a..dbb6cee99 100644 --- a/src/pages/authenticator/index.tsx +++ b/src/pages/authenticator/index.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from 'react'; import OTPDisplay from 'components/Authenicator/OTPDisplay'; import { getAuthCodes } from 'services/authenticator/authenticatorService'; +import { Button } from '@mui/material'; const OTPPage = () => { const [codes, setCodes] = useState([]); @@ -29,8 +30,31 @@ const OTPPage = () => { .includes(searchTerm.toLowerCase()) ); + const DownloadApp = () => { + return ( +
+

Download our mobile app to add & manage your secrets.

+ + + +
+ ); + }; + return ( - // center the page
{ textAlign: 'center', marginTop: '32px', }}> -

No results found.

- {/*

Add a new secret to get started.

-

Download ente auth mobile app to manage your secrets

*/} + {searchTerm.length !== 0 ? ( +

No results found.

+ ) : ( +
+ )}
) : ( filteredCodes.map((code) => ( )) )} +
+ +
); }; From aaa3a273c0c5d7c1494101a886799d33e0fb5f50 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 20 Mar 2023 10:26:40 +0530 Subject: [PATCH 11/38] Show authenticator section to internal users only --- src/components/Sidebar/UtilitySection.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx index 5d1ff9142..f797b92cb 100644 --- a/src/components/Sidebar/UtilitySection.tsx +++ b/src/components/Sidebar/UtilitySection.tsx @@ -99,7 +99,7 @@ export default function UtilitySection({ closeSidebar }) { {constants.DEDUPLICATE_FILES} - {!isInternalUser() && ( + {isInternalUser() && ( {constants.AUTHENTICATOR_SECTION} From 7c8739a57b351ef12ee660768b19a5123d469bcf Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 20 Mar 2023 10:43:17 +0530 Subject: [PATCH 12/38] [ente auth] redirect to root if key is missing --- src/pages/authenticator/index.tsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx index dbb6cee99..c5f5fab0b 100644 --- a/src/pages/authenticator/index.tsx +++ b/src/pages/authenticator/index.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'; import OTPDisplay from 'components/Authenicator/OTPDisplay'; import { getAuthCodes } from 'services/authenticator/authenticatorService'; import { Button } from '@mui/material'; +import { CustomError } from 'utils/error'; const OTPPage = () => { const [codes, setCodes] = useState([]); @@ -10,10 +11,20 @@ const OTPPage = () => { useEffect(() => { const fetchCodes = async () => { try { - getAuthCodes().then((res) => { - setCodes(res); - }); + getAuthCodes() + .then((res) => { + setCodes(res); + }) + .catch((err) => { + if (err.message === CustomError.KEY_MISSING) { + window.location.href = '/'; + return; + } + + console.error('something wrong here', err); + }); } catch (error) { + console.error('something wrong where asdas'); console.error(error); } }; From e8d8ffacc995287c6348773e831d406c8abbc4b7 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 20 Mar 2023 11:00:33 +0530 Subject: [PATCH 13/38] Fix routing on redirect to creds page --- src/pages/authenticator/index.tsx | 9 +++++++-- src/pages/credentials/index.tsx | 4 +++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx index c5f5fab0b..967be144b 100644 --- a/src/pages/authenticator/index.tsx +++ b/src/pages/authenticator/index.tsx @@ -3,8 +3,11 @@ import OTPDisplay from 'components/Authenicator/OTPDisplay'; import { getAuthCodes } from 'services/authenticator/authenticatorService'; import { Button } from '@mui/material'; import { CustomError } from 'utils/error'; +import { PAGES } from 'constants/pages'; +import { useRouter } from 'next/router'; const OTPPage = () => { + const router = useRouter(); const [codes, setCodes] = useState([]); const [searchTerm, setSearchTerm] = useState(''); @@ -17,10 +20,12 @@ const OTPPage = () => { }) .catch((err) => { if (err.message === CustomError.KEY_MISSING) { - window.location.href = '/'; + router.push({ + pathname: PAGES.CREDENTIALS, + query: { redirectPage: PAGES.AUTHENICATOR }, + }); return; } - console.error('something wrong here', err); }); } catch (error) { diff --git a/src/pages/credentials/index.tsx b/src/pages/credentials/index.tsx index 02c54f389..2e3359a83 100644 --- a/src/pages/credentials/index.tsx +++ b/src/pages/credentials/index.tsx @@ -30,6 +30,8 @@ import VerifyMasterPasswordForm, { export default function Credentials() { const router = useRouter(); + const routeReidrectPage = + router.query.redirectPage?.toString() ?? PAGES.GALLERY; const [keyAttributes, setKeyAttributes] = useState(); const appContext = useContext(AppContext); const [user, setUser] = useState(); @@ -85,7 +87,7 @@ export default function Credentials() { await decryptAndStoreToken(key); const redirectURL = appContext.redirectURL; appContext.setRedirectURL(null); - router.push(redirectURL ?? PAGES.GALLERY); + router.push(redirectURL ?? routeReidrectPage ?? PAGES.GALLERY); } catch (e) { logError(e, 'useMasterPassword failed'); } From 02e2de1ef588733eb2c743339113f9418aa3b43a Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 20 Mar 2023 11:04:50 +0530 Subject: [PATCH 14/38] Hide search box when there's no entry --- src/pages/authenticator/index.tsx | 17 +++++++++++------ .../authenticator/authenticatorService.ts | 3 +++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx index 967be144b..f40cf5ef1 100644 --- a/src/pages/authenticator/index.tsx +++ b/src/pages/authenticator/index.tsx @@ -78,13 +78,18 @@ const OTPPage = () => { alignItems: 'center', justifyContent: 'flex-start', }}> +

ente Authenticator

- setSearchTerm(e.target.value)} - /> + {filteredCodes.length === 0 && searchTerm.length === 0 ? ( + <> + ) : ( + setSearchTerm(e.target.value)} + /> + )}
{filteredCodes.length === 0 ? ( diff --git a/src/services/authenticator/authenticatorService.ts b/src/services/authenticator/authenticatorService.ts index ebe80149e..8027661bd 100644 --- a/src/services/authenticator/authenticatorService.ts +++ b/src/services/authenticator/authenticatorService.ts @@ -44,6 +44,9 @@ export const getAuthCodes = async (): Promise => { } return 0; }); + // remove all entries from authCodes + authCodes.splice(0, authCodes.length); + return authCodes; } catch (e) { logError(e, 'get authenticator entities failed'); From 6d094b3bcad17487a02f03b24e38998fc7001e81 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 20 Mar 2023 11:05:57 +0530 Subject: [PATCH 15/38] Undo test changes --- src/services/authenticator/authenticatorService.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/services/authenticator/authenticatorService.ts b/src/services/authenticator/authenticatorService.ts index 8027661bd..ebe80149e 100644 --- a/src/services/authenticator/authenticatorService.ts +++ b/src/services/authenticator/authenticatorService.ts @@ -44,9 +44,6 @@ export const getAuthCodes = async (): Promise => { } return 0; }); - // remove all entries from authCodes - authCodes.splice(0, authCodes.length); - return authCodes; } catch (e) { logError(e, 'get authenticator entities failed'); From 134cd8343bc83204c53e03e3a64c86b4487e9569 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 20 Mar 2023 11:58:39 +0530 Subject: [PATCH 16/38] Handle error in generating code --- src/components/Authenicator/OTPDisplay.tsx | 77 ++++++++++++------- .../authenticator/authenticatorService.ts | 13 +++- 2 files changed, 61 insertions(+), 29 deletions(-) diff --git a/src/components/Authenicator/OTPDisplay.tsx b/src/components/Authenicator/OTPDisplay.tsx index 9be78988c..f476dc4ff 100644 --- a/src/components/Authenicator/OTPDisplay.tsx +++ b/src/components/Authenicator/OTPDisplay.tsx @@ -91,30 +91,35 @@ const OTPDisplay = (props: OTPDisplayProps) => { const { codeInfo } = props; const [code, setCode] = useState(''); const [nextcode, setNextCode] = useState(''); + const [codeErr, setCodeErr] = useState(''); const generateCodes = () => { - const currentTime = new Date().getTime(); - if (codeInfo.type.toLowerCase() === 'totp') { - const totp = new TOTP({ - secret: codeInfo.secret, - algorithm: codeInfo.algorithm, - period: codeInfo.period ?? Code.defaultPeriod, - digits: codeInfo.digits, - }); - 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 })); + try { + const currentTime = new Date().getTime(); + if (codeInfo.type.toLowerCase() === 'totp') { + const totp = new TOTP({ + secret: codeInfo.secret, + algorithm: codeInfo.algorithm, + period: codeInfo.period ?? Code.defaultPeriod, + digits: codeInfo.digits, + }); + 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); } }; @@ -138,12 +143,28 @@ const OTPDisplay = (props: OTPDisplayProps) => { return (
- + {codeErr === '' ? ( + + ) : ( +
+

{codeErr}

+

{codeInfo.rawData ?? 'no rawdata'}

+
+ )}
); }; diff --git a/src/services/authenticator/authenticatorService.ts b/src/services/authenticator/authenticatorService.ts index ebe80149e..4f64b0811 100644 --- a/src/services/authenticator/authenticatorService.ts +++ b/src/services/authenticator/authenticatorService.ts @@ -28,8 +28,19 @@ export const getAuthCodes = async (): Promise => { entity.header, authentitorKey ); - return Code.fromRawData(entity.id, decryptedCode); + try { + return Code.fromRawData(entity.id, decryptedCode); + } catch (e) { + console.log( + 'failed to parse code', + e, + entity.id, + decryptedCode + ); + return null; + } }) + .filter((f) => f !== null || f !== undefined) ); // sort by issuer name which can be undefined also authCodes.sort((a, b) => { From 6cb022dede77c1e000565dc494b4a2c4c6089a9d Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 21 Mar 2023 14:11:37 +0530 Subject: [PATCH 17/38] Fix code generation --- src/components/Authenicator/OTPDisplay.tsx | 4 ++-- src/types/authenticator/code.ts | 13 ++++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/components/Authenicator/OTPDisplay.tsx b/src/components/Authenicator/OTPDisplay.tsx index f476dc4ff..7a851aeca 100644 --- a/src/components/Authenicator/OTPDisplay.tsx +++ b/src/components/Authenicator/OTPDisplay.tsx @@ -99,9 +99,9 @@ const OTPDisplay = (props: OTPDisplayProps) => { if (codeInfo.type.toLowerCase() === 'totp') { const totp = new TOTP({ secret: codeInfo.secret, - algorithm: codeInfo.algorithm, + algorithm: codeInfo.algorithm ?? Code.defaultAlgo, period: codeInfo.period ?? Code.defaultPeriod, - digits: codeInfo.digits, + digits: codeInfo.digits ?? Code.defaultDigits, }); setCode(totp.generate()); setNextCode( diff --git a/src/types/authenticator/code.ts b/src/types/authenticator/code.ts index 8e94d7b45..cc244a4c4 100644 --- a/src/types/authenticator/code.ts +++ b/src/types/authenticator/code.ts @@ -12,6 +12,7 @@ type AlgorithmType = export class Code { static readonly defaultDigits = 6; + static readonly defaultAlgo = 'sha1'; static readonly defaultPeriod = 30; // id for the corresponding auth entity @@ -70,12 +71,14 @@ export class Code { }); const uri = URI.parse(santizedRawData); - let uriPath = uri.path; + let uriPath = decodeURIComponent(uri.path); if ( uriPath.startsWith('/otpauth://') || uriPath.startsWith('otpauth://') ) { uriPath = uriPath.split('otpauth://')[1]; + } else if (uriPath.startsWith('otpauth%3A//')) { + uriPath = uriPath.split('otpauth%3A//')[1]; } return new Code( @@ -94,7 +97,11 @@ export class Code { private static _getAccount(uriPath: string): string { try { const path = decodeURIComponent(uriPath); - return path.split(':')[1]; + if (path.includes(':')) { + return path.split(':')[1]; + } else if (path.includes('/')) { + return path.split('/')[1]; + } } catch (e) { return ''; } @@ -122,7 +129,7 @@ export class Code { private static _getDigits(uriParams): number { try { - return parseInt(uriParams['digits'], 10); + return parseInt(uriParams['digits'], 10) || Code.defaultDigits; } catch (e) { return Code.defaultDigits; } From d2ebf79b47d50cbf6575a4b1cbc9316157eda717 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 21 Mar 2023 14:36:14 +0530 Subject: [PATCH 18/38] Remove logging for rawCode --- src/services/authenticator/authenticatorService.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/services/authenticator/authenticatorService.ts b/src/services/authenticator/authenticatorService.ts index 4f64b0811..cdf31f7ab 100644 --- a/src/services/authenticator/authenticatorService.ts +++ b/src/services/authenticator/authenticatorService.ts @@ -31,12 +31,7 @@ export const getAuthCodes = async (): Promise => { try { return Code.fromRawData(entity.id, decryptedCode); } catch (e) { - console.log( - 'failed to parse code', - e, - entity.id, - decryptedCode - ); + console.log('failed to parse code', e, entity.id); return null; } }) From 0746deff74bdf27a9cb2d98fabe95d306338beb1 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 21 Mar 2023 16:23:31 +0530 Subject: [PATCH 19/38] Log codeInfo --- src/components/Authenicator/OTPDisplay.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Authenicator/OTPDisplay.tsx b/src/components/Authenicator/OTPDisplay.tsx index 7a851aeca..bcaecb760 100644 --- a/src/components/Authenicator/OTPDisplay.tsx +++ b/src/components/Authenicator/OTPDisplay.tsx @@ -119,6 +119,7 @@ const OTPDisplay = (props: OTPDisplayProps) => { setNextCode(hotp.generate({ counter: 1 })); } } catch (err) { + console.log('codeInfo', codeInfo); setCodeErr(err.message); } }; From aaa301d09dba71aaf73ae23fcc1c3c4851b15b34 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Wed, 22 Mar 2023 12:44:45 +0530 Subject: [PATCH 20/38] [ente authenticator] Handle \r --- src/types/authenticator/code.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/types/authenticator/code.ts b/src/types/authenticator/code.ts index cc244a4c4..de32ad611 100644 --- a/src/types/authenticator/code.ts +++ b/src/types/authenticator/code.ts @@ -51,7 +51,8 @@ export class Code { static fromRawData(id: string, rawData: string): Code { let santizedRawData = rawData .replace(/\+/g, '%2B') - .replace(/:/g, '%3A'); + .replace(/:/g, '%3A') + .replaceAll('\r', ''); if (santizedRawData.startsWith('"')) { santizedRawData = santizedRawData.substring(1); } From 3a3b1337b328e95c7dc8b1016b15a14beec1b770 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Wed, 22 Mar 2023 13:01:47 +0530 Subject: [PATCH 21/38] [ente auth] improve parsing of issuer from path --- src/types/authenticator/code.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/types/authenticator/code.ts b/src/types/authenticator/code.ts index de32ad611..0e2ff8bba 100644 --- a/src/types/authenticator/code.ts +++ b/src/types/authenticator/code.ts @@ -121,8 +121,16 @@ export class Code { } return issuer; } - const path = decodeURIComponent(uriPath); - return path.split(':')[0].substring(1); + let path = decodeURIComponent(uriPath); + if (path.startsWith('totp/') || path.startsWith('hotp/')) { + path = path.substring(5); + } + if (path.includes(':')) { + return path.split(':')[0]; + } else if (path.includes('-')) { + return path.split('-')[0]; + } + return path; } catch (e) { return ''; } From 76710698cdb6c1eb229caed04f49ebd5f18f2baa Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Wed, 22 Mar 2023 13:10:52 +0530 Subject: [PATCH 22/38] [ente auth] Improve bad code display --- src/components/Authenicator/OTPDisplay.tsx | 35 +++++++++++++--------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/components/Authenicator/OTPDisplay.tsx b/src/components/Authenicator/OTPDisplay.tsx index bcaecb760..fd328041b 100644 --- a/src/components/Authenicator/OTPDisplay.tsx +++ b/src/components/Authenicator/OTPDisplay.tsx @@ -83,6 +83,26 @@ const TOTPDisplay = ({ issuer, account, code, 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; } @@ -119,7 +139,6 @@ const OTPDisplay = (props: OTPDisplayProps) => { setNextCode(hotp.generate({ counter: 1 })); } } catch (err) { - console.log('codeInfo', codeInfo); setCodeErr(err.message); } }; @@ -152,19 +171,7 @@ const OTPDisplay = (props: OTPDisplayProps) => { nextCode={nextcode} /> ) : ( -
-

{codeErr}

-

{codeInfo.rawData ?? 'no rawdata'}

-
+ )}
); From 6d9767db405d57f3f34871ce41a8a46e2e9aeb4d Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 25 Mar 2023 07:14:08 +0530 Subject: [PATCH 23/38] Remove logging --- src/pages/authenticator/index.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx index f40cf5ef1..6471d2263 100644 --- a/src/pages/authenticator/index.tsx +++ b/src/pages/authenticator/index.tsx @@ -26,11 +26,10 @@ const OTPPage = () => { }); return; } - console.error('something wrong here', err); + // do not log errors }); } catch (error) { - console.error('something wrong where asdas'); - console.error(error); + // do not log errors } }; fetchCodes(); From 4c190a319c22bf453f31be621eaa50aec79ccd01 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 25 Mar 2023 07:21:37 +0530 Subject: [PATCH 24/38] Remove exception from log --- src/services/authenticator/authenticatorService.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/services/authenticator/authenticatorService.ts b/src/services/authenticator/authenticatorService.ts index cdf31f7ab..8ef3df69b 100644 --- a/src/services/authenticator/authenticatorService.ts +++ b/src/services/authenticator/authenticatorService.ts @@ -31,7 +31,10 @@ export const getAuthCodes = async (): Promise => { try { return Code.fromRawData(entity.id, decryptedCode); } catch (e) { - console.log('failed to parse code', e, entity.id); + logError( + Error('failed to parse code'), + 'codeId = ' + entity.id + ); return null; } }) From c6da52b1f56dd0ecf850e72b83b6325a38d1eadf Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 25 Mar 2023 07:23:52 +0530 Subject: [PATCH 25/38] Switch to /auth instead of /authenticator --- src/components/Sidebar/UtilitySection.tsx | 2 +- src/constants/pages/index.ts | 3 ++- src/pages/authenticator/index.tsx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx index fe1db3702..99c199107 100644 --- a/src/components/Sidebar/UtilitySection.tsx +++ b/src/components/Sidebar/UtilitySection.tsx @@ -63,7 +63,7 @@ export default function UtilitySection({ closeSidebar }) { const redirectToDeduplicatePage = () => router.push(PAGES.DEDUPLICATE); - const redirectToAuthenticatorPage = () => router.push(PAGES.AUTHENICATOR); + const redirectToAuthenticatorPage = () => router.push(PAGES.AUTH); const somethingWentWrong = () => setDialogMessage({ diff --git a/src/constants/pages/index.ts b/src/constants/pages/index.ts index 38f2b3a6a..10594aae6 100644 --- a/src/constants/pages/index.ts +++ b/src/constants/pages/index.ts @@ -14,5 +14,6 @@ export enum PAGES { SHARED_ALBUMS = '/shared-albums', // ML_DEBUG = '/ml-debug', DEDUPLICATE = '/deduplicate', - AUTHENICATOR = '/authenticator', + // AUTH page is used to show (auth)enticator codes + AUTH = '/auth', } diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx index 6471d2263..13aed7dc5 100644 --- a/src/pages/authenticator/index.tsx +++ b/src/pages/authenticator/index.tsx @@ -22,7 +22,7 @@ const OTPPage = () => { if (err.message === CustomError.KEY_MISSING) { router.push({ pathname: PAGES.CREDENTIALS, - query: { redirectPage: PAGES.AUTHENICATOR }, + query: { redirectPage: PAGES.AUTH }, }); return; } From e63260b5ce4c2388a6e025ecd50779cf4a003c98 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 25 Mar 2023 07:25:07 +0530 Subject: [PATCH 26/38] Fix typo --- src/pages/credentials/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/credentials/index.tsx b/src/pages/credentials/index.tsx index e9422a8d8..7c0f2c651 100644 --- a/src/pages/credentials/index.tsx +++ b/src/pages/credentials/index.tsx @@ -31,7 +31,7 @@ import VerifyMasterPasswordForm, { export default function Credentials() { const router = useRouter(); - const routeReidrectPage = + const routerRedirectPage = router.query.redirectPage?.toString() ?? PAGES.GALLERY; const [keyAttributes, setKeyAttributes] = useState(); const appContext = useContext(AppContext); @@ -88,7 +88,7 @@ export default function Credentials() { await decryptAndStoreToken(key); const redirectURL = appContext.redirectURL; appContext.setRedirectURL(null); - router.push(redirectURL ?? routeReidrectPage ?? PAGES.GALLERY); + router.push(redirectURL ?? routerRedirectPage ?? PAGES.GALLERY); } catch (e) { logError(e, 'useMasterPassword failed'); } From dcaae1f4787fca75026a63689481662f53c25650 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 25 Mar 2023 07:28:38 +0530 Subject: [PATCH 27/38] Refactor --- src/pages/authenticator/index.tsx | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx index 13aed7dc5..567a98d6f 100644 --- a/src/pages/authenticator/index.tsx +++ b/src/pages/authenticator/index.tsx @@ -12,24 +12,20 @@ const OTPPage = () => { const [searchTerm, setSearchTerm] = useState(''); useEffect(() => { + // refactor this code const fetchCodes = async () => { try { - getAuthCodes() - .then((res) => { - setCodes(res); - }) - .catch((err) => { - if (err.message === CustomError.KEY_MISSING) { - router.push({ - pathname: PAGES.CREDENTIALS, - query: { redirectPage: PAGES.AUTH }, - }); - return; - } - // do not log errors + const res = await getAuthCodes(); + setCodes(res); + } catch (err) { + if (err.message === CustomError.KEY_MISSING) { + router.push({ + pathname: PAGES.CREDENTIALS, + query: { redirectPage: PAGES.AUTH }, }); - } catch (error) { - // do not log errors + } else { + // do not log errors + } } }; fetchCodes(); From ac713bf9d6f0d6d4826eff7b5e5736c180453ee6 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 25 Mar 2023 07:30:19 +0530 Subject: [PATCH 28/38] Fix typos --- src/components/Authenicator/OTPDisplay.tsx | 4 ++-- src/services/authenticator/authenticatorService.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Authenicator/OTPDisplay.tsx b/src/components/Authenicator/OTPDisplay.tsx index fd328041b..37415e644 100644 --- a/src/components/Authenicator/OTPDisplay.tsx +++ b/src/components/Authenicator/OTPDisplay.tsx @@ -110,7 +110,7 @@ interface OTPDisplayProps { const OTPDisplay = (props: OTPDisplayProps) => { const { codeInfo } = props; const [code, setCode] = useState(''); - const [nextcode, setNextCode] = useState(''); + const [nextCode, setNextCode] = useState(''); const [codeErr, setCodeErr] = useState(''); const generateCodes = () => { @@ -168,7 +168,7 @@ const OTPDisplay = (props: OTPDisplayProps) => { issuer={codeInfo.issuer} account={codeInfo.account} code={code} - nextCode={nextcode} + nextCode={nextCode} /> ) : ( diff --git a/src/services/authenticator/authenticatorService.ts b/src/services/authenticator/authenticatorService.ts index 8ef3df69b..54f010683 100644 --- a/src/services/authenticator/authenticatorService.ts +++ b/src/services/authenticator/authenticatorService.ts @@ -12,7 +12,7 @@ export const getAuthCodes = async (): Promise => { try { const authKeyData = await getAuthKey(); const cryptoWorker = await ComlinkCryptoWorker.getInstance(); - const authentitorKey = await cryptoWorker.decryptB64( + const authenticatorKey = await cryptoWorker.decryptB64( authKeyData.encryptedKey, authKeyData.header, masterKey @@ -26,7 +26,7 @@ export const getAuthCodes = async (): Promise => { const decryptedCode = await cryptoWorker.decryptMetadata( entity.encryptedData, entity.header, - authentitorKey + authenticatorKey ); try { return Code.fromRawData(entity.id, decryptedCode); From f8667bf692b08c53b9ce5cb52a3fe9887d257e39 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 25 Mar 2023 07:35:44 +0530 Subject: [PATCH 29/38] Simplify if --- src/components/Authenicator/OTPDisplay.tsx | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/Authenicator/OTPDisplay.tsx b/src/components/Authenicator/OTPDisplay.tsx index 37415e644..a6cf1e263 100644 --- a/src/components/Authenicator/OTPDisplay.tsx +++ b/src/components/Authenicator/OTPDisplay.tsx @@ -112,6 +112,7 @@ const OTPDisplay = (props: OTPDisplayProps) => { const [code, setCode] = useState(''); const [nextCode, setNextCode] = useState(''); const [codeErr, setCodeErr] = useState(''); + const generateCodeInterval = 1000; const generateCodes = () => { try { @@ -145,19 +146,18 @@ const OTPDisplay = (props: OTPDisplayProps) => { useEffect(() => { generateCodes(); - let intervalId; + const codeType = codeInfo.type; + const intervalId = + codeType.toLowerCase() === 'totp' || + codeType.toLowerCase() === 'hotp' + ? setInterval(() => { + generateCodes(); + }, generateCodeInterval) + : null; - if (codeInfo.type.toLowerCase() === 'totp') { - intervalId = setInterval(() => { - generateCodes(); - }, 1000); - } else if (codeInfo.type.toLowerCase() === 'hotp') { - intervalId = setInterval(() => { - generateCodes(); - }, 1000); - } - - return () => clearInterval(intervalId); + return () => { + if (intervalId) clearInterval(intervalId); + }; }, [codeInfo]); return ( From 2069fff499b2c17672a2d9ebfdc1ff0374195888 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 25 Mar 2023 07:38:54 +0530 Subject: [PATCH 30/38] Convert pure data class to interface --- src/types/authenticator/auth_entity.ts | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/types/authenticator/auth_entity.ts b/src/types/authenticator/auth_entity.ts index 083651d31..cd97f5262 100644 --- a/src/types/authenticator/auth_entity.ts +++ b/src/types/authenticator/auth_entity.ts @@ -1,23 +1,8 @@ -export class AuthEntity { +export interface AuthEntity { id: string; encryptedData: string | null; header: string | null; isDeleted: boolean; createdAt: number; updatedAt: number; - constructor( - id: string, - encryptedData: string | null, - header: string | null, - isDeleted: boolean, - createdAt: number, - updatedAt: number - ) { - this.id = id; - this.encryptedData = encryptedData; - this.header = header; - this.isDeleted = isDeleted; - this.createdAt = createdAt; - this.updatedAt = updatedAt; - } } From 5688c42d6e4dc9a4fd6ab5684fa296c09ef5a91c Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 25 Mar 2023 07:43:32 +0530 Subject: [PATCH 31/38] Fix string extraction --- public/locales/en/translation.json | 1 + src/components/Sidebar/UtilitySection.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 906b3c264..879208e1c 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -459,6 +459,7 @@ "UPLOAD_GOOGLE_TAKEOUT": "Google takeout", "CANCEL_UPLOADS": "Cancel uploads", "DEDUPLICATE_FILES": "Deduplicate files", + "AUTHENTICATOR_SECTION": "Authenticator", "NO_DUPLICATES_FOUND": "You've no duplicate files that can be cleared", "CLUB_BY_CAPTURE_TIME": "Club by capture time", "FILES": "Files", diff --git a/src/components/Sidebar/UtilitySection.tsx b/src/components/Sidebar/UtilitySection.tsx index 99c199107..a07c70a11 100644 --- a/src/components/Sidebar/UtilitySection.tsx +++ b/src/components/Sidebar/UtilitySection.tsx @@ -102,7 +102,7 @@ export default function UtilitySection({ closeSidebar }) { {isInternalUser() && ( - {constants.AUTHENTICATOR_SECTION} + {t('AUTHENTICATOR_SECTION')} )} From c2451105e5bf8c3ca3da1a35f92538de6a908c19 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 25 Mar 2023 08:07:19 +0530 Subject: [PATCH 32/38] Wrap decryption inside tryCatch --- src/services/authenticator/authenticatorService.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/services/authenticator/authenticatorService.ts b/src/services/authenticator/authenticatorService.ts index 54f010683..b0ae58ef6 100644 --- a/src/services/authenticator/authenticatorService.ts +++ b/src/services/authenticator/authenticatorService.ts @@ -23,12 +23,13 @@ export const getAuthCodes = async (): Promise => { authEntity .filter((f) => !f.isDeleted) .map(async (entity) => { - const decryptedCode = await cryptoWorker.decryptMetadata( - entity.encryptedData, - entity.header, - authenticatorKey - ); try { + const decryptedCode = + await cryptoWorker.decryptMetadata( + entity.encryptedData, + entity.header, + authenticatorKey + ); return Code.fromRawData(entity.id, decryptedCode); } catch (e) { logError( From 5e7411c405b775d9b60d27d497041a2d7df9c253 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 25 Mar 2023 08:52:01 +0530 Subject: [PATCH 33/38] Extract footer as component --- src/components/Authenicator/AuthFooder.tsx | 25 +++++++++++++++++++ src/pages/authenticator/index.tsx | 29 ++-------------------- 2 files changed, 27 insertions(+), 27 deletions(-) create mode 100644 src/components/Authenicator/AuthFooder.tsx diff --git a/src/components/Authenicator/AuthFooder.tsx b/src/components/Authenicator/AuthFooder.tsx new file mode 100644 index 000000000..64eeb1029 --- /dev/null +++ b/src/components/Authenicator/AuthFooder.tsx @@ -0,0 +1,25 @@ +import { Button } from '@mui/material'; + +export const AuthFooter = () => { + return ( +
+

Download our mobile app to add & manage your secrets.

+ + + +
+ ); +}; diff --git a/src/pages/authenticator/index.tsx b/src/pages/authenticator/index.tsx index 567a98d6f..b61adf333 100644 --- a/src/pages/authenticator/index.tsx +++ b/src/pages/authenticator/index.tsx @@ -1,10 +1,10 @@ import React, { useEffect, useState } from 'react'; import OTPDisplay from 'components/Authenicator/OTPDisplay'; import { getAuthCodes } from 'services/authenticator/authenticatorService'; -import { Button } from '@mui/material'; import { CustomError } from 'utils/error'; import { PAGES } from 'constants/pages'; import { useRouter } from 'next/router'; +import { AuthFooter } from 'components/Authenicator/AuthFooder'; const OTPPage = () => { const router = useRouter(); @@ -12,7 +12,6 @@ const OTPPage = () => { const [searchTerm, setSearchTerm] = useState(''); useEffect(() => { - // refactor this code const fetchCodes = async () => { try { const res = await getAuthCodes(); @@ -41,30 +40,6 @@ const OTPPage = () => { .includes(searchTerm.toLowerCase()) ); - const DownloadApp = () => { - return ( -
-

Download our mobile app to add & manage your secrets.

- - - -
- ); - }; - return (
{ )) )}
- +
); From 8c8a03db3d66f5714b942cca69e1a8d2d2600ff8 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 3 Apr 2023 11:02:55 +0530 Subject: [PATCH 34/38] refactor --- src/services/authenticator/authenticatorService.ts | 4 ++-- src/types/authenticator/{auth_entity.ts => api.ts} | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) rename src/types/authenticator/{auth_entity.ts => api.ts} (70%) diff --git a/src/services/authenticator/authenticatorService.ts b/src/services/authenticator/authenticatorService.ts index b0ae58ef6..9928f8b41 100644 --- a/src/services/authenticator/authenticatorService.ts +++ b/src/services/authenticator/authenticatorService.ts @@ -1,5 +1,5 @@ import HTTPService from 'services/HTTPService'; -import { AuthEntity } from 'types/authenticator/auth_entity'; +import { AuthEntity } from 'types/authenticator/api'; import { Code } from 'types/authenticator/code'; import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker'; import { getEndpoint } from 'utils/common/apiUtil'; @@ -61,7 +61,7 @@ export const getAuthCodes = async (): Promise => { } }; -export const getAuthKey = async () => { +export const getAuthKey = async (): Promise => { try { const resp = await HTTPService.get( `${ENDPOINT}/authenticator/key`, diff --git a/src/types/authenticator/auth_entity.ts b/src/types/authenticator/api.ts similarity index 70% rename from src/types/authenticator/auth_entity.ts rename to src/types/authenticator/api.ts index cd97f5262..569df8185 100644 --- a/src/types/authenticator/auth_entity.ts +++ b/src/types/authenticator/api.ts @@ -6,3 +6,8 @@ export interface AuthEntity { createdAt: number; updatedAt: number; } + +export interface AuthKey { + encryptedKey: string; + header: string; +} From 349d9a3786d7e3edebec0c52efbcd352bdb54732 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 3 Apr 2023 11:06:28 +0530 Subject: [PATCH 35/38] refactor: rename --- src/pages/{authenticator => auth}/index.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/pages/{authenticator => auth}/index.tsx (100%) diff --git a/src/pages/authenticator/index.tsx b/src/pages/auth/index.tsx similarity index 100% rename from src/pages/authenticator/index.tsx rename to src/pages/auth/index.tsx From db95a096a0e4aaf5ffb3f10761610f460a6a426c Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 3 Apr 2023 11:08:53 +0530 Subject: [PATCH 36/38] Rename OtpPage -> AuthenticatorCodePages --- src/pages/auth/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/auth/index.tsx b/src/pages/auth/index.tsx index b61adf333..4a0c038a6 100644 --- a/src/pages/auth/index.tsx +++ b/src/pages/auth/index.tsx @@ -6,7 +6,7 @@ import { PAGES } from 'constants/pages'; import { useRouter } from 'next/router'; import { AuthFooter } from 'components/Authenicator/AuthFooder'; -const OTPPage = () => { +const AuthenticatorCodesPage = () => { const router = useRouter(); const [codes, setCodes] = useState([]); const [searchTerm, setSearchTerm] = useState(''); @@ -88,4 +88,4 @@ const OTPPage = () => { ); }; -export default OTPPage; +export default AuthenticatorCodesPage; From b25d869d7f8caa603b1f0296dfce890d6bf002de Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 3 Apr 2023 11:16:26 +0530 Subject: [PATCH 37/38] Fix nextCode generation timing --- src/components/Authenicator/OTPDisplay.tsx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/components/Authenicator/OTPDisplay.tsx b/src/components/Authenicator/OTPDisplay.tsx index a6cf1e263..d87d63d53 100644 --- a/src/components/Authenicator/OTPDisplay.tsx +++ b/src/components/Authenicator/OTPDisplay.tsx @@ -112,7 +112,6 @@ const OTPDisplay = (props: OTPDisplayProps) => { const [code, setCode] = useState(''); const [nextCode, setNextCode] = useState(''); const [codeErr, setCodeErr] = useState(''); - const generateCodeInterval = 1000; const generateCodes = () => { try { @@ -145,15 +144,26 @@ const OTPDisplay = (props: OTPDisplayProps) => { }; useEffect(() => { + // this is to set the initial code and nextCode on component mount generateCodes(); const codeType = codeInfo.type; - const intervalId = + 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(); - }, generateCodeInterval) + }, codePeriodInMs) : null; + }, timeToNextCode); return () => { if (intervalId) clearInterval(intervalId); From f9b12ace969d5579e49c248db6d7af3245b34d35 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 3 Apr 2023 11:29:16 +0530 Subject: [PATCH 38/38] Fix lint error --- src/services/authenticator/authenticatorService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/authenticator/authenticatorService.ts b/src/services/authenticator/authenticatorService.ts index 9928f8b41..afa07258e 100644 --- a/src/services/authenticator/authenticatorService.ts +++ b/src/services/authenticator/authenticatorService.ts @@ -1,5 +1,5 @@ import HTTPService from 'services/HTTPService'; -import { AuthEntity } from 'types/authenticator/api'; +import { AuthEntity, AuthKey } from 'types/authenticator/api'; import { Code } from 'types/authenticator/code'; import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker'; import { getEndpoint } from 'utils/common/apiUtil'; @@ -61,7 +61,7 @@ export const getAuthCodes = async (): Promise => { } }; -export const getAuthKey = async (): Promise => { +export const getAuthKey = async (): Promise => { try { const resp = await HTTPService.get( `${ENDPOINT}/authenticator/key`,