From 32310343e3210bde9fd616f6e2ca191fde96b888 Mon Sep 17 00:00:00 2001 From: solufa Date: Sun, 22 Dec 2024 23:47:57 +0900 Subject: [PATCH] feat: implement totpSetup UI --- client/layouts/basicHeader/BasicHeader.tsx | 13 +-- client/layouts/basicHeader/YourProfile.tsx | 76 +++++++++++++++ client/package-lock.json | 102 ++++++++++++++++++++- client/package.json | 2 + 4 files changed, 177 insertions(+), 16 deletions(-) create mode 100644 client/layouts/basicHeader/YourProfile.tsx diff --git a/client/layouts/basicHeader/BasicHeader.tsx b/client/layouts/basicHeader/BasicHeader.tsx index f9e0fc9..12c3a7e 100644 --- a/client/layouts/basicHeader/BasicHeader.tsx +++ b/client/layouts/basicHeader/BasicHeader.tsx @@ -10,6 +10,7 @@ import type { ReactNode } from 'react'; import { useEffect, useState } from 'react'; import { pagesPath } from 'utils/$path'; import styles from './BasicHeader.module.css'; +import { YourProfile } from './YourProfile'; const Menu = ({ open, @@ -71,17 +72,7 @@ export const BasicHeader = (props: { user: UserDto }) => { - setOpenProfile(false)}> - - -
Sign in name: {props.user.signInName}
- -
Display name: {props.user.displayName}
- -
Email: {props.user.email}
-
- setOpenProfile(false)} /> -
+ {openProfile && setOpenPassword(false)} />} setOpenPassword(false)}> diff --git a/client/layouts/basicHeader/YourProfile.tsx b/client/layouts/basicHeader/YourProfile.tsx new file mode 100644 index 0000000..30d358e --- /dev/null +++ b/client/layouts/basicHeader/YourProfile.tsx @@ -0,0 +1,76 @@ +import { Button, TextField, View } from '@aws-amplify/ui-react'; +import { + fetchMFAPreference, + setUpTOTP, + updateMFAPreference, + verifyTOTPSetup, +} from 'aws-amplify/auth'; +import { APP_NAME } from 'common/constants'; +import type { UserDto } from 'common/types/user'; +import { Modal, ModalBody, ModalFooter, ModalHeader } from 'components/modal/Modal'; +import { Spacer } from 'components/Spacer'; +import { useEffect, useState } from 'react'; + +export const YourProfile = (props: { user: UserDto; onClose: () => void }) => { + const [enabledTotp, setEnabledTotp] = useState(); + const [qrCodeUrl, setQrCodeUrl] = useState(''); + const [totpCode, setTotpCode] = useState(''); + const enableTOTP = async () => { + const totpSetupDetails = await setUpTOTP(); + const setupUri = totpSetupDetails.getSetupUri(APP_NAME); + + setQrCodeUrl(setupUri.toString()); + }; + const verifyTOTP = async () => { + await verifyTOTPSetup({ code: totpCode }); + await updateMFAPreference({ totp: 'PREFERRED' }); + alert('TOTP has been successfully enabled!'); + setQrCodeUrl(''); + }; + + useEffect(() => { + fetchMFAPreference().then((res) => { + setEnabledTotp(res.preferred === 'TOTP'); + }); + }, []); + + return ( + + + +
Sign in name: {props.user.signInName}
+ +
Display name: {props.user.displayName}
+ +
Email: {props.user.email}
+ + {enabledTotp ? ( +
MFA: true
+ ) : qrCodeUrl ? ( + +

Scan this QR code with your authenticator app:

+ TOTP QR Code + setTotpCode(e.target.value)} + /> + +
+ ) : ( + + )} +
+ +
+ ); +}; diff --git a/client/package-lock.json b/client/package-lock.json index 3857a06..5b4910b 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -16,6 +16,7 @@ "axios": "^1.7.7", "jotai": "^2.10.3", "next": "^15.1.0", + "qrcode": "^1.5.4", "react": "^18.3.1", "react-dom": "^18.3.1", "swagger-ui-react": "^5.18.2", @@ -23,6 +24,7 @@ "zod": "^3.23.8" }, "devDependencies": { + "@types/qrcode": "^1.5.5", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@types/swagger-ui-react": "^4.18.3", @@ -338,6 +340,85 @@ "react": "^16.14.0 || ^17.0 || ^18.0" } }, + "node_modules/@aws-amplify/ui-react/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@aws-amplify/ui-react/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/@aws-amplify/ui-react/node_modules/qrcode": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.0.tgz", + "integrity": "sha512-9MgRpgVc+/+47dFvQeD6U2s0Z92EsKzcHogtum4QB+UNd025WOJSHvn/hjk9xmzj7Stj95CyUAs31mrjxliEsQ==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "encode-utf8": "^1.0.3", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@aws-amplify/ui-react/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/@aws-amplify/ui-react/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aws-amplify/ui-react/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@aws-crypto/crc32": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", @@ -4453,6 +4534,16 @@ "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", "devOptional": true }, + "node_modules/@types/qrcode": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.5.tgz", + "integrity": "sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/ramda": { "version": "0.30.2", "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.30.2.tgz", @@ -5553,7 +5644,8 @@ "node_modules/encode-utf8": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", - "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==" + "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==", + "license": "MIT" }, "node_modules/enhanced-resolve": { "version": "5.17.0", @@ -8354,12 +8446,12 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "node_modules/qrcode": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.0.tgz", - "integrity": "sha512-9MgRpgVc+/+47dFvQeD6U2s0Z92EsKzcHogtum4QB+UNd025WOJSHvn/hjk9xmzj7Stj95CyUAs31mrjxliEsQ==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", "dependencies": { "dijkstrajs": "^1.0.1", - "encode-utf8": "^1.0.3", "pngjs": "^5.0.0", "yargs": "^15.3.1" }, diff --git a/client/package.json b/client/package.json index c7cf634..e09eb2a 100644 --- a/client/package.json +++ b/client/package.json @@ -29,6 +29,7 @@ "axios": "^1.7.7", "jotai": "^2.10.3", "next": "^15.1.0", + "qrcode": "^1.5.4", "react": "^18.3.1", "react-dom": "^18.3.1", "swagger-ui-react": "^5.18.2", @@ -36,6 +37,7 @@ "zod": "^3.23.8" }, "devDependencies": { + "@types/qrcode": "^1.5.5", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@types/swagger-ui-react": "^4.18.3",