From f5b3ded19021f668992e8dbc2b4f83e966497e29 Mon Sep 17 00:00:00 2001 From: Abhinav-grd Date: Thu, 17 Jun 2021 16:15:52 +0530 Subject: [PATCH] added qr code render --- src/components/Sidebar.tsx | 18 ++++++ src/components/TwoFactorModal.tsx | 85 ++++++++++++++++++++++++++ src/pages/_app.tsx | 3 + src/services/userService.ts | 11 ++++ src/utils/strings/englishConstants.tsx | 72 ++++++++++++---------- 5 files changed, 156 insertions(+), 33 deletions(-) create mode 100644 src/components/TwoFactorModal.tsx diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index a082da254..d36331416 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -28,6 +28,7 @@ import { LogoImage } from 'pages/_app'; import { SetDialogMessage } from './MessageDialog'; import EnteSpinner from './EnteSpinner'; import RecoveryKeyModal from './RecoveryKeyModal'; +import TwoFactorModal from './TwoFactorModal'; interface Props { files: File[]; @@ -45,6 +46,7 @@ export default function Sidebar(props: Props) { }, []); const [isOpen, setIsOpen] = useState(false); const [recoverModalView, setRecoveryModalView] = useState(false); + const [twoFactorModal, setTwoFactorModal] = useState(true); useEffect(() => { const main = async () => { if (!isOpen) { @@ -214,6 +216,22 @@ export default function Sidebar(props: Props) { {constants.DOWNLOAD_RECOVERY_KEY} + <> + setTwoFactorModal(false)} + somethingWentWrong={() => props.setDialogMessage({ + title: constants.TWO_FACTOR_SECRET_FETCHING_FAILED, + close: { variant: 'danger' }, + })} + /> + setTwoFactorModal(true)} + > + {constants.TWO_FACTOR_AUTHENTICATION} + + { diff --git a/src/components/TwoFactorModal.tsx b/src/components/TwoFactorModal.tsx new file mode 100644 index 000000000..9e449e073 --- /dev/null +++ b/src/components/TwoFactorModal.tsx @@ -0,0 +1,85 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import React, { useEffect, useState } from 'react'; +import constants from 'utils/strings/constants'; +import MessageDialog from './MessageDialog'; +import { DeadCenter } from 'pages/gallery'; +import { Button } from 'react-bootstrap'; +import { setupTwoFactor, TwoFactorSector } from 'services/userService'; +import styled from 'styled-components'; +import EnteSpinner from './EnteSpinner'; + +const QRCode = styled.img` +height:200px; +width:200px; +`; + +interface Props { + show: boolean; + onHide: () => void; + somethingWentWrong: any; +} + +enum SetupMode { + QR_CODE, + MANUAL_CODE, +} +function TwoFactorModal({ somethingWentWrong, ...props }: Props) { + const [setupMode, setSetupMode] = useState(SetupMode.QR_CODE); + const [twoFactorSecret, setTwoFactorSecret] = useState(null); + useEffect(() => { + if (!props.show) { + return; + } + const main = async () => { + const twoFactorSecret = await setupTwoFactor(); + setTwoFactorSecret(twoFactorSecret); + }; + main(); + }, [props.show]); + + return ( + null, + disabled: false, + variant: 'success', + }, + }} + > + {setupMode === SetupMode.QR_CODE ? ( + <> +

{constants.TWO_FACTOR_AUTHENTICATION_QR_INSTRUCTION}

+ + {!twoFactorSecret ? : + + } + + + + ) : (<> +

{constants.TWO_FACTOR_AUTHENTICATION_MANUAL_CODE_INSTRUCTION}

+ +
+
+ +
+ + ) + } +
+ ); +} +export default TwoFactorModal; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index c13831d36..ff1653267 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -161,6 +161,9 @@ const GlobalStyles = createGlobalStyle` background-size: 20px 20px; background-position: center; } + .btn.focus , .btn:focus{ + box-shadow: none; + } .btn-success { background: #2dc262; border-color: #29a354; diff --git a/src/services/userService.ts b/src/services/userService.ts index ad4622dce..77dd2ce74 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -34,6 +34,10 @@ export interface VerificationResponse { encryptedToken?: string; token?: string; } +export interface TwoFactorSector { + secretKey: string + qrCode: string +} export const getOtt = (email: string) => HTTPService.get(`${ENDPOINT}/users/ott`, { email, @@ -98,3 +102,10 @@ export const isTokenValid = async () => { return false; } }; + +export const setupTwoFactor = async () => { + const resp = await HTTPService.get(`${ENDPOINT}/users/two-factor/setup`, null, { + 'X-Auth-Token': getToken(), + }); + return resp.data as TwoFactorSector; +}; diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 75e0344b8..1196e0944 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -5,7 +5,7 @@ import styled from 'styled-components'; * Global English constants. */ -const dateString = function(date) { +const dateString = function (date) { return new Date(date / 1000).toLocaleDateString('en-US', { year: 'numeric', month: 'long', @@ -24,7 +24,7 @@ const Logo = styled.img` `; const englishConstants = { - HERO_HEADER: () =>
with
your memories are
, + HERO_HEADER: () =>
with
your memories are
, HERO_SLIDE_1_TITLE: 'protected', HERO_SLIDE_1: 'end-to-end encrypted with your password, visible only to you', HERO_SLIDE_2_TITLE: 'synced', @@ -46,7 +46,7 @@ const englishConstants = { VERIFY_EMAIL: 'verify email', EMAIL_SENT: ({ email }) => (

- we have sent a mail to {email} + we have sent a mail to {email}

), CHECK_INBOX: 'please check your inbox (and spam) to complete verification', @@ -67,10 +67,10 @@ const englishConstants = { 'please enter a password that we can use to encrypt your data', PASSPHRASE_DISCLAIMER: () => (

- we don't store your password, so if you forget, + we don't store your password, so if you forget, we will not be able to help you {' '} - recover your data. + recover your data.

), PASSPHRASE_HINT: 'password', @@ -182,26 +182,26 @@ const englishConstants = { MESSAGE: 'message', INSTALL_MOBILE_APP: () => ( <> - install our{' '} + install our{' '} - android + android {' '} - or + or {' '} - ios app + ios app {' '} - to automatically backup all your photos + to automatically backup all your photos ), DOWNLOAD_APP_MESSAGE: () => ( @@ -230,41 +230,41 @@ const englishConstants = { FREE_SUBSCRIPTION_INFO: (expiryTime) => ( <>

- you are on the free + you are on the free {' '} - plan that expires on{' '} + plan that expires on{' '} {dateString(expiryTime)}

), RENEWAL_ACTIVE_SUBSCRIPTION_INFO: (expiryTime) => (

- your subscription will renew on {dateString(expiryTime)} + your subscription will renew on {dateString(expiryTime)}

), RENEWAL_CANCELLED_SUBSCRIPTION_INFO: (expiryTime) => ( <>

- your subscription will be cancelled on {dateString(expiryTime)} + your subscription will be cancelled on {dateString(expiryTime)}

), USAGE_INFO: (usage, quota) => (

- you have used {usage} + you have used {usage} {' '} - GB out of your {quota} + GB out of your {quota} {' '} - GB quota + GB quota

), SUBSCRIPTION_PURCHASE_SUCCESS: (expiryTime) => ( <>

we've received your payment

- your subscription is valid till{' '} + your subscription is valid till{' '} {dateString(expiryTime)} ), @@ -287,8 +287,8 @@ const englishConstants = { CANCEL_SUBSCRIPTION_MESSAGE: () => ( <>

- all of your data will be deleted from our servers at the end of - this billing period. + all of your data will be deleted from our servers at the end of + this billing period.

are you sure that you want to unsubscribe?

@@ -313,8 +313,8 @@ const englishConstants = { <>

are you sure you want to delete this album?

- all files that are present only in this album will be - permanently deleted + all files that are present only in this album will be + permanently deleted

), @@ -336,42 +336,42 @@ const englishConstants = { SEARCH_HINT: () => try searching for New York, April 14, Christmas..., TERMS_AND_CONDITIONS: () => (

- I agree to the{' '} + I agree to the{' '} - terms + terms {' '} - and + and {' '} - privacy policy + privacy policy {' '}

), CONFIRM_PASSWORD_NOT_SAVED: () => (

- i understand that if i lose my password , i may lose my data since - my data is{' '} + i understand that if i lose my password , i may lose my data since + my data is{' '} - end-to-end encrypted + end-to-end encrypted {' '} - with ente + with ente

), SEARCH_STATS: ({ resultCount, timeTaken }) => ( - found {resultCount} + found {resultCount} {' '} - memories ( + memories ( {' '} {' '} {timeTaken} {' '} - seconds ) + seconds ) ), NOT_FILE_OWNER: 'deleting shared collection files is not allowed', @@ -397,6 +397,12 @@ const englishConstants = { SHOW_ALL: 'show all', LOGIN_TO_UPLOAD_FILES: (count: number) => count === 1 ? `1 file received. login to upload` : `${count} files received. login to upload`, FILES_TO_BE_UPLOADED: (count: number) => count === 1 ? `1 file received. uploading in a jiffy` : `${count} files received. Uploading in a jiffy`, + TWO_FACTOR_AUTHENTICATION: 'two-factor authentication', + TWO_FACTOR_SECRET_FETCHING_FAILED: 'unable to get two factor authentication data, please try again ', + TWO_FACTOR_AUTHENTICATION_QR_INSTRUCTION: 'please scan QR code below with your favorite authenticator app', + ENTER_CODE_MANUALLY: 'enter the code manually', + TWO_FACTOR_AUTHENTICATION_MANUAL_CODE_INSTRUCTION: 'please enter this code in your favorite authenticator app', + }; export default englishConstants;