export function signInActor()

in packages/ui/src/machines/authenticator/actors/signIn.ts [36:519]


export function signInActor({ services }: SignInMachineOptions) {
  return createMachine<SignInContext, AuthEvent>(
    {
      initial: 'init',
      id: 'signInActor',
      states: {
        init: {
          always: [{ target: 'signIn' }],
        },
        signIn: {
          initial: 'edit',
          exit: ['clearFormValues', 'clearTouched'],
          states: {
            edit: {
              entry: sendUpdate(),
              on: {
                SUBMIT: 'submit',
                CHANGE: { actions: 'handleInput' },
                FEDERATED_SIGN_IN: 'federatedSignIn',
              },
            },
            federatedSignIn: {
              tags: ['pending'],
              entry: [sendUpdate(), 'clearError'],
              invoke: {
                src: 'federatedSignIn',
                // getting navigated out anyway, only track errors.
                // onDone: '#signInActor.resolved',
                onError: { actions: 'setRemoteError' },
              },
            },
            submit: {
              tags: ['pending'],
              entry: ['parsePhoneNumber', 'clearError', sendUpdate()],
              invoke: {
                src: 'signIn',
                onDone: [
                  {
                    cond: 'shouldSetupTOTP',
                    actions: ['setUser', 'setChallengeName'],
                    target: '#signInActor.setupTOTP',
                  },
                  {
                    cond: 'shouldConfirmSignIn',
                    actions: ['setUser', 'setChallengeName'],
                    target: '#signInActor.confirmSignIn',
                  },
                  {
                    cond: 'shouldForceChangePassword',
                    actions: [
                      'setUser',
                      'setChallengeName',
                      'setRequiredAttributes',
                    ],
                    target: '#signInActor.forceNewPassword',
                  },
                  {
                    actions: 'setUser',
                    target: 'verifying',
                  },
                ],
                onError: [
                  {
                    cond: 'shouldRedirectToConfirmSignUp',
                    actions: ['setCredentials', 'setConfirmSignUpIntent'],
                    target: 'rejected',
                  },
                  {
                    cond: 'shouldRedirectToConfirmResetPassword',
                    actions: [
                      'setUsernameAuthAttributes',
                      'setConfirmResetPasswordIntent',
                    ],
                    target: 'rejected',
                  },
                  {
                    actions: 'setRemoteError',
                    target: 'edit',
                  },
                ],
              },
            },
            verifying: {
              tags: ['pending'],
              entry: ['clearError', sendUpdate()],
              invoke: {
                src: 'checkVerifiedContact',
                onDone: [
                  {
                    cond: 'shouldRequestVerification',
                    target: '#signInActor.verifyUser',
                    actions: 'setUnverifiedAttributes',
                  },
                  {
                    target: 'resolved',
                  },
                ],
                onError: {
                  actions: 'setRemoteError',
                  target: 'edit',
                },
              },
            },
            resolved: { always: '#signInActor.resolved' },
            rejected: { always: '#signInActor.rejected' },
          },
        },
        confirmSignIn: {
          initial: 'edit',
          exit: ['clearFormValues', 'clearError', 'clearTouched'],
          states: {
            edit: {
              entry: sendUpdate(),
              on: {
                SUBMIT: 'submit',
                SIGN_IN: '#signInActor.signIn',
                CHANGE: { actions: 'handleInput' },
              },
            },
            submit: {
              tags: ['pending'],
              entry: ['clearError', sendUpdate()],
              invoke: {
                src: 'confirmSignIn',
                onDone: {
                  target: '#signInActor.resolved',
                  actions: [
                    'setUser',
                    'clearChallengeName',
                    'clearRequiredAttributes',
                  ],
                },
                onError: {
                  target: 'edit',
                  actions: 'setRemoteError',
                },
              },
            },
          },
        },
        forceNewPassword: {
          type: 'parallel',
          exit: ['clearFormValues', 'clearError', 'clearTouched'],
          states: {
            validation: {
              initial: 'pending',
              states: {
                pending: {
                  invoke: {
                    src: 'validateFields',
                    onDone: {
                      target: 'valid',
                      actions: 'clearValidationError',
                    },
                    onError: {
                      target: 'invalid',
                      actions: 'setFieldErrors',
                    },
                  },
                },
                valid: { entry: sendUpdate() },
                invalid: { entry: sendUpdate() },
              },
              on: {
                CHANGE: {
                  actions: 'handleInput',
                  target: '.pending',
                },
                BLUR: {
                  actions: 'handleBlur',
                  target: '.pending',
                },
              },
            },
            submit: {
              initial: 'idle',
              entry: 'clearError',
              states: {
                idle: {
                  entry: sendUpdate(),
                  on: {
                    SUBMIT: 'validate',
                  },
                },
                validate: {
                  entry: sendUpdate(),
                  invoke: {
                    src: 'validateFields',
                    onDone: {
                      target: 'pending',
                      actions: 'clearValidationError',
                    },
                    onError: {
                      target: 'idle',
                      actions: 'setFieldErrors',
                    },
                  },
                },
                pending: {
                  tags: ['pending'],
                  entry: [sendUpdate(), 'clearError'],
                  invoke: {
                    src: 'forceNewPassword',
                    onDone: [
                      {
                        cond: 'shouldConfirmSignIn',
                        actions: ['setUser', 'setChallengeName'],
                        target: '#signInActor.confirmSignIn',
                      },
                      {
                        cond: 'shouldSetupTOTP',
                        actions: ['setUser', 'setChallengeName'],

                        target: '#signInActor.setupTOTP',
                      },
                      {
                        target: 'resolved',
                        actions: ['setUser', 'setCredentials'],
                      },
                    ],
                    onError: {
                      target: 'idle',
                      actions: 'setRemoteError',
                    },
                  },
                },
                resolved: { type: 'final', always: '#signInActor.resolved' },
              },
            },
          },
        },
        setupTOTP: {
          initial: 'edit',
          exit: ['clearFormValues', 'clearError', 'clearTouched'],
          states: {
            edit: {
              entry: sendUpdate(),
              on: {
                SUBMIT: 'submit',
                SIGN_IN: '#signInActor.signIn',
                CHANGE: { actions: 'handleInput' },
              },
            },
            submit: {
              tags: ['pending'],
              entry: [sendUpdate(), 'clearError'],
              invoke: {
                src: 'verifyTotpToken',
                onDone: {
                  actions: [
                    'setUser',
                    'clearChallengeName',
                    'clearRequiredAttributes',
                  ],
                  target: '#signInActor.resolved',
                },
                onError: {
                  actions: 'setRemoteError',
                  target: 'edit',
                },
              },
            },
          },
        },
        verifyUser: {
          initial: 'edit',
          exit: ['clearFormValues', 'clearError', 'clearTouched'],
          states: {
            edit: {
              entry: sendUpdate(),
              on: {
                SUBMIT: 'submit',
                SKIP: '#signInActor.resolved',
                CHANGE: { actions: 'handleInput' },
              },
            },
            submit: {
              tags: ['pending'],
              entry: 'clearError',
              invoke: {
                src: 'verifyUser',
                onDone: {
                  target: '#signInActor.confirmVerifyUser',
                },
                onError: {
                  actions: 'setRemoteError',
                  target: 'edit',
                },
              },
            },
          },
        },
        confirmVerifyUser: {
          initial: 'edit',
          exit: [
            'clearFormValues',
            'clearError',
            'clearUnverifiedAttributes',
            'clearAttributeToVerify',
            'clearTouched',
          ],
          states: {
            edit: {
              entry: sendUpdate(),
              on: {
                SUBMIT: 'submit',
                SKIP: '#signInActor.resolved',
                CHANGE: { actions: 'handleInput' },
              },
            },
            submit: {
              tags: ['pending'],
              entry: 'clearError',
              invoke: {
                src: 'confirmVerifyUser',
                onDone: {
                  target: '#signInActor.resolved',
                },
                onError: {
                  actions: 'setRemoteError',
                  target: 'edit',
                },
              },
            },
          },
        },
        resolved: {
          type: 'final',
          data: (context) => ({
            user: context.user,
          }),
        },
        rejected: {
          type: 'final',
          data: (context, event) => {
            return {
              intent: context.redirectIntent,
              authAttributes: context.authAttributes,
            };
          },
        },
      },
    },
    {
      actions: {
        clearAttributeToVerify,
        clearChallengeName,
        clearRequiredAttributes,
        clearError,
        clearFormValues,
        clearTouched,
        clearUnverifiedAttributes,
        clearValidationError,
        handleInput,
        handleBlur,
        parsePhoneNumber,
        setChallengeName,
        setConfirmResetPasswordIntent,
        setConfirmSignUpIntent,
        setRequiredAttributes,
        setCredentials,
        setFieldErrors,
        setRemoteError,
        setUnverifiedAttributes,
        setUser,
        setUsernameAuthAttributes,
      },
      guards: {
        shouldConfirmSignIn: (_, event): boolean => {
          const challengeName = get(event, 'data.challengeName');
          const validChallengeNames = [
            AuthChallengeNames.SMS_MFA,
            AuthChallengeNames.SOFTWARE_TOKEN_MFA,
          ];

          return validChallengeNames.includes(challengeName);
        },
        shouldRedirectToConfirmSignUp: (_, event): boolean => {
          return event.data.code === 'UserNotConfirmedException';
        },
        shouldRedirectToConfirmResetPassword: (_, event): boolean => {
          return event.data.code === 'PasswordResetRequiredException';
        },
        shouldSetupTOTP: (_, event): boolean => {
          const challengeName = get(event, 'data.challengeName');

          return challengeName === AuthChallengeNames.MFA_SETUP;
        },
        shouldForceChangePassword: (_, event): boolean => {
          const challengeName = get(event, 'data.challengeName');

          return challengeName === AuthChallengeNames.NEW_PASSWORD_REQUIRED;
        },
        shouldRequestVerification: (_, event): boolean => {
          const { unverified, verified } = event.data;

          return isEmpty(verified) && !isEmpty(unverified);
        },
      },
      services: {
        async signIn(context) {
          const { username, password } = context.formValues;

          return await services.handleSignIn({
            username,
            password,
          });
        },
        async confirmSignIn(context, event) {
          const { challengeName, user } = context;
          const { confirmation_code: code } = context.formValues;

          let mfaType;
          if (
            challengeName === AuthChallengeNames.SMS_MFA ||
            challengeName === AuthChallengeNames.SOFTWARE_TOKEN_MFA
          ) {
            mfaType = challengeName;
          }

          return await services.handleConfirmSignIn({ user, code, mfaType });
        },
        async forceNewPassword(context, event) {
          const { user, formValues } = context;
          let {
            password,
            confirm_password,
            phone_number,
            country_code,
            ...rest
          } = formValues;

          let phoneNumberWithCountryCode;
          if (phone_number) {
            phoneNumberWithCountryCode =
              `${country_code}${phone_number}`.replace(/[^A-Z0-9+]/gi, '');
            rest = { ...rest, phone_number: phoneNumberWithCountryCode };
          }

          return Auth.completeNewPassword(user, password, rest);
        },
        async verifyTotpToken(context, event) {
          const { user } = context;
          const { confirmation_code } = context.formValues;

          return Auth.verifyTotpToken(user, confirmation_code);
        },
        async federatedSignIn(_, event) {
          const { provider } = event.data;

          return await Auth.federatedSignIn({ provider });
        },
        async checkVerifiedContact(context, event) {
          const { user } = context;
          const result = await Auth.verifiedContact(user);

          return result;
        },
        async verifyUser(context, event) {
          const { unverifiedAttr } = context.formValues;
          const result = await Auth.verifyCurrentUserAttribute(unverifiedAttr);

          context.attributeToVerify = unverifiedAttr;

          return result;
        },
        async confirmVerifyUser(context, event) {
          const { attributeToVerify } = context;
          const { confirmation_code } = context.formValues;

          return await Auth.verifyCurrentUserAttributeSubmit(
            attributeToVerify,
            confirmation_code
          );
        },
        async validateFields(context, event) {
          return runValidators(context.formValues, context.touched, [
            defaultServices.validateConfirmPassword,
          ]);
        },
      },
    }
  );
}