@aws-amplify/auth 6.5.0 → 6.5.1-email-mfa-test.b78433a.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.
- package/dist/cjs/common/AuthErrorStrings.js +2 -2
- package/dist/cjs/common/AuthErrorStrings.js.map +1 -1
- package/dist/cjs/providers/cognito/apis/confirmSignIn.js +2 -2
- package/dist/cjs/providers/cognito/apis/confirmSignIn.js.map +1 -1
- package/dist/cjs/providers/cognito/apis/updateMFAPreference.js +2 -1
- package/dist/cjs/providers/cognito/apis/updateMFAPreference.js.map +1 -1
- package/dist/cjs/providers/cognito/utils/oauth/getRedirectUrl.js +3 -1
- package/dist/cjs/providers/cognito/utils/oauth/getRedirectUrl.js.map +1 -1
- package/dist/cjs/providers/cognito/utils/signInHelpers.js +183 -130
- package/dist/cjs/providers/cognito/utils/signInHelpers.js.map +1 -1
- package/dist/esm/common/AuthErrorStrings.mjs +2 -2
- package/dist/esm/common/AuthErrorStrings.mjs.map +1 -1
- package/dist/esm/foundation/factories/serviceClients/cognitoIdentityProvider/types/sdk.d.ts +24 -3
- package/dist/esm/providers/cognito/apis/confirmSignIn.mjs +2 -2
- package/dist/esm/providers/cognito/apis/confirmSignIn.mjs.map +1 -1
- package/dist/esm/providers/cognito/apis/updateMFAPreference.mjs +2 -1
- package/dist/esm/providers/cognito/apis/updateMFAPreference.mjs.map +1 -1
- package/dist/esm/providers/cognito/types/inputs.d.ts +1 -0
- package/dist/esm/providers/cognito/utils/oauth/getRedirectUrl.mjs +3 -1
- package/dist/esm/providers/cognito/utils/oauth/getRedirectUrl.mjs.map +1 -1
- package/dist/esm/providers/cognito/utils/signInHelpers.d.ts +4 -3
- package/dist/esm/providers/cognito/utils/signInHelpers.mjs +181 -127
- package/dist/esm/providers/cognito/utils/signInHelpers.mjs.map +1 -1
- package/dist/esm/types/models.d.ts +44 -2
- package/package.json +104 -104
- package/src/common/AuthErrorStrings.ts +4 -2
- package/src/foundation/factories/serviceClients/cognitoIdentityProvider/types/sdk.ts +23 -1
- package/src/providers/cognito/apis/confirmSignIn.ts +2 -2
- package/src/providers/cognito/apis/updateMFAPreference.ts +2 -1
- package/src/providers/cognito/types/inputs.ts +1 -0
- package/src/providers/cognito/utils/oauth/getRedirectUrl.ts +1 -0
- package/src/providers/cognito/utils/signInHelpers.ts +240 -166
- 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
|
}
|
|
@@ -39,6 +39,7 @@ export function getRedirectUrl(
|
|
|
39
39
|
// origin + pathname => https://example.com/app
|
|
40
40
|
const isSameOriginAndPathName = (redirect: string) =>
|
|
41
41
|
redirect.startsWith(
|
|
42
|
+
// eslint-disable-next-line no-constant-binary-expression
|
|
42
43
|
String(window.location.origin + window.location.pathname ?? '/'),
|
|
43
44
|
);
|
|
44
45
|
// domain => outlook.live.com, github.com
|
|
@@ -154,49 +154,110 @@ export async function handleMFASetupChallenge({
|
|
|
154
154
|
config,
|
|
155
155
|
}: HandleAuthChallengeRequest): Promise<RespondToAuthChallengeCommandOutput> {
|
|
156
156
|
const { userPoolId, userPoolClientId, userPoolEndpoint } = config;
|
|
157
|
-
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
{
|
|
171
|
-
|
|
183
|
+
|
|
184
|
+
const isTOTPCode = /^\d+$/.test(challengeResponse.trim());
|
|
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
|
+
{ region: getRegionFromUserPoolId(userPoolId) },
|
|
226
|
+
jsonReq,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const isEmail = /^\S+@\S+\.\S+$/.test(challengeResponse.trim());
|
|
231
|
+
|
|
232
|
+
if (isEmail) {
|
|
233
|
+
challengeResponses.EMAIL = challengeResponse;
|
|
234
|
+
|
|
235
|
+
const jsonReq: RespondToAuthChallengeCommandInput = {
|
|
236
|
+
ChallengeName: 'MFA_SETUP',
|
|
237
|
+
ChallengeResponses: challengeResponses,
|
|
172
238
|
Session: session,
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
239
|
+
ClientMetadata: clientMetadata,
|
|
240
|
+
ClientId: userPoolClientId,
|
|
241
|
+
};
|
|
176
242
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
243
|
+
const respondToAuthChallenge = createRespondToAuthChallengeClient({
|
|
244
|
+
endpointResolver: createCognitoUserPoolEndpointResolver({
|
|
245
|
+
endpointOverride: userPoolEndpoint,
|
|
246
|
+
}),
|
|
247
|
+
});
|
|
181
248
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
ClientId: userPoolClientId,
|
|
188
|
-
};
|
|
249
|
+
return respondToAuthChallenge(
|
|
250
|
+
{ region: getRegionFromUserPoolId(userPoolId) },
|
|
251
|
+
jsonReq,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
189
254
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
255
|
+
throw new AuthError({
|
|
256
|
+
name: AuthErrorCodes.SignInException,
|
|
257
|
+
message: `Cannot proceed with MFA setup using challengeResponse: ${challengeResponse}`,
|
|
258
|
+
recoverySuggestion:
|
|
259
|
+
'Try passing "EMAIL", "TOTP", a valid email, or OTP code as the challengeResponse.',
|
|
194
260
|
});
|
|
195
|
-
|
|
196
|
-
return respondToAuthChallenge(
|
|
197
|
-
{ region: getRegionFromUserPoolId(userPoolId) },
|
|
198
|
-
jsonReq,
|
|
199
|
-
);
|
|
200
261
|
}
|
|
201
262
|
|
|
202
263
|
export async function handleSelectMFATypeChallenge({
|
|
@@ -208,7 +269,9 @@ export async function handleSelectMFATypeChallenge({
|
|
|
208
269
|
}: HandleAuthChallengeRequest): Promise<RespondToAuthChallengeCommandOutput> {
|
|
209
270
|
const { userPoolId, userPoolClientId, userPoolEndpoint } = config;
|
|
210
271
|
assertValidationError(
|
|
211
|
-
challengeResponse === 'TOTP' ||
|
|
272
|
+
challengeResponse === 'TOTP' ||
|
|
273
|
+
challengeResponse === 'SMS' ||
|
|
274
|
+
challengeResponse === 'EMAIL',
|
|
212
275
|
AuthValidationErrorCode.IncorrectMFAMethod,
|
|
213
276
|
);
|
|
214
277
|
|
|
@@ -247,88 +310,6 @@ export async function handleSelectMFATypeChallenge({
|
|
|
247
310
|
);
|
|
248
311
|
}
|
|
249
312
|
|
|
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
313
|
export async function handleCompleteNewPasswordChallenge({
|
|
333
314
|
challengeResponse,
|
|
334
315
|
clientMetadata,
|
|
@@ -842,37 +823,65 @@ export async function getSignInResult(params: {
|
|
|
842
823
|
case 'MFA_SETUP': {
|
|
843
824
|
const { signInSession, username } = signInStore.getState();
|
|
844
825
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
826
|
+
const mfaSetupTypes =
|
|
827
|
+
getMFATypes(parseMFATypes(challengeParameters.MFAS_CAN_SETUP)) || [];
|
|
828
|
+
|
|
829
|
+
const allowedMfaSetupTypes = getAllowedMfaSetupTypes(mfaSetupTypes);
|
|
830
|
+
|
|
831
|
+
const isTotpMfaSetupAvailable = allowedMfaSetupTypes.includes('TOTP');
|
|
832
|
+
const isEmailMfaSetupAvailable = allowedMfaSetupTypes.includes('EMAIL');
|
|
833
|
+
|
|
834
|
+
if (isTotpMfaSetupAvailable && isEmailMfaSetupAvailable) {
|
|
835
|
+
return {
|
|
836
|
+
isSignedIn: false,
|
|
837
|
+
nextStep: {
|
|
838
|
+
signInStep: 'CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION',
|
|
839
|
+
allowedMFATypes: allowedMfaSetupTypes,
|
|
840
|
+
},
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
if (isEmailMfaSetupAvailable) {
|
|
845
|
+
return {
|
|
846
|
+
isSignedIn: false,
|
|
847
|
+
nextStep: {
|
|
848
|
+
signInStep: 'CONTINUE_SIGN_IN_WITH_EMAIL_SETUP',
|
|
849
|
+
},
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
if (isTotpMfaSetupAvailable) {
|
|
854
|
+
const associateSoftwareToken = createAssociateSoftwareTokenClient({
|
|
855
|
+
endpointResolver: createCognitoUserPoolEndpointResolver({
|
|
856
|
+
endpointOverride: authConfig.userPoolEndpoint,
|
|
857
|
+
}),
|
|
858
|
+
});
|
|
859
|
+
const { Session, SecretCode: secretCode } =
|
|
860
|
+
await associateSoftwareToken(
|
|
861
|
+
{ region: getRegionFromUserPoolId(authConfig.userPoolId) },
|
|
862
|
+
{
|
|
863
|
+
Session: signInSession,
|
|
864
|
+
},
|
|
865
|
+
);
|
|
866
|
+
|
|
867
|
+
signInStore.dispatch({
|
|
868
|
+
type: 'SET_SIGN_IN_SESSION',
|
|
869
|
+
value: Session,
|
|
851
870
|
});
|
|
852
871
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
Session: signInSession,
|
|
862
|
-
},
|
|
863
|
-
);
|
|
864
|
-
signInStore.dispatch({
|
|
865
|
-
type: 'SET_SIGN_IN_SESSION',
|
|
866
|
-
value: Session,
|
|
867
|
-
});
|
|
872
|
+
return {
|
|
873
|
+
isSignedIn: false,
|
|
874
|
+
nextStep: {
|
|
875
|
+
signInStep: 'CONTINUE_SIGN_IN_WITH_TOTP_SETUP',
|
|
876
|
+
totpSetupDetails: getTOTPSetupDetails(secretCode!, username),
|
|
877
|
+
},
|
|
878
|
+
};
|
|
879
|
+
}
|
|
868
880
|
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
totpSetupDetails: getTOTPSetupDetails(secretCode!, username),
|
|
874
|
-
},
|
|
875
|
-
};
|
|
881
|
+
throw new AuthError({
|
|
882
|
+
name: AuthErrorCodes.SignInException,
|
|
883
|
+
message: `Cannot initiate MFA setup from available types: ${mfaSetupTypes}`,
|
|
884
|
+
});
|
|
876
885
|
}
|
|
877
886
|
case 'NEW_PASSWORD_REQUIRED':
|
|
878
887
|
return {
|
|
@@ -913,6 +922,18 @@ export async function getSignInResult(params: {
|
|
|
913
922
|
signInStep: 'CONFIRM_SIGN_IN_WITH_TOTP_CODE',
|
|
914
923
|
},
|
|
915
924
|
};
|
|
925
|
+
case 'EMAIL_OTP':
|
|
926
|
+
return {
|
|
927
|
+
isSignedIn: false,
|
|
928
|
+
nextStep: {
|
|
929
|
+
signInStep: 'CONFIRM_SIGN_IN_WITH_EMAIL_CODE',
|
|
930
|
+
codeDeliveryDetails: {
|
|
931
|
+
deliveryMedium:
|
|
932
|
+
challengeParameters.CODE_DELIVERY_DELIVERY_MEDIUM as AuthDeliveryMedium,
|
|
933
|
+
destination: challengeParameters.CODE_DELIVERY_DESTINATION,
|
|
934
|
+
},
|
|
935
|
+
},
|
|
936
|
+
};
|
|
916
937
|
case 'ADMIN_NO_SRP_AUTH':
|
|
917
938
|
break;
|
|
918
939
|
case 'DEVICE_PASSWORD_VERIFIER':
|
|
@@ -1000,14 +1021,6 @@ export async function handleChallengeName(
|
|
|
1000
1021
|
const deviceName = options?.friendlyDeviceName;
|
|
1001
1022
|
|
|
1002
1023
|
switch (challengeName) {
|
|
1003
|
-
case 'SMS_MFA':
|
|
1004
|
-
return handleSMSMFAChallenge({
|
|
1005
|
-
challengeResponse,
|
|
1006
|
-
clientMetadata,
|
|
1007
|
-
session,
|
|
1008
|
-
username,
|
|
1009
|
-
config,
|
|
1010
|
-
});
|
|
1011
1024
|
case 'SELECT_MFA_TYPE':
|
|
1012
1025
|
return handleSelectMFATypeChallenge({
|
|
1013
1026
|
challengeResponse,
|
|
@@ -1050,8 +1063,11 @@ export async function handleChallengeName(
|
|
|
1050
1063
|
username,
|
|
1051
1064
|
tokenOrchestrator,
|
|
1052
1065
|
);
|
|
1066
|
+
case 'SMS_MFA':
|
|
1053
1067
|
case 'SOFTWARE_TOKEN_MFA':
|
|
1054
|
-
|
|
1068
|
+
case 'EMAIL_OTP':
|
|
1069
|
+
return handleMFAChallenge({
|
|
1070
|
+
challengeName,
|
|
1055
1071
|
challengeResponse,
|
|
1056
1072
|
clientMetadata,
|
|
1057
1073
|
session,
|
|
@@ -1062,7 +1078,7 @@ export async function handleChallengeName(
|
|
|
1062
1078
|
// TODO: remove this error message for production apps
|
|
1063
1079
|
throw new AuthError({
|
|
1064
1080
|
name: AuthErrorCodes.SignInException,
|
|
1065
|
-
message: `An error occurred during the sign in process.
|
|
1081
|
+
message: `An error occurred during the sign in process.
|
|
1066
1082
|
${challengeName} challengeName returned by the underlying service was not addressed.`,
|
|
1067
1083
|
});
|
|
1068
1084
|
}
|
|
@@ -1070,6 +1086,7 @@ export async function handleChallengeName(
|
|
|
1070
1086
|
export function mapMfaType(mfa: string): CognitoMFAType {
|
|
1071
1087
|
let mfaType: CognitoMFAType = 'SMS_MFA';
|
|
1072
1088
|
if (mfa === 'TOTP') mfaType = 'SOFTWARE_TOKEN_MFA';
|
|
1089
|
+
if (mfa === 'EMAIL') mfaType = 'EMAIL_OTP';
|
|
1073
1090
|
|
|
1074
1091
|
return mfaType;
|
|
1075
1092
|
}
|
|
@@ -1077,6 +1094,7 @@ export function mapMfaType(mfa: string): CognitoMFAType {
|
|
|
1077
1094
|
export function getMFAType(type?: string): AuthMFAType | undefined {
|
|
1078
1095
|
if (type === 'SMS_MFA') return 'SMS';
|
|
1079
1096
|
if (type === 'SOFTWARE_TOKEN_MFA') return 'TOTP';
|
|
1097
|
+
if (type === 'EMAIL_OTP') return 'EMAIL';
|
|
1080
1098
|
// TODO: log warning for unknown MFA type
|
|
1081
1099
|
}
|
|
1082
1100
|
|
|
@@ -1091,15 +1109,10 @@ export function parseMFATypes(mfa?: string): CognitoMFAType[] {
|
|
|
1091
1109
|
return JSON.parse(mfa) as CognitoMFAType[];
|
|
1092
1110
|
}
|
|
1093
1111
|
|
|
1094
|
-
export function
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
)
|
|
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);
|
|
1112
|
+
export function getAllowedMfaSetupTypes(availableMfaSetupTypes: AuthMFAType[]) {
|
|
1113
|
+
return availableMfaSetupTypes.filter(
|
|
1114
|
+
authMfaType => authMfaType === 'EMAIL' || authMfaType === 'TOTP',
|
|
1115
|
+
);
|
|
1103
1116
|
}
|
|
1104
1117
|
|
|
1105
1118
|
export async function assertUserNotAuthenticated() {
|
|
@@ -1230,3 +1243,64 @@ export function getActiveSignInUsername(username: string): string {
|
|
|
1230
1243
|
|
|
1231
1244
|
return state.username ?? username;
|
|
1232
1245
|
}
|
|
1246
|
+
|
|
1247
|
+
export async function handleMFAChallenge({
|
|
1248
|
+
challengeName,
|
|
1249
|
+
challengeResponse,
|
|
1250
|
+
clientMetadata,
|
|
1251
|
+
session,
|
|
1252
|
+
username,
|
|
1253
|
+
config,
|
|
1254
|
+
}: HandleAuthChallengeRequest & {
|
|
1255
|
+
challengeName: Extract<
|
|
1256
|
+
ChallengeName,
|
|
1257
|
+
'EMAIL_OTP' | 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA'
|
|
1258
|
+
>;
|
|
1259
|
+
}) {
|
|
1260
|
+
const { userPoolId, userPoolClientId, userPoolEndpoint } = config;
|
|
1261
|
+
|
|
1262
|
+
const challengeResponses: Record<string, string> = {
|
|
1263
|
+
USERNAME: username,
|
|
1264
|
+
};
|
|
1265
|
+
|
|
1266
|
+
if (challengeName === 'EMAIL_OTP') {
|
|
1267
|
+
challengeResponses.EMAIL_OTP_CODE = challengeResponse;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
if (challengeName === 'SMS_MFA') {
|
|
1271
|
+
challengeResponses.SMS_MFA_CODE = challengeResponse;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
if (challengeName === 'SOFTWARE_TOKEN_MFA') {
|
|
1275
|
+
challengeResponses.SOFTWARE_TOKEN_MFA_CODE = challengeResponse;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
const userContextData = getUserContextData({
|
|
1279
|
+
username,
|
|
1280
|
+
userPoolId,
|
|
1281
|
+
userPoolClientId,
|
|
1282
|
+
});
|
|
1283
|
+
|
|
1284
|
+
const jsonReq: RespondToAuthChallengeCommandInput = {
|
|
1285
|
+
ChallengeName: challengeName,
|
|
1286
|
+
ChallengeResponses: challengeResponses,
|
|
1287
|
+
Session: session,
|
|
1288
|
+
ClientMetadata: clientMetadata,
|
|
1289
|
+
ClientId: userPoolClientId,
|
|
1290
|
+
UserContextData: userContextData,
|
|
1291
|
+
};
|
|
1292
|
+
|
|
1293
|
+
const respondToAuthChallenge = createRespondToAuthChallengeClient({
|
|
1294
|
+
endpointResolver: createCognitoUserPoolEndpointResolver({
|
|
1295
|
+
endpointOverride: userPoolEndpoint,
|
|
1296
|
+
}),
|
|
1297
|
+
});
|
|
1298
|
+
|
|
1299
|
+
return respondToAuthChallenge(
|
|
1300
|
+
{
|
|
1301
|
+
region: getRegionFromUserPoolId(userPoolId),
|
|
1302
|
+
userAgentValue: getAuthUserAgentValue(AuthAction.ConfirmSignIn),
|
|
1303
|
+
},
|
|
1304
|
+
jsonReq,
|
|
1305
|
+
);
|
|
1306
|
+
}
|
package/src/types/models.ts
CHANGED
|
@@ -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;
|