@aws-amplify/auth 6.5.6-unstable.caf00e2.0 → 6.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/dist/cjs/common/AuthErrorStrings.js +2 -2
  2. package/dist/cjs/common/AuthErrorStrings.js.map +1 -1
  3. package/dist/cjs/providers/cognito/apis/confirmSignIn.js +2 -2
  4. package/dist/cjs/providers/cognito/apis/confirmSignIn.js.map +1 -1
  5. package/dist/cjs/providers/cognito/apis/updateMFAPreference.js +2 -1
  6. package/dist/cjs/providers/cognito/apis/updateMFAPreference.js.map +1 -1
  7. package/dist/cjs/providers/cognito/utils/signInHelpers.js +189 -130
  8. package/dist/cjs/providers/cognito/utils/signInHelpers.js.map +1 -1
  9. package/dist/esm/common/AuthErrorStrings.mjs +2 -2
  10. package/dist/esm/common/AuthErrorStrings.mjs.map +1 -1
  11. package/dist/esm/foundation/factories/serviceClients/cognitoIdentityProvider/types/sdk.d.ts +24 -3
  12. package/dist/esm/providers/cognito/apis/confirmSignIn.mjs +2 -2
  13. package/dist/esm/providers/cognito/apis/confirmSignIn.mjs.map +1 -1
  14. package/dist/esm/providers/cognito/apis/updateMFAPreference.mjs +2 -1
  15. package/dist/esm/providers/cognito/apis/updateMFAPreference.mjs.map +1 -1
  16. package/dist/esm/providers/cognito/types/inputs.d.ts +1 -0
  17. package/dist/esm/providers/cognito/utils/signInHelpers.d.ts +4 -3
  18. package/dist/esm/providers/cognito/utils/signInHelpers.mjs +187 -127
  19. package/dist/esm/providers/cognito/utils/signInHelpers.mjs.map +1 -1
  20. package/dist/esm/types/models.d.ts +44 -2
  21. package/package.json +104 -104
  22. package/src/common/AuthErrorStrings.ts +4 -2
  23. package/src/foundation/factories/serviceClients/cognitoIdentityProvider/types/sdk.ts +23 -1
  24. package/src/providers/cognito/apis/confirmSignIn.ts +2 -2
  25. package/src/providers/cognito/apis/updateMFAPreference.ts +2 -1
  26. package/src/providers/cognito/types/inputs.ts +1 -0
  27. package/src/providers/cognito/utils/signInHelpers.ts +246 -166
  28. package/src/types/models.ts +48 -1
@@ -8,6 +8,7 @@ import { MetadataBearer as __MetadataBearer } from '@aws-sdk/types';
8
8
  export type ChallengeName =
9
9
  | 'SMS_MFA'
10
10
  | 'SOFTWARE_TOKEN_MFA'
11
+ | 'EMAIL_OTP'
11
12
  | 'SELECT_MFA_TYPE'
12
13
  | 'MFA_SETUP'
13
14
  | 'PASSWORD_VERIFIER'
@@ -28,7 +29,7 @@ export type ChallengeParameters = {
28
29
  MFAS_CAN_SETUP?: string;
29
30
  } & Record<string, unknown>;
30
31
 
31
- export type CognitoMFAType = 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA';
32
+ export type CognitoMFAType = 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA' | 'EMAIL_OTP';
32
33
 
33
34
  export interface CognitoMFASettings {
34
35
  Enabled?: boolean;
@@ -55,6 +56,7 @@ declare enum ChallengeNameType {
55
56
  SELECT_MFA_TYPE = 'SELECT_MFA_TYPE',
56
57
  SMS_MFA = 'SMS_MFA',
57
58
  SOFTWARE_TOKEN_MFA = 'SOFTWARE_TOKEN_MFA',
59
+ EMAIL_OTP = 'EMAIL_OTP',
58
60
  }
59
61
  declare enum DeliveryMediumType {
60
62
  EMAIL = 'EMAIL',
@@ -1430,6 +1432,10 @@ export interface SetUserMFAPreferenceRequest {
1430
1432
  * <p>The time-based one-time password software token MFA settings.</p>
1431
1433
  */
1432
1434
  SoftwareTokenMfaSettings?: SoftwareTokenMfaSettingsType;
1435
+ /**
1436
+ * <p>The email message multi-factor authentication (MFA) settings.</p>
1437
+ */
1438
+ EmailMfaSettings?: EmailMfaSettingsType;
1433
1439
  /**
1434
1440
  * <p>The access token for the user.</p>
1435
1441
  */
@@ -1538,6 +1544,22 @@ export interface SoftwareTokenMfaSettingsType {
1538
1544
  */
1539
1545
  PreferredMfa?: boolean;
1540
1546
  }
1547
+ /**
1548
+ * <p>The type used for enabling email MFA at the user level. If an MFA type is activated for a user, the user will be prompted for MFA during all sign-in attempts, unless device tracking
1549
+ * is turned on and the device has been trusted. If you want MFA to be applied selectively based on the assessed risk level of sign-in attempts, deactivate MFA for users and turn on Adaptive
1550
+ * Authentication for the user pool.</p>
1551
+ */
1552
+ export interface EmailMfaSettingsType {
1553
+ /**
1554
+ * <p>Specifies whether email MFA is activated. If an MFA type is activated for a user, the user will be prompted for MFA during all sign-in attempts, unless device tracking is turned
1555
+ * on and the device has been trusted.</p>
1556
+ */
1557
+ Enabled?: boolean;
1558
+ /**
1559
+ * <p>Specifies whether email MFA is the preferred MFA method.</p>
1560
+ */
1561
+ PreferredMfa?: boolean;
1562
+ }
1541
1563
  export type UpdateDeviceStatusCommandInput = UpdateDeviceStatusRequest;
1542
1564
  export interface UpdateDeviceStatusCommandOutput
1543
1565
  extends UpdateDeviceStatusResponse,
@@ -71,8 +71,8 @@ export async function confirmSignIn(
71
71
  throw new AuthError({
72
72
  name: AuthErrorCodes.SignInException,
73
73
  message: `
74
- An error occurred during the sign in process.
75
-
74
+ An error occurred during the sign in process.
75
+
76
76
  This most likely occurred due to:
77
77
  1. signIn was not called before confirmSignIn.
78
78
  2. signIn threw an exception.
@@ -27,7 +27,7 @@ import { createCognitoUserPoolEndpointResolver } from '../factories';
27
27
  export async function updateMFAPreference(
28
28
  input: UpdateMFAPreferenceInput,
29
29
  ): Promise<void> {
30
- const { sms, totp } = input;
30
+ const { sms, totp, email } = input;
31
31
  const authConfig = Amplify.getConfig().Auth?.Cognito;
32
32
  assertTokenProviderConfig(authConfig);
33
33
  const { userPoolEndpoint, userPoolId } = authConfig;
@@ -47,6 +47,7 @@ export async function updateMFAPreference(
47
47
  AccessToken: tokens.accessToken.toString(),
48
48
  SMSMfaSettings: getMFASettings(sms),
49
49
  SoftwareTokenMfaSettings: getMFASettings(totp),
50
+ EmailMfaSettings: getMFASettings(email),
50
51
  },
51
52
  );
52
53
  }
@@ -118,6 +118,7 @@ export type SignUpInput = AuthSignUpInput<SignUpOptions<UserAttributeKey>>;
118
118
  export interface UpdateMFAPreferenceInput {
119
119
  sms?: MFAPreference;
120
120
  totp?: MFAPreference;
121
+ email?: MFAPreference;
121
122
  }
122
123
 
123
124
  /**
@@ -154,49 +154,116 @@ export async function handleMFASetupChallenge({
154
154
  config,
155
155
  }: HandleAuthChallengeRequest): Promise<RespondToAuthChallengeCommandOutput> {
156
156
  const { userPoolId, userPoolClientId, userPoolEndpoint } = config;
157
- const challengeResponses = {
157
+
158
+ if (challengeResponse === 'EMAIL') {
159
+ return {
160
+ ChallengeName: 'MFA_SETUP',
161
+ Session: session,
162
+ ChallengeParameters: {
163
+ MFAS_CAN_SETUP: '["EMAIL_OTP"]',
164
+ },
165
+ $metadata: {},
166
+ };
167
+ }
168
+
169
+ if (challengeResponse === 'TOTP') {
170
+ return {
171
+ ChallengeName: 'MFA_SETUP',
172
+ Session: session,
173
+ ChallengeParameters: {
174
+ MFAS_CAN_SETUP: '["SOFTWARE_TOKEN_MFA"]',
175
+ },
176
+ $metadata: {},
177
+ };
178
+ }
179
+
180
+ const challengeResponses: Record<string, string> = {
158
181
  USERNAME: username,
159
182
  };
160
- const verifySoftwareToken = createVerifySoftwareTokenClient({
161
- endpointResolver: createCognitoUserPoolEndpointResolver({
162
- endpointOverride: userPoolEndpoint,
163
- }),
164
- });
165
- const { Session } = await verifySoftwareToken(
166
- {
167
- region: getRegionFromUserPoolId(userPoolId),
168
- userAgentValue: getAuthUserAgentValue(AuthAction.ConfirmSignIn),
169
- },
170
- {
171
- UserCode: challengeResponse,
183
+
184
+ const isTOTPCode = /^\d+$/.test(challengeResponse);
185
+
186
+ if (isTOTPCode) {
187
+ const verifySoftwareToken = createVerifySoftwareTokenClient({
188
+ endpointResolver: createCognitoUserPoolEndpointResolver({
189
+ endpointOverride: userPoolEndpoint,
190
+ }),
191
+ });
192
+
193
+ const { Session } = await verifySoftwareToken(
194
+ {
195
+ region: getRegionFromUserPoolId(userPoolId),
196
+ userAgentValue: getAuthUserAgentValue(AuthAction.ConfirmSignIn),
197
+ },
198
+ {
199
+ UserCode: challengeResponse,
200
+ Session: session,
201
+ FriendlyDeviceName: deviceName,
202
+ },
203
+ );
204
+
205
+ signInStore.dispatch({
206
+ type: 'SET_SIGN_IN_SESSION',
207
+ value: Session,
208
+ });
209
+
210
+ const jsonReq: RespondToAuthChallengeCommandInput = {
211
+ ChallengeName: 'MFA_SETUP',
212
+ ChallengeResponses: challengeResponses,
213
+ Session,
214
+ ClientMetadata: clientMetadata,
215
+ ClientId: userPoolClientId,
216
+ };
217
+
218
+ const respondToAuthChallenge = createRespondToAuthChallengeClient({
219
+ endpointResolver: createCognitoUserPoolEndpointResolver({
220
+ endpointOverride: userPoolEndpoint,
221
+ }),
222
+ });
223
+
224
+ return respondToAuthChallenge(
225
+ {
226
+ region: getRegionFromUserPoolId(userPoolId),
227
+ userAgentValue: getAuthUserAgentValue(AuthAction.ConfirmSignIn),
228
+ },
229
+ jsonReq,
230
+ );
231
+ }
232
+
233
+ const isEmail = challengeResponse.includes('@');
234
+
235
+ if (isEmail) {
236
+ challengeResponses.EMAIL = challengeResponse;
237
+
238
+ const jsonReq: RespondToAuthChallengeCommandInput = {
239
+ ChallengeName: 'MFA_SETUP',
240
+ ChallengeResponses: challengeResponses,
172
241
  Session: session,
173
- FriendlyDeviceName: deviceName,
174
- },
175
- );
242
+ ClientMetadata: clientMetadata,
243
+ ClientId: userPoolClientId,
244
+ };
176
245
 
177
- signInStore.dispatch({
178
- type: 'SET_SIGN_IN_SESSION',
179
- value: Session,
180
- });
246
+ const respondToAuthChallenge = createRespondToAuthChallengeClient({
247
+ endpointResolver: createCognitoUserPoolEndpointResolver({
248
+ endpointOverride: userPoolEndpoint,
249
+ }),
250
+ });
181
251
 
182
- const jsonReq: RespondToAuthChallengeCommandInput = {
183
- ChallengeName: 'MFA_SETUP',
184
- ChallengeResponses: challengeResponses,
185
- Session,
186
- ClientMetadata: clientMetadata,
187
- ClientId: userPoolClientId,
188
- };
252
+ return respondToAuthChallenge(
253
+ {
254
+ region: getRegionFromUserPoolId(userPoolId),
255
+ userAgentValue: getAuthUserAgentValue(AuthAction.ConfirmSignIn),
256
+ },
257
+ jsonReq,
258
+ );
259
+ }
189
260
 
190
- const respondToAuthChallenge = createRespondToAuthChallengeClient({
191
- endpointResolver: createCognitoUserPoolEndpointResolver({
192
- endpointOverride: userPoolEndpoint,
193
- }),
261
+ throw new AuthError({
262
+ name: AuthErrorCodes.SignInException,
263
+ message: `Cannot proceed with MFA setup using challengeResponse: ${challengeResponse}`,
264
+ recoverySuggestion:
265
+ 'Try passing "EMAIL", "TOTP", a valid email, or OTP code as the challengeResponse.',
194
266
  });
195
-
196
- return respondToAuthChallenge(
197
- { region: getRegionFromUserPoolId(userPoolId) },
198
- jsonReq,
199
- );
200
267
  }
201
268
 
202
269
  export async function handleSelectMFATypeChallenge({
@@ -208,7 +275,9 @@ export async function handleSelectMFATypeChallenge({
208
275
  }: HandleAuthChallengeRequest): Promise<RespondToAuthChallengeCommandOutput> {
209
276
  const { userPoolId, userPoolClientId, userPoolEndpoint } = config;
210
277
  assertValidationError(
211
- challengeResponse === 'TOTP' || challengeResponse === 'SMS',
278
+ challengeResponse === 'TOTP' ||
279
+ challengeResponse === 'SMS' ||
280
+ challengeResponse === 'EMAIL',
212
281
  AuthValidationErrorCode.IncorrectMFAMethod,
213
282
  );
214
283
 
@@ -247,88 +316,6 @@ export async function handleSelectMFATypeChallenge({
247
316
  );
248
317
  }
249
318
 
250
- export async function handleSMSMFAChallenge({
251
- challengeResponse,
252
- clientMetadata,
253
- session,
254
- username,
255
- config,
256
- }: HandleAuthChallengeRequest): Promise<RespondToAuthChallengeCommandOutput> {
257
- const { userPoolId, userPoolClientId, userPoolEndpoint } = config;
258
- const challengeResponses = {
259
- USERNAME: username,
260
- SMS_MFA_CODE: challengeResponse,
261
- };
262
- const UserContextData = getUserContextData({
263
- username,
264
- userPoolId,
265
- userPoolClientId,
266
- });
267
- const jsonReq: RespondToAuthChallengeCommandInput = {
268
- ChallengeName: 'SMS_MFA',
269
- ChallengeResponses: challengeResponses,
270
- Session: session,
271
- ClientMetadata: clientMetadata,
272
- ClientId: userPoolClientId,
273
- UserContextData,
274
- };
275
-
276
- const respondToAuthChallenge = createRespondToAuthChallengeClient({
277
- endpointResolver: createCognitoUserPoolEndpointResolver({
278
- endpointOverride: userPoolEndpoint,
279
- }),
280
- });
281
-
282
- return respondToAuthChallenge(
283
- {
284
- region: getRegionFromUserPoolId(userPoolId),
285
- userAgentValue: getAuthUserAgentValue(AuthAction.ConfirmSignIn),
286
- },
287
- jsonReq,
288
- );
289
- }
290
- export async function handleSoftwareTokenMFAChallenge({
291
- challengeResponse,
292
- clientMetadata,
293
- session,
294
- username,
295
- config,
296
- }: HandleAuthChallengeRequest): Promise<RespondToAuthChallengeCommandOutput> {
297
- const { userPoolId, userPoolClientId, userPoolEndpoint } = config;
298
- const challengeResponses = {
299
- USERNAME: username,
300
- SOFTWARE_TOKEN_MFA_CODE: challengeResponse,
301
- };
302
-
303
- const UserContextData = getUserContextData({
304
- username,
305
- userPoolId,
306
- userPoolClientId,
307
- });
308
-
309
- const jsonReq: RespondToAuthChallengeCommandInput = {
310
- ChallengeName: 'SOFTWARE_TOKEN_MFA',
311
- ChallengeResponses: challengeResponses,
312
- Session: session,
313
- ClientMetadata: clientMetadata,
314
- ClientId: userPoolClientId,
315
- UserContextData,
316
- };
317
-
318
- const respondToAuthChallenge = createRespondToAuthChallengeClient({
319
- endpointResolver: createCognitoUserPoolEndpointResolver({
320
- endpointOverride: userPoolEndpoint,
321
- }),
322
- });
323
-
324
- return respondToAuthChallenge(
325
- {
326
- region: getRegionFromUserPoolId(userPoolId),
327
- userAgentValue: getAuthUserAgentValue(AuthAction.ConfirmSignIn),
328
- },
329
- jsonReq,
330
- );
331
- }
332
319
  export async function handleCompleteNewPasswordChallenge({
333
320
  challengeResponse,
334
321
  clientMetadata,
@@ -842,37 +829,65 @@ export async function getSignInResult(params: {
842
829
  case 'MFA_SETUP': {
843
830
  const { signInSession, username } = signInStore.getState();
844
831
 
845
- if (!isMFATypeEnabled(challengeParameters, 'TOTP'))
846
- throw new AuthError({
847
- name: AuthErrorCodes.SignInException,
848
- message: `Cannot initiate MFA setup from available types: ${getMFATypes(
849
- parseMFATypes(challengeParameters.MFAS_CAN_SETUP),
850
- )}`,
832
+ const mfaSetupTypes =
833
+ getMFATypes(parseMFATypes(challengeParameters.MFAS_CAN_SETUP)) || [];
834
+
835
+ const allowedMfaSetupTypes = getAllowedMfaSetupTypes(mfaSetupTypes);
836
+
837
+ const isTotpMfaSetupAvailable = allowedMfaSetupTypes.includes('TOTP');
838
+ const isEmailMfaSetupAvailable = allowedMfaSetupTypes.includes('EMAIL');
839
+
840
+ if (isTotpMfaSetupAvailable && isEmailMfaSetupAvailable) {
841
+ return {
842
+ isSignedIn: false,
843
+ nextStep: {
844
+ signInStep: 'CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION',
845
+ allowedMFATypes: allowedMfaSetupTypes,
846
+ },
847
+ };
848
+ }
849
+
850
+ if (isEmailMfaSetupAvailable) {
851
+ return {
852
+ isSignedIn: false,
853
+ nextStep: {
854
+ signInStep: 'CONTINUE_SIGN_IN_WITH_EMAIL_SETUP',
855
+ },
856
+ };
857
+ }
858
+
859
+ if (isTotpMfaSetupAvailable) {
860
+ const associateSoftwareToken = createAssociateSoftwareTokenClient({
861
+ endpointResolver: createCognitoUserPoolEndpointResolver({
862
+ endpointOverride: authConfig.userPoolEndpoint,
863
+ }),
864
+ });
865
+ const { Session, SecretCode: secretCode } =
866
+ await associateSoftwareToken(
867
+ { region: getRegionFromUserPoolId(authConfig.userPoolId) },
868
+ {
869
+ Session: signInSession,
870
+ },
871
+ );
872
+
873
+ signInStore.dispatch({
874
+ type: 'SET_SIGN_IN_SESSION',
875
+ value: Session,
851
876
  });
852
877
 
853
- const associateSoftwareToken = createAssociateSoftwareTokenClient({
854
- endpointResolver: createCognitoUserPoolEndpointResolver({
855
- endpointOverride: authConfig.userPoolEndpoint,
856
- }),
857
- });
858
- const { Session, SecretCode: secretCode } = await associateSoftwareToken(
859
- { region: getRegionFromUserPoolId(authConfig.userPoolId) },
860
- {
861
- Session: signInSession,
862
- },
863
- );
864
- signInStore.dispatch({
865
- type: 'SET_SIGN_IN_SESSION',
866
- value: Session,
867
- });
878
+ return {
879
+ isSignedIn: false,
880
+ nextStep: {
881
+ signInStep: 'CONTINUE_SIGN_IN_WITH_TOTP_SETUP',
882
+ totpSetupDetails: getTOTPSetupDetails(secretCode!, username),
883
+ },
884
+ };
885
+ }
868
886
 
869
- return {
870
- isSignedIn: false,
871
- nextStep: {
872
- signInStep: 'CONTINUE_SIGN_IN_WITH_TOTP_SETUP',
873
- totpSetupDetails: getTOTPSetupDetails(secretCode!, username),
874
- },
875
- };
887
+ throw new AuthError({
888
+ name: AuthErrorCodes.SignInException,
889
+ message: `Cannot initiate MFA setup from available types: ${mfaSetupTypes}`,
890
+ });
876
891
  }
877
892
  case 'NEW_PASSWORD_REQUIRED':
878
893
  return {
@@ -913,6 +928,18 @@ export async function getSignInResult(params: {
913
928
  signInStep: 'CONFIRM_SIGN_IN_WITH_TOTP_CODE',
914
929
  },
915
930
  };
931
+ case 'EMAIL_OTP':
932
+ return {
933
+ isSignedIn: false,
934
+ nextStep: {
935
+ signInStep: 'CONFIRM_SIGN_IN_WITH_EMAIL_CODE',
936
+ codeDeliveryDetails: {
937
+ deliveryMedium:
938
+ challengeParameters.CODE_DELIVERY_DELIVERY_MEDIUM as AuthDeliveryMedium,
939
+ destination: challengeParameters.CODE_DELIVERY_DESTINATION,
940
+ },
941
+ },
942
+ };
916
943
  case 'ADMIN_NO_SRP_AUTH':
917
944
  break;
918
945
  case 'DEVICE_PASSWORD_VERIFIER':
@@ -1000,14 +1027,6 @@ export async function handleChallengeName(
1000
1027
  const deviceName = options?.friendlyDeviceName;
1001
1028
 
1002
1029
  switch (challengeName) {
1003
- case 'SMS_MFA':
1004
- return handleSMSMFAChallenge({
1005
- challengeResponse,
1006
- clientMetadata,
1007
- session,
1008
- username,
1009
- config,
1010
- });
1011
1030
  case 'SELECT_MFA_TYPE':
1012
1031
  return handleSelectMFATypeChallenge({
1013
1032
  challengeResponse,
@@ -1050,8 +1069,11 @@ export async function handleChallengeName(
1050
1069
  username,
1051
1070
  tokenOrchestrator,
1052
1071
  );
1072
+ case 'SMS_MFA':
1053
1073
  case 'SOFTWARE_TOKEN_MFA':
1054
- return handleSoftwareTokenMFAChallenge({
1074
+ case 'EMAIL_OTP':
1075
+ return handleMFAChallenge({
1076
+ challengeName,
1055
1077
  challengeResponse,
1056
1078
  clientMetadata,
1057
1079
  session,
@@ -1062,7 +1084,7 @@ export async function handleChallengeName(
1062
1084
  // TODO: remove this error message for production apps
1063
1085
  throw new AuthError({
1064
1086
  name: AuthErrorCodes.SignInException,
1065
- message: `An error occurred during the sign in process.
1087
+ message: `An error occurred during the sign in process.
1066
1088
  ${challengeName} challengeName returned by the underlying service was not addressed.`,
1067
1089
  });
1068
1090
  }
@@ -1070,6 +1092,7 @@ export async function handleChallengeName(
1070
1092
  export function mapMfaType(mfa: string): CognitoMFAType {
1071
1093
  let mfaType: CognitoMFAType = 'SMS_MFA';
1072
1094
  if (mfa === 'TOTP') mfaType = 'SOFTWARE_TOKEN_MFA';
1095
+ if (mfa === 'EMAIL') mfaType = 'EMAIL_OTP';
1073
1096
 
1074
1097
  return mfaType;
1075
1098
  }
@@ -1077,6 +1100,7 @@ export function mapMfaType(mfa: string): CognitoMFAType {
1077
1100
  export function getMFAType(type?: string): AuthMFAType | undefined {
1078
1101
  if (type === 'SMS_MFA') return 'SMS';
1079
1102
  if (type === 'SOFTWARE_TOKEN_MFA') return 'TOTP';
1103
+ if (type === 'EMAIL_OTP') return 'EMAIL';
1080
1104
  // TODO: log warning for unknown MFA type
1081
1105
  }
1082
1106
 
@@ -1091,15 +1115,10 @@ export function parseMFATypes(mfa?: string): CognitoMFAType[] {
1091
1115
  return JSON.parse(mfa) as CognitoMFAType[];
1092
1116
  }
1093
1117
 
1094
- export function isMFATypeEnabled(
1095
- challengeParams: ChallengeParameters,
1096
- mfaType: AuthMFAType,
1097
- ): boolean {
1098
- const { MFAS_CAN_SETUP } = challengeParams;
1099
- const mfaTypes = getMFATypes(parseMFATypes(MFAS_CAN_SETUP));
1100
- if (!mfaTypes) return false;
1101
-
1102
- return mfaTypes.includes(mfaType);
1118
+ export function getAllowedMfaSetupTypes(availableMfaSetupTypes: AuthMFAType[]) {
1119
+ return availableMfaSetupTypes.filter(
1120
+ authMfaType => authMfaType === 'EMAIL' || authMfaType === 'TOTP',
1121
+ );
1103
1122
  }
1104
1123
 
1105
1124
  export async function assertUserNotAuthenticated() {
@@ -1230,3 +1249,64 @@ export function getActiveSignInUsername(username: string): string {
1230
1249
 
1231
1250
  return state.username ?? username;
1232
1251
  }
1252
+
1253
+ export async function handleMFAChallenge({
1254
+ challengeName,
1255
+ challengeResponse,
1256
+ clientMetadata,
1257
+ session,
1258
+ username,
1259
+ config,
1260
+ }: HandleAuthChallengeRequest & {
1261
+ challengeName: Extract<
1262
+ ChallengeName,
1263
+ 'EMAIL_OTP' | 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA'
1264
+ >;
1265
+ }) {
1266
+ const { userPoolId, userPoolClientId, userPoolEndpoint } = config;
1267
+
1268
+ const challengeResponses: Record<string, string> = {
1269
+ USERNAME: username,
1270
+ };
1271
+
1272
+ if (challengeName === 'EMAIL_OTP') {
1273
+ challengeResponses.EMAIL_OTP_CODE = challengeResponse;
1274
+ }
1275
+
1276
+ if (challengeName === 'SMS_MFA') {
1277
+ challengeResponses.SMS_MFA_CODE = challengeResponse;
1278
+ }
1279
+
1280
+ if (challengeName === 'SOFTWARE_TOKEN_MFA') {
1281
+ challengeResponses.SOFTWARE_TOKEN_MFA_CODE = challengeResponse;
1282
+ }
1283
+
1284
+ const userContextData = getUserContextData({
1285
+ username,
1286
+ userPoolId,
1287
+ userPoolClientId,
1288
+ });
1289
+
1290
+ const jsonReq: RespondToAuthChallengeCommandInput = {
1291
+ ChallengeName: challengeName,
1292
+ ChallengeResponses: challengeResponses,
1293
+ Session: session,
1294
+ ClientMetadata: clientMetadata,
1295
+ ClientId: userPoolClientId,
1296
+ UserContextData: userContextData,
1297
+ };
1298
+
1299
+ const respondToAuthChallenge = createRespondToAuthChallengeClient({
1300
+ endpointResolver: createCognitoUserPoolEndpointResolver({
1301
+ endpointOverride: userPoolEndpoint,
1302
+ }),
1303
+ });
1304
+
1305
+ return respondToAuthChallenge(
1306
+ {
1307
+ region: getRegionFromUserPoolId(userPoolId),
1308
+ userAgentValue: getAuthUserAgentValue(AuthAction.ConfirmSignIn),
1309
+ },
1310
+ jsonReq,
1311
+ );
1312
+ }
@@ -44,7 +44,7 @@ export interface AuthTOTPSetupDetails {
44
44
  getSetupUri(appName: string, accountName?: string): URL;
45
45
  }
46
46
 
47
- export type AuthMFAType = 'SMS' | 'TOTP';
47
+ export type AuthMFAType = 'SMS' | 'TOTP' | 'EMAIL';
48
48
 
49
49
  export type AuthAllowedMFATypes = AuthMFAType[];
50
50
 
@@ -63,6 +63,20 @@ export interface ContinueSignInWithTOTPSetup {
63
63
  signInStep: 'CONTINUE_SIGN_IN_WITH_TOTP_SETUP';
64
64
  totpSetupDetails: AuthTOTPSetupDetails;
65
65
  }
66
+ export interface ContinueSignInWithEmailSetup {
67
+ /**
68
+ * Auth step requires user to set up EMAIL as multifactor authentication by associating an email address
69
+ * and entering the OTP.
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * // Code retrieved from email
74
+ * const emailAddress = 'example@example.com';
75
+ * await confirmSignIn({challengeResponse: emailAddress });
76
+ * ```
77
+ */
78
+ signInStep: 'CONTINUE_SIGN_IN_WITH_EMAIL_SETUP';
79
+ }
66
80
  export interface ConfirmSignInWithTOTPCode {
67
81
  /**
68
82
  * Auth step requires user to use TOTP as multifactor authentication by retriving an OTP code from authenticator app.
@@ -92,6 +106,21 @@ export interface ContinueSignInWithMFASelection {
92
106
  allowedMFATypes?: AuthAllowedMFATypes;
93
107
  }
94
108
 
109
+ export interface ContinueSignInWithMFASetupSelection {
110
+ /**
111
+ * Auth step requires user to select an mfa option (SMS | TOTP) to setup before continuing the sign-in flow.
112
+ *
113
+ * @example
114
+ * ```typescript
115
+ * await confirmSignIn({challengeResponse:'TOTP'});
116
+ * // OR
117
+ * await confirmSignIn({challengeResponse:'EMAIL'});
118
+ * ```
119
+ */
120
+ signInStep: 'CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION';
121
+ allowedMFATypes?: AuthAllowedMFATypes;
122
+ }
123
+
95
124
  export interface ConfirmSignInWithCustomChallenge {
96
125
  /**
97
126
  * Auth step requires user to respond to a custom challenge.
@@ -146,6 +175,21 @@ export interface ConfirmSignInWithSMSCode {
146
175
  codeDeliveryDetails?: AuthCodeDeliveryDetails;
147
176
  }
148
177
 
178
+ export interface ConfirmSignInWithEmailCode {
179
+ /**
180
+ * Auth step requires user to use EMAIL as multifactor authentication by retrieving a code sent to inbox.
181
+ *
182
+ * @example
183
+ * ```typescript
184
+ * // Code retrieved from email
185
+ * const emailCode = '112233'
186
+ * await confirmSignIn({challengeResponse: emailCode})
187
+ * ```
188
+ */
189
+ signInStep: 'CONFIRM_SIGN_IN_WITH_EMAIL_CODE';
190
+ codeDeliveryDetails?: AuthCodeDeliveryDetails;
191
+ }
192
+
149
193
  export interface ConfirmSignUpStep {
150
194
  /**
151
195
  * Auth step requires to confirm user's sign-up.
@@ -181,7 +225,10 @@ export type AuthNextSignInStep<
181
225
  | ConfirmSignInWithNewPasswordRequired<UserAttributeKey>
182
226
  | ConfirmSignInWithSMSCode
183
227
  | ConfirmSignInWithTOTPCode
228
+ | ConfirmSignInWithEmailCode
184
229
  | ContinueSignInWithTOTPSetup
230
+ | ContinueSignInWithEmailSetup
231
+ | ContinueSignInWithMFASetupSelection
185
232
  | ConfirmSignUpStep
186
233
  | ResetPasswordStep
187
234
  | DoneSignInStep;