furaiev / amazon-cognito-identity-dart-2

Unofficial Amazon Cognito Identity Provider Dart SDK, to easily add user sign-up and sign-in to your mobile and web apps with AWS.
MIT License
187 stars 114 forks source link

User is not confirmed error when sending SMS_MFA #53

Closed Parker-Bergen closed 4 years ago

Parker-Bergen commented 4 years ago

When sending a code for SMS MFA an error is thrown of CognitoUserException: User Confirmation Necessary although the user is already of status confirmed

ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: CognitoUserException: User Confirmation Necessary
E/flutter (13388): #0      CognitoUser.sendMFACode (package:amazon_cognito_identity_dart_2/src/cognito_user.dart:871:7)

I am sending the SMS MFA code that the user receives after they attempt to login. This is automatically sent cause MFA is required on the pool.

furaiev commented 4 years ago

Actually, this is the Congito returns UserConfirmationNecessary.

You can get a session via catches Exception:

} on CognitoUserConfirmationNecessaryException catch (e) {
//  Your code if needed, e.g.
//  await getAndRefreshSession();
  return e.signInUserSession;
} 
Parker-Bergen commented 4 years ago

So I see how that will get me a session but in the before you responded I changed the base code based off the issues that existed on amazon-cogito-identity-dart, I added the && !_signInUserSession.isValid() This may be incorrect but it worked for me. Not sure why it should throw an exception if that is not a true issue.

furaiev commented 4 years ago

Sorry, but I can only guess where you have added this check :) Please share more code.

Parker-Bergen commented 4 years ago

Inside of the file cognito_user.dart in Future<CognitoUserSession> sendMFACode at the very end of the function

final dataConfirm =
        await client.request('ConfirmDevice', confirmDeviceParamsReq);
    _deviceKey = dataAuthenticate['AuthenticationResult']['NewDeviceMetadata']
        ['DeviceKey'];
    await cacheDeviceKeyAndPassword();
    if (dataConfirm['UserConfirmationNecessary'] == true && !_signInUserSession.isValid()) {
      throw CognitoUserConfirmationNecessaryException(
          signInUserSession: _signInUserSession);
    }

    return _signInUserSession;
furaiev commented 4 years ago

I see, in official AWS Amplify (js) they return:

if (dataConfirm.UserConfirmationNecessary === true) {
    return callback.onSuccess(
        this.signInUserSession,
        dataConfirm.UserConfirmationNecessary
    );
}
return callback.onSuccess(this.signInUserSession);

In order to distinguish success and UserConfirmationNecessary success we need to catch UserConfirmationNecessary as exception. In your code UserConfirmationNecessary is hidden. Actually it's almost the same as remove UserConfirmationNecessary checking at all.

Parker-Bergen commented 4 years ago

Hmm ok so I will remove that and catch the exception, but how do I know if the user actually needs to be confirmed?

furaiev commented 4 years ago

This parameter is requested to adjust a parameter setDeviceStatusRemembered or setDeviceStatusNotRemembered, so it's about Device, not about User. Currently, this package doesn't support these methods but they can be easily added if needed.

Parker-Bergen commented 4 years ago

Yes I see those values in the pool when the user logs in. The value is always not_remembered. I take that that is not useful if the persistent data stores the user in the app? It is more of just a value to let admins know whether the user can log in easily from say a website.

furaiev commented 4 years ago

Yeap, for admins, for "remember me" feature, for managing 'pushes', etc.

Parker-Bergen commented 4 years ago

Perfect. Thanks for the understanding and help! And also yes would be cool to have that functionality in the future.