From 7c63c5ba6854250cc59d88862a727fa5e72ba63a Mon Sep 17 00:00:00 2001
From: William Lee <43682783+wlee221@users.noreply.github.com>
Date: Wed, 23 Nov 2022 16:53:17 -0800
Subject: [PATCH 1/2] Revert "feat(account-settings): Add ConfigureTOTP
component (#2969)"
This reverts commit 8a65f82be879163e1be27f740436a06218cef61e.
---
.../configure-totp/aws-exports.js | 2 -
.../configure-totp/index.page.tsx | 47 -----
.../__tests__/__snapshots__/exports.ts.snap | 1 -
.../ConfigureTOTP/ConfigureTOTP.tsx | 175 ------------------
.../__tests__/ConfigureTOTP.test.tsx | 120 ------------
.../__snapshots__/ConfigureTOTP.test.tsx.snap | 61 ------
.../ConfigureTOTP/constants.ts | 5 -
.../ConfigureTOTP/defaults.tsx | 23 ---
.../AccountSettings/ConfigureTOTP/index.ts | 1 -
.../AccountSettings/ConfigureTOTP/types.ts | 20 --
.../src/components/AccountSettings/index.ts | 1 -
.../src/components/AccountSettings/types.ts | 14 +-
packages/react/src/hooks/useAuth.ts | 2 +-
.../ui/src/helpers/accountSettings/utils.ts | 34 ----
14 files changed, 2 insertions(+), 504 deletions(-)
delete mode 100644 examples/next/pages/ui/components/account-settings/configure-totp/aws-exports.js
delete mode 100644 examples/next/pages/ui/components/account-settings/configure-totp/index.page.tsx
delete mode 100644 packages/react/src/components/AccountSettings/ConfigureTOTP/ConfigureTOTP.tsx
delete mode 100644 packages/react/src/components/AccountSettings/ConfigureTOTP/__tests__/ConfigureTOTP.test.tsx
delete mode 100644 packages/react/src/components/AccountSettings/ConfigureTOTP/__tests__/__snapshots__/ConfigureTOTP.test.tsx.snap
delete mode 100644 packages/react/src/components/AccountSettings/ConfigureTOTP/constants.ts
delete mode 100644 packages/react/src/components/AccountSettings/ConfigureTOTP/defaults.tsx
delete mode 100644 packages/react/src/components/AccountSettings/ConfigureTOTP/index.ts
delete mode 100644 packages/react/src/components/AccountSettings/ConfigureTOTP/types.ts
diff --git a/examples/next/pages/ui/components/account-settings/configure-totp/aws-exports.js b/examples/next/pages/ui/components/account-settings/configure-totp/aws-exports.js
deleted file mode 100644
index ed7194f6bca..00000000000
--- a/examples/next/pages/ui/components/account-settings/configure-totp/aws-exports.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import awsExports from '@environments/auth/auth-with-optional-totp-and-sms-mfa/src/aws-exports';
-export default awsExports;
diff --git a/examples/next/pages/ui/components/account-settings/configure-totp/index.page.tsx b/examples/next/pages/ui/components/account-settings/configure-totp/index.page.tsx
deleted file mode 100644
index 0d98a884f00..00000000000
--- a/examples/next/pages/ui/components/account-settings/configure-totp/index.page.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import React from 'react';
-
-import { Amplify } from 'aws-amplify';
-
-import {
- Authenticator,
- Button,
- Card,
- Flex,
- Heading,
-} from '@aws-amplify/ui-react';
-import '@aws-amplify/ui-react/styles.css';
-
-import { Alert, ConfigureTOTP } from '@aws-amplify/ui-react';
-
-import awsExports from './aws-exports';
-Amplify.configure(awsExports);
-
-export default function App() {
- const [isSuccessful, setIsSuccessful] = React.useState(false);
- return (
-
- {({ signOut }) => (
-
-
-
-
- Setup MFA:
- {
- setIsSuccessful(true);
- }}
- />
- {isSuccessful ? (
-
- TOTP has been set up successfully
-
- ) : null}
-
-
-
-
-
- )}
-
- );
-}
diff --git a/packages/react/__tests__/__snapshots__/exports.ts.snap b/packages/react/__tests__/__snapshots__/exports.ts.snap
index 17f1f9e0af2..475a6633c20 100644
--- a/packages/react/__tests__/__snapshots__/exports.ts.snap
+++ b/packages/react/__tests__/__snapshots__/exports.ts.snap
@@ -17,7 +17,6 @@ Array [
"ComponentClassObject",
"ComponentPropsToStylePropsMap",
"ComponentPropsToStylePropsMapKeys",
- "ConfigureTOTP",
"DeleteUser",
"Divider",
"Expander",
diff --git a/packages/react/src/components/AccountSettings/ConfigureTOTP/ConfigureTOTP.tsx b/packages/react/src/components/AccountSettings/ConfigureTOTP/ConfigureTOTP.tsx
deleted file mode 100644
index ce6bed0436f..00000000000
--- a/packages/react/src/components/AccountSettings/ConfigureTOTP/ConfigureTOTP.tsx
+++ /dev/null
@@ -1,175 +0,0 @@
-import React from 'react';
-import QRCode from 'qrcode';
-
-import {
- AmplifyUser,
- getLogger,
- getTotpCodeURL,
- setupTOTP,
- translate,
- verifyTOTPToken,
-} from '@aws-amplify/ui';
-
-import { useAuth } from '../../../internal';
-import { View, Flex } from '../../../primitives';
-import { FormValues } from '../types';
-import {
- ConfirmationCode,
- CopySecretKey,
- Error,
- SecretKeyQRCode,
- SubmitButton,
-} from './defaults';
-import { ConfigureTOTPProps, TotpSecret, VerifyTotpStatus } from './types';
-import { QR_CODE_DIMENSIONS } from './constants';
-
-const logger = getLogger('Auth');
-
-function ConfigureTOTP({
- totpIssuer = 'AWSCognito',
- totpUsername,
- onSuccess,
- onError,
-}: ConfigureTOTPProps): JSX.Element | null {
- const [formValues, setFormValues] = React.useState({ code: '' });
- const [totpSecret, setTotpSecret] = React.useState(null);
- const [verifyTotpStatus, setVerifyTotpStatus] =
- React.useState({
- isVerifying: false,
- errorMessage: null,
- });
-
- const { user, isLoading } = useAuth();
-
- const hasInit = React.useRef(false);
-
- const generateQRCode = React.useCallback(
- async (currentUser: AmplifyUser): Promise => {
- try {
- const secretKey = await setupTOTP(currentUser);
- const username = totpUsername || currentUser?.username;
- const totpCode = getTotpCodeURL(totpIssuer, username, secretKey);
- const qrCode = await QRCode.toDataURL(totpCode);
-
- setTotpSecret({ secretKey, qrCode });
- } catch (e) {
- logger.error(e);
- }
- },
- [totpIssuer, totpUsername]
- );
-
- React.useEffect(() => {
- if (user && !hasInit.current) {
- hasInit.current = true;
- generateQRCode(user);
- }
- }, [generateQRCode, user]);
-
- // translations
- const confirmText = translate('Confirm');
- const copyCodeText = translate('Copy Secret Code');
-
- // event handlers
- const handleChange = React.useCallback(
- (event: React.ChangeEvent) => {
- event.preventDefault();
- const { name, value } = event.target;
- setFormValues((prevFormValues) => ({ ...prevFormValues, [name]: value }));
- },
- []
- );
-
- const runVerifyTotpToken = React.useCallback(
- async ({ user, code }: { user: AmplifyUser; code: string }) => {
- setVerifyTotpStatus({ isVerifying: true, errorMessage: null });
-
- try {
- await verifyTOTPToken({ user, code });
-
- setVerifyTotpStatus({ isVerifying: false, errorMessage: null });
-
- onSuccess?.(); // notify success to the parent
- } catch (e) {
- const error = e as Error;
-
- setVerifyTotpStatus({
- isVerifying: false,
- errorMessage: error.message,
- });
-
- onError?.(error); // notify error to the parent
- }
- },
- [onError, onSuccess]
- );
-
- const handleSubmit = React.useCallback(
- (event: React.FormEvent) => {
- event.preventDefault();
- const { code } = formValues;
- runVerifyTotpToken({ user, code });
- },
- [user, formValues, runVerifyTotpToken]
- );
-
- const handleCopy = React.useCallback(() => {
- navigator.clipboard.writeText(totpSecret.secretKey);
- }, [totpSecret]);
-
- /** Return null if user isn't authenticated in the first place */
- if (!user) {
- logger.warn(' requires user to be authenticated.');
- return null;
- }
-
- /** Return null if Auth.getCurrentAuthenticatedUser is still in progress */
- if (isLoading) {
- return null;
- }
-
- const { isVerifying, errorMessage } = verifyTotpStatus;
-
- return (
-
-
- {totpSecret?.qrCode ? (
-
- ) : null}
-
- {copyCodeText}
-
-
-
-
- {confirmText}
-
- {errorMessage ? {errorMessage} : null}
-
-
- );
-}
-
-export default ConfigureTOTP;
diff --git a/packages/react/src/components/AccountSettings/ConfigureTOTP/__tests__/ConfigureTOTP.test.tsx b/packages/react/src/components/AccountSettings/ConfigureTOTP/__tests__/ConfigureTOTP.test.tsx
deleted file mode 100644
index 3b07399d515..00000000000
--- a/packages/react/src/components/AccountSettings/ConfigureTOTP/__tests__/ConfigureTOTP.test.tsx
+++ /dev/null
@@ -1,120 +0,0 @@
-import React from 'react';
-import {
- render,
- screen,
- fireEvent,
- waitFor,
- act,
-} from '@testing-library/react';
-
-import * as UIModule from '@aws-amplify/ui';
-
-import ConfigureTOTP from '../ConfigureTOTP';
-
-const user = { username: 'testuser' } as unknown as UIModule.AmplifyUser;
-jest.mock('../../../../internal', () => ({
- useAuth: () => ({
- user,
- isLoading: false,
- }),
-}));
-
-const setupTOTPSpy = jest.spyOn(UIModule, 'setupTOTP');
-const verifyTOTPToken = jest.spyOn(UIModule, 'verifyTOTPToken');
-
-describe('ConfigureTOTP', () => {
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
- it('renders as expected', () => {
- setupTOTPSpy.mockResolvedValue('secretcode');
- const { container } = render();
- expect(container).toMatchSnapshot();
- });
-
- it('calls setupTOTP with expected arguments', async () => {
- setupTOTPSpy.mockResolvedValue('secretcode');
-
- render();
-
- await screen.findByAltText('qr code');
- expect(setupTOTPSpy).toHaveBeenCalledWith(user);
- });
-
- it('onSuccess is called after successful totp verification', async () => {
- setupTOTPSpy.mockResolvedValue('secretcode');
- verifyTOTPToken.mockResolvedValue();
-
- const onSuccess = jest.fn();
-
- await act(async () => {
- render();
- });
-
- const submitButton = await screen.findByRole('button', {
- name: 'Confirm',
- });
- fireEvent.submit(submitButton);
-
- // submit handling is async, wait for onSuccess to be called
- // https://testing-library.com/docs/dom-testing-library/api-async/#waitfor
- await waitFor(() => expect(onSuccess).toBeCalledTimes(1));
- });
-
- it('onError is called after unsuccessful submit', async () => {
- setupTOTPSpy.mockResolvedValue('secretCode');
- verifyTOTPToken.mockRejectedValue(new Error('Mock Error'));
-
- const onError = jest.fn();
- await act(async () => {
- render();
- });
- const submitButton = await screen.findByRole('button', {
- name: 'Confirm',
- });
-
- fireEvent.submit(submitButton);
-
- // submit handling is async, wait for onError to be called
- await waitFor(() => expect(onError).toBeCalledTimes(1));
- });
-
- it('error message is displayed after unsuccessful submit', async () => {
- setupTOTPSpy.mockResolvedValue('secretCode');
- verifyTOTPToken.mockRejectedValue(new Error('Mock Error'));
-
- const onError = jest.fn();
-
- await act(async () => {
- render();
- });
-
- const submitButton = await screen.findByRole('button', {
- name: 'Confirm',
- });
-
- fireEvent.submit(submitButton);
-
- // submit handling is async, wait for error to be displayed
- expect(await screen.findByText('Mock Error')).toBeDefined();
- });
-
- it('error message is displayed after unsuccessful submit', async () => {
- setupTOTPSpy.mockResolvedValue('secretCode');
- verifyTOTPToken.mockRejectedValue(new Error('Mock Error'));
-
- const onError = jest.fn();
- await act(async () => {
- render();
- });
-
- const submitButton = await screen.findByRole('button', {
- name: 'Confirm',
- });
-
- fireEvent.submit(submitButton);
-
- expect(await screen.findByText('Mock Error')).toBeDefined();
- });
-});
diff --git a/packages/react/src/components/AccountSettings/ConfigureTOTP/__tests__/__snapshots__/ConfigureTOTP.test.tsx.snap b/packages/react/src/components/AccountSettings/ConfigureTOTP/__tests__/__snapshots__/ConfigureTOTP.test.tsx.snap
deleted file mode 100644
index ee951f69035..00000000000
--- a/packages/react/src/components/AccountSettings/ConfigureTOTP/__tests__/__snapshots__/ConfigureTOTP.test.tsx.snap
+++ /dev/null
@@ -1,61 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ConfigureTOTP renders as expected 1`] = `
-
-`;
diff --git a/packages/react/src/components/AccountSettings/ConfigureTOTP/constants.ts b/packages/react/src/components/AccountSettings/ConfigureTOTP/constants.ts
deleted file mode 100644
index 3557f271985..00000000000
--- a/packages/react/src/components/AccountSettings/ConfigureTOTP/constants.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-const QR_CODE_SIZE = '228px';
-export const QR_CODE_DIMENSIONS = {
- height: QR_CODE_SIZE,
- width: QR_CODE_SIZE,
-};
diff --git a/packages/react/src/components/AccountSettings/ConfigureTOTP/defaults.tsx b/packages/react/src/components/AccountSettings/ConfigureTOTP/defaults.tsx
deleted file mode 100644
index 1b5e9602d7e..00000000000
--- a/packages/react/src/components/AccountSettings/ConfigureTOTP/defaults.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-
-import { Alert, Button, Image, TextField } from '../../../primitives';
-import {
- AccountSettingsError,
- AccountSettingsImage,
- AccountSettingsSubmitButton,
- AccountSettingsTextField,
-} from '../types';
-
-export const ConfirmationCode: AccountSettingsTextField = TextField;
-
-export const SecretKeyQRCode: AccountSettingsImage = Image;
-
-export const CopySecretKey: AccountSettingsSubmitButton = Button;
-
-export const SubmitButton: AccountSettingsSubmitButton = (props) => (
-
-);
-
-export const Error: AccountSettingsError = (props) => {
- return ;
-};
diff --git a/packages/react/src/components/AccountSettings/ConfigureTOTP/index.ts b/packages/react/src/components/AccountSettings/ConfigureTOTP/index.ts
deleted file mode 100644
index a41d614ed93..00000000000
--- a/packages/react/src/components/AccountSettings/ConfigureTOTP/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default as ConfigureTOTP } from './ConfigureTOTP';
diff --git a/packages/react/src/components/AccountSettings/ConfigureTOTP/types.ts b/packages/react/src/components/AccountSettings/ConfigureTOTP/types.ts
deleted file mode 100644
index 9e2170fd63f..00000000000
--- a/packages/react/src/components/AccountSettings/ConfigureTOTP/types.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-export interface TotpSecret {
- secretKey: string;
- qrCode: string;
-}
-
-export interface VerifyTotpStatus {
- isVerifying: boolean;
- errorMessage: string;
-}
-
-export interface ConfigureTOTPProps {
- /** TOTP issuer. Default to "AWSCognito" */
- totpIssuer?: string;
- /** TOTP username. Default to `user.username` */
- totpUsername?: string;
- /** callback once totp is successfully set up */
- onSuccess?: () => void;
- /** callback when there's an error */
- onError?: (error: Error) => void;
-}
diff --git a/packages/react/src/components/AccountSettings/index.ts b/packages/react/src/components/AccountSettings/index.ts
index 94efda90280..bdb43156855 100644
--- a/packages/react/src/components/AccountSettings/index.ts
+++ b/packages/react/src/components/AccountSettings/index.ts
@@ -1,3 +1,2 @@
export { ChangePassword } from './ChangePassword';
export { DeleteUser } from './DeleteUser';
-export { ConfigureTOTP } from './ConfigureTOTP';
diff --git a/packages/react/src/components/AccountSettings/types.ts b/packages/react/src/components/AccountSettings/types.ts
index 8f86b642c4f..cd7a352b064 100644
--- a/packages/react/src/components/AccountSettings/types.ts
+++ b/packages/react/src/components/AccountSettings/types.ts
@@ -2,8 +2,6 @@ import React from 'react';
import {
AlertProps,
ButtonProps,
- TextFieldProps,
- ImageProps,
PasswordFieldProps,
PrimitiveProps,
} from '../../primitives/types';
@@ -14,12 +12,10 @@ import {
* Note that `PrimitiveProps` is used to get native html types, like `onSubmit`.
*/
type CommonPasswordFieldProps = PrimitiveProps;
-type CommonTextFieldProps = PrimitiveProps;
-type CommonImageProps = PrimitiveProps;
type CommonButtonProps = PrimitiveProps;
type CommonAlertProps = PrimitiveProps;
-/*
+/**
* These are overridable component types (e.g. submit button).
*/
export type AccountSettingsPasswordField = React.ComponentType<
@@ -27,14 +23,6 @@ export type AccountSettingsPasswordField = React.ComponentType<
Props & CommonPasswordFieldProps & { validationErrors?: string[] }
>;
-export type AccountSettingsTextField = React.ComponentType<
- Props & CommonTextFieldProps
->;
-
-export type AccountSettingsImage = React.ComponentType<
- Props & CommonImageProps
->;
-
export type AccountSettingsSubmitButton = React.ComponentType<
Props & CommonButtonProps
>;
diff --git a/packages/react/src/hooks/useAuth.ts b/packages/react/src/hooks/useAuth.ts
index 50f8273a14b..b4229e29edb 100644
--- a/packages/react/src/hooks/useAuth.ts
+++ b/packages/react/src/hooks/useAuth.ts
@@ -28,7 +28,7 @@ export const useAuth = (): UseAuthResult => {
* This util will be used to get current user after those events.
*/
const fetchCurrentUser = React.useCallback(async () => {
- setResult((prevResult) => ({ ...prevResult, isLoading: true }));
+ setResult({ user: undefined, isLoading: true, error: undefined });
try {
// casting the result because `Auth.currentAuthenticateduser` returns `any`
diff --git a/packages/ui/src/helpers/accountSettings/utils.ts b/packages/ui/src/helpers/accountSettings/utils.ts
index 808a5ac0a01..1225608248b 100644
--- a/packages/ui/src/helpers/accountSettings/utils.ts
+++ b/packages/ui/src/helpers/accountSettings/utils.ts
@@ -42,37 +42,3 @@ export const deleteUser = async () => {
return Promise.reject(e);
}
};
-
-export const setupTOTP = async (user: AmplifyUser) => {
- try {
- logger.debug('calling Auth.setupTOTP');
-
- const secretCode = await Auth.setupTOTP(user);
- logger.debug('Auth.setupTOTP was successful');
-
- return secretCode;
- } catch (e) {
- logger.error('Auth.setupTOTP failed with error', e);
- return Promise.reject(e);
- }
-};
-
-export const verifyTOTPToken = async ({
- user,
- code,
-}: {
- user: AmplifyUser;
- code: string;
-}) => {
- try {
- logger.debug('calling Auth.verifyTotpToken');
-
- await Auth.verifyTotpToken(user, code);
- logger.debug('Auth.verifyTotpToken was successful');
-
- return Promise.resolve();
- } catch (e) {
- logger.error('Auth.verifyTotpToken failed with error', e);
- return Promise.reject(e);
- }
-};
From 2ac5d2b0704165ab1cc07e9bfaf2d8f3dc0bc09e Mon Sep 17 00:00:00 2001
From: wlee221
Date: Wed, 30 Nov 2022 12:41:03 -0800
Subject: [PATCH 2/2] Revert fetchCurrentUser change
---
packages/react/src/hooks/useAuth.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/react/src/hooks/useAuth.ts b/packages/react/src/hooks/useAuth.ts
index b4229e29edb..50f8273a14b 100644
--- a/packages/react/src/hooks/useAuth.ts
+++ b/packages/react/src/hooks/useAuth.ts
@@ -28,7 +28,7 @@ export const useAuth = (): UseAuthResult => {
* This util will be used to get current user after those events.
*/
const fetchCurrentUser = React.useCallback(async () => {
- setResult({ user: undefined, isLoading: true, error: undefined });
+ setResult((prevResult) => ({ ...prevResult, isLoading: true }));
try {
// casting the result because `Auth.currentAuthenticateduser` returns `any`