@aws-amplify/ui
Version:
`@aws-amplify/ui` contains low-level logic & styles for stand-alone usage or re-use in framework-specific implementations.
265 lines (262 loc) • 9.4 kB
JavaScript
import { actions } from 'xstate';
import '@aws-amplify/core/internals/utils';
import 'aws-amplify/utils';
import '../../utils/setUserAgent/constants.mjs';
import { translate, DefaultTexts } from '../../i18n/translations.mjs';
import '../../types/authenticator/user.mjs';
import '../../types/authenticator/attributes.mjs';
import { trimValues } from '../../helpers/authenticator/utils.mjs';
import '../../helpers/accountSettings/utils.mjs';
import 'aws-amplify';
import { sanitizePhoneNumber, getUsernameSignUp } from './utils.mjs';
const { assign } = actions;
const clearActorDoneData = assign({ actorDoneData: undefined });
const clearChallengeName = assign({ challengeName: undefined });
const clearMissingAttributes = assign({ missingAttributes: undefined });
const clearError = assign({ remoteError: undefined });
const clearFormValues = assign({
formValues: (context) => ({
// Preserve username for passwordless flows to avoid "username is required" errors
username: context.formValues?.username,
}),
});
const clearTouched = assign({ touched: {} });
const clearUser = assign({ user: undefined });
const clearValidationError = assign({ validationError: {} });
/**
* "set" actions
*/
const setTotpSecretCode = assign({
totpSecretCode: (_, { data }) => {
const { sharedSecret } = (data.nextStep?.totpSetupDetails ??
{});
return sharedSecret;
},
});
const setAllowedMfaTypes = assign({
allowedMfaTypes: (_, { data }) => {
return data.nextStep?.allowedMFATypes;
},
});
const setSignInStep = assign({ step: 'SIGN_IN' });
const setShouldVerifyUserAttributeStep = assign({
step: 'SHOULD_CONFIRM_USER_ATTRIBUTE',
});
const setConfirmAttributeCompleteStep = assign({
step: 'CONFIRM_ATTRIBUTE_COMPLETE',
});
// map v6 `signInStep` to v5 `challengeName`
const setChallengeName = assign({
challengeName: (_, { data }) => {
const { signInStep } = data.nextStep;
switch (signInStep) {
case 'CONFIRM_SIGN_IN_WITH_SMS_CODE':
return 'SMS_MFA';
case 'CONFIRM_SIGN_IN_WITH_TOTP_CODE':
return 'SOFTWARE_TOKEN_MFA';
case 'CONFIRM_SIGN_IN_WITH_EMAIL_CODE':
return 'EMAIL_OTP';
case 'CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION':
case 'CONTINUE_SIGN_IN_WITH_EMAIL_SETUP':
case 'CONTINUE_SIGN_IN_WITH_TOTP_SETUP':
return 'MFA_SETUP';
case 'CONTINUE_SIGN_IN_WITH_MFA_SELECTION':
return 'SELECT_MFA_TYPE';
default:
return undefined;
}
},
});
const setUsernameForgotPassword = assign({
username: ({ formValues, loginMechanisms }) => {
const loginMechanism = loginMechanisms[0];
const { username, country_code } = formValues;
if (loginMechanism === 'phone_number') {
// forgot password `formValues` uses `username` for base phone number value
// prefix `country_code` for full `username`
return sanitizePhoneNumber(country_code, username);
}
// default username field for loginMechanism === 'email' is "username" for SignIn
return username;
},
});
const setUsernameSignUp = assign({ username: getUsernameSignUp });
const setUsernameSignIn = assign({
username: ({ formValues, loginMechanisms }) => {
const loginMechanism = loginMechanisms[0];
const { username, country_code } = formValues;
if (loginMechanism === 'phone_number') {
// sign in `formValues` uses `username` for base phone number value
// prefix `country_code` for full `username`
return sanitizePhoneNumber(country_code, username);
}
// return `email` and `username`
return username;
},
});
const setNextSignInStep = assign({
step: (_, { data }) => data.nextStep.signInStep === 'DONE'
? 'SIGN_IN_COMPLETE'
: data.nextStep.signInStep,
});
const setNextSignUpStep = assign({
step: (_, { data }) => data.nextStep.signUpStep === 'DONE'
? 'SIGN_UP_COMPLETE'
: data.nextStep.signUpStep,
});
const setNextResetPasswordStep = assign({
step: (_, { data }) => data.nextStep.resetPasswordStep === 'DONE'
? 'RESET_PASSWORD_COMPLETE'
: data.nextStep.resetPasswordStep,
});
const setMissingAttributes = assign({
missingAttributes: (_, { data }) => data.nextStep?.missingAttributes,
});
const setFieldErrors = assign({
validationError: (_, { data }) => data,
});
const setRemoteError = assign({
remoteError: (_, { data }) => {
if (data.name === 'NoUserPoolError') {
return `Configuration error (see console) – please contact the administrator`;
}
const message = data?.message || '';
// Handle USER_AUTH flow not enabled error
if (message.includes('USER_AUTH flow not enabled')) {
return translate(DefaultTexts.PASSWORDLESS_NOT_ENABLED);
}
// Handle cannot send code error
if (message.includes('Cannot send code to either')) {
return translate(DefaultTexts.CODE_DELIVERY_FAILED);
}
// Handle invalid/wrong verification code
if (message.includes('Invalid code or auth state')) {
return translate(DefaultTexts.VERIFICATION_CODE_INVALID);
}
// Handle expired verification code
if (message.includes('session is expired')) {
return translate(DefaultTexts.VERIFICATION_CODE_EXPIRED);
}
// Handle passkey authentication canceled
if (message.includes('ceremony has been canceled')) {
return translate(DefaultTexts.PASSKEY_AUTHENTICATION_CANCELED);
}
return message || data;
},
});
const setUser = assign({ user: (_, { data }) => data });
const resolveCodeDeliveryDetails = (details) => ({
Destination: details.destination,
DeliveryMedium: details.deliveryMedium,
AttributeName: details.attributName,
});
const setCodeDeliveryDetails = assign({
codeDeliveryDetails: (_, { data }) => {
if (data
?.nextStep?.codeDeliveryDetails) {
return resolveCodeDeliveryDetails(data
.nextStep.codeDeliveryDetails);
}
return resolveCodeDeliveryDetails(data);
},
});
const handleInput = assign({
formValues: (context, { data }) => {
const { name, value } = data;
return { ...context['formValues'], [name]: value };
},
});
const handleSubmit = assign({
formValues: (context, { data }) =>
// do not trim password
trimValues({ ...context['formValues'], ...data }, 'password'),
});
const handleBlur = assign({
touched: (context, { data }) => ({
...context['touched'],
[data.name]: true,
}),
});
const setUnverifiedUserAttributes = assign({
unverifiedUserAttributes: (context, { data }) => {
// Use fetchedUserAttributes from context if data is not provided
const attributes = data || context.fetchedUserAttributes;
if (!attributes)
return {};
const { email, phone_number } = attributes;
const unverifiedUserAttributes = {
...(email && { email }),
...(phone_number && { phone_number }),
};
return unverifiedUserAttributes;
},
});
const clearSelectedUserAttribute = assign({ selectedUserAttribute: undefined });
const setSelectedUserAttribute = assign({
selectedUserAttribute: (context) => context.formValues?.unverifiedAttr,
});
// Maps to unexposed `ConfirmSignUpSignUpStep`
const setConfirmSignUpSignUpStep = assign({ step: 'CONFIRM_SIGN_UP' });
// Passwordless actions
const setSelectedAuthMethod = assign({
selectedAuthMethod: (_, { data }) => data.method,
});
const setSelectedAuthMethodFromForm = assign({
selectedAuthMethod: (_, { data }) => {
// Extract method from form data if present, default to PASSWORD for form submissions
return data?.__authMethod || 'PASSWORD';
},
});
const setSelectAuthMethodStep = assign({
step: 'SELECT_AUTH_METHOD',
});
const setFetchedUserAttributes = assign({
fetchedUserAttributes: (_, event) => event.data,
});
const setHasExistingPasskeys = assign({
hasExistingPasskeys: (_, event) => event.data,
});
const clearHasExistingPasskeys = assign({
hasExistingPasskeys: false,
});
const ACTIONS = {
clearActorDoneData,
clearChallengeName,
clearError,
clearFormValues,
clearHasExistingPasskeys,
clearMissingAttributes,
clearSelectedUserAttribute,
clearTouched,
clearUser,
clearValidationError,
handleBlur,
handleInput,
handleSubmit,
setAllowedMfaTypes,
setChallengeName,
setCodeDeliveryDetails,
setFetchedUserAttributes,
setFieldErrors,
setHasExistingPasskeys,
setMissingAttributes,
setNextResetPasswordStep,
setNextSignInStep,
setNextSignUpStep,
setRemoteError,
setConfirmAttributeCompleteStep,
setConfirmSignUpSignUpStep,
setSelectAuthMethodStep,
setSelectedAuthMethod,
setSelectedAuthMethodFromForm,
setShouldVerifyUserAttributeStep,
setSelectedUserAttribute,
setSignInStep,
setTotpSecretCode,
setUser,
setUnverifiedUserAttributes,
setUsernameForgotPassword,
setUsernameSignIn,
setUsernameSignUp,
};
export { ACTIONS as default };