Skip to content

Commit 42db720

Browse files
jjarvispisrax
andauthored
feat(auth): add support for Email MFA (#13945)
* [Email MFA] Updating fetchMFAPreference and updateMFAPreference (#13720) * add EMAIL MFA option in fetchMFAPreference * add EMAIL MFA option in updateMFAPreference * update fetchMFAPreference tests * update updateMFAPreference tests * update bundle size * remove redundant assertions * [Email MFA] Add support for EMAIL_OTP during sign in flows (#13745) * Confirm Sign In With Email OTP * Confirm Sign In Tests With Email OTP * Update packages/auth/src/types/models.ts Co-authored-by: israx <[email protected]> * Fix Errant Pascal Casing --------- Co-authored-by: israx <[email protected]> * feat(auth): [EMAIL MFA] Sign In / Confirm Sign In With MFA_SETUP (#13760) * Sign In / Confirm Sign In With MFA_SETUP * Sign In State Management Tests * Confirm Sign In Happy Path Tests * Fix State Management Tests * Apply Feedback * loose email matching * Remove Unnecessary Export * Update SignInHelpers For Getting Allowed MFA Setup Types * Add Error Case Unit Tests * feat(auth): [EMAIL MFA] enable integ tests with backend configuration swapping (#13794) * chore: enable mfa integ tests * chore: add mfa-setup test def * chore: temporarily enable push integ tests * chore: disable push integ tests * chore: address test strategy feedback * chore: use trimmed challenge response * chore: improved naming * chore: update bundle size tests * chore: remove trimmed challenge response --------- Co-authored-by: israx <[email protected]>
1 parent caf00e2 commit 42db720

File tree

17 files changed

+1021
-238
lines changed

17 files changed

+1021
-238
lines changed

.github/integ-config/integ-all.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,3 +870,48 @@ tests:
870870
spec: ssr-context-isolation
871871
yarn_script: ci:ssr-context-isolation
872872
browser: [chrome]
873+
- test_name: integ_next_mfa_req_email
874+
desc: 'mfa required with email sign in attribute'
875+
framework: next
876+
category: auth
877+
sample_name: [mfa]
878+
spec: mfa-req-email
879+
browser: [chrome]
880+
env:
881+
NEXT_PUBLIC_BACKEND_CONFIG: mfa-req-email
882+
- test_name: integ_next_mfa_req_phone
883+
desc: 'mfa required with phone sign in attribute'
884+
framework: next
885+
category: auth
886+
sample_name: [mfa]
887+
spec: mfa-req-phone
888+
browser: [chrome]
889+
env:
890+
NEXT_PUBLIC_BACKEND_CONFIG: mfa-req-phone
891+
- test_name: integ_next_mfa_opt_email
892+
desc: 'mfa optional with email sign in attribute'
893+
framework: next
894+
category: auth
895+
sample_name: [mfa]
896+
spec: mfa-opt-email
897+
browser: [chrome]
898+
env:
899+
NEXT_PUBLIC_BACKEND_CONFIG: mfa-opt-email
900+
- test_name: integ_next_mfa_opt_phone
901+
desc: 'mfa optional with phone sign in attribute'
902+
framework: next
903+
category: auth
904+
sample_name: [mfa]
905+
spec: mfa-opt-phone
906+
browser: [chrome]
907+
env:
908+
NEXT_PUBLIC_BACKEND_CONFIG: mfa-opt-phone
909+
- test_name: integ_next_mfa_setup
910+
desc: 'mfa setup sign in flow'
911+
framework: next
912+
category: auth
913+
sample_name: [mfa]
914+
spec: mfa-setup
915+
browser: [chrome]
916+
env:
917+
NEXT_PUBLIC_BACKEND_CONFIG: mfa-setup

.github/workflows/callable-e2e-test.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ on:
3737
yarn_script:
3838
required: false
3939
type: string
40+
env:
41+
required: false
42+
type: string
4043

4144
env:
4245
AMPLIFY_DIR: /home/runner/work/amplify-js/amplify-js/amplify-js
@@ -84,6 +87,7 @@ jobs:
8487
E2E_RETRY_COUNT: ${{ inputs.retry_count }}
8588
E2E_TEST_NAME: ${{ inputs.test_name }}
8689
E2E_YARN_SCRIPT: ${{ inputs.yarn_script }}
90+
E2E_ENV: ${{ inputs.env }}
8791
run: |
8892
if [ -z "$E2E_YARN_SCRIPT" ]; then
8993
../amplify-js/scripts/retry-yarn-script.sh -s \
@@ -95,7 +99,8 @@ jobs:
9599
$E2E_BROWSER \
96100
dev \
97101
$E2E_BACKEND \
98-
$E2E_AMPLIFY_JS_DIR" \
102+
$E2E_AMPLIFY_JS_DIR \
103+
--env $(echo $E2E_ENV | jq -r 'tostring')" \
99104
$E2E_YARN_SCRIPT \
100105
-n $E2E_RETRY_COUNT
101106
else
@@ -115,6 +120,7 @@ jobs:
115120
E2E_RETRY_COUNT: ${{ inputs.retry_count }}
116121
E2E_TEST_NAME: ${{ inputs.test_name }}
117122
E2E_YARN_SCRIPT: ${{ inputs.yarn_script }}
123+
E2E_ENV: ${{ inputs.env }}
118124
run: |
119125
if [ -z "$E2E_YARN_SCRIPT" ]; then
120126
../amplify-js/scripts/retry-yarn-script.sh -s \
@@ -126,7 +132,8 @@ jobs:
126132
$E2E_BROWSER \
127133
prod \
128134
$E2E_BACKEND \
129-
$E2E_AMPLIFY_JS_DIR" \
135+
$E2E_AMPLIFY_JS_DIR \
136+
--env $(echo $E2E_ENV | jq -r 'tostring')" \
130137
$E2E_YARN_SCRIPT \
131138
-n $E2E_RETRY_COUNT
132139
else

.github/workflows/callable-e2e-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ jobs:
4444
timeout_minutes: ${{ matrix.integ-config.timeout_minutes || 35 }}
4545
retry_count: ${{ matrix.integ-config.retry_count || 3 }}
4646
yarn_script: ${{ matrix.integ-config.yarn_script || '' }}
47+
env: ${{ matrix.integ-config.env && toJSON(matrix.integ-config.env) || '{}' }}
4748

4849
# e2e-test-runner-headless:
4950
# name: E2E test runnner_headless

packages/auth/__tests__/providers/cognito/confirmSignInErrorCases.test.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { AuthValidationErrorCode } from '../../../src/errors/types/validation';
55
import { confirmSignIn } from '../../../src/providers/cognito/apis/confirmSignIn';
66
import { RespondToAuthChallengeException } from '../../../src/providers/cognito/types/errors';
77
import { signInStore } from '../../../src/providers/cognito/utils/signInStore';
8+
import { AuthErrorCodes } from '../../../src/common/AuthErrorStrings';
89
import { createRespondToAuthChallengeClient } from '../../../src/foundation/factories/serviceClients/cognitoIdentityProvider';
910

1011
import { getMockError } from './testUtils/data';
@@ -26,7 +27,7 @@ describe('confirmSignIn API error path cases:', () => {
2627
const signInSession = '1234234232';
2728
const { username } = authAPITestParams.user1;
2829
// assert mocks
29-
const mockStoreGetState = signInStore.getState as jest.Mock;
30+
const mockStoreGetState = jest.mocked(signInStore.getState);
3031
const mockRespondToAuthChallenge = jest.fn();
3132
const mockCreateRespondToAuthChallengeClient = jest.mocked(
3233
createRespondToAuthChallengeClient,
@@ -62,7 +63,7 @@ describe('confirmSignIn API error path cases:', () => {
6263
}
6364
});
6465

65-
it('should throw an error when sign-in step is CONTINUE_SIGN_IN_WITH_MFA_SELECTION and challengeResponse is not "SMS" or "TOTP"', async () => {
66+
it('should throw an error when sign-in step is CONTINUE_SIGN_IN_WITH_MFA_SELECTION and challengeResponse is not "SMS", "TOTP", or "EMAIL"', async () => {
6667
expect.assertions(2);
6768
try {
6869
await confirmSignIn({ challengeResponse: 'NO_SMS' });
@@ -88,4 +89,23 @@ describe('confirmSignIn API error path cases:', () => {
8889
);
8990
}
9091
});
92+
it('should throw an error when sign-in step is MFA_SETUP and challengeResponse is not valid', async () => {
93+
expect.assertions(3);
94+
95+
mockStoreGetState.mockReturnValue({
96+
username,
97+
challengeName: 'MFA_SETUP',
98+
signInSession,
99+
});
100+
101+
try {
102+
await confirmSignIn({
103+
challengeResponse: 'SMS',
104+
});
105+
} catch (err: any) {
106+
expect(err).toBeInstanceOf(AuthError);
107+
expect(err.name).toBe(AuthErrorCodes.SignInException);
108+
expect(err.message).toContain('SMS');
109+
}
110+
});
91111
});

0 commit comments

Comments
 (0)