Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to await for the aws cognito authenticateUser call (which appears to be a callback)

So we are using await/async calls, but the authenticateUser command provided by aws-amplify appears to use a callback. I am a python coder and have not coded with node in quite a while, so this may be a naive question!

I tried converting it to a promise like so:

function authenticateUserAsync(user, authDetails) {
    return new Promise(function(resolve, reject, challenge) {
          user.authenticateUser(authDetails, { 
                          onSuccess: resolve,
                          onFailure: reject, 
                          newPasswordRequired: challenge });

    }).then(
        function(result) { 
           return result; 
        });
}

and later

 idToken = await authenticateUserAsync(user, authDetails,
    function(result) {
        console.log("Token: ");
        console.log(result.idToken);
        return result.idToken;
    },
    function(err) {
        console.log(err);
        this.setState({idToken: ''});
        if (err.code == 'NotAuthorizedException') {
                return 'not_authorized';
        } else {
                return 'unknown_error';
        }

    },
    function(userAttrs, reqAttrs) {
        return 'challenge';
    } 
  );

But, no matter how I tweak it, the code flows right on by and then I get an unhandled promise rejection (in my test the auth fails at the moment)

like image 698
Golda Velez Avatar asked Jan 03 '23 11:01

Golda Velez


1 Answers

Good on you for trying to implement a call to authenticateUser() with modern-day Javascript constructs, but there are several issues with your approach. I'd be really curious to see the finished code.

The main issue is that Cognito authenticateUser() needs three callbacks and promises only handle two. A dummy function can be passed for the newPasswordRequired callback, if you never expect to hit that code-path. Another approach is to use the resolve function for both the onSuccess and the newPasswordRequired callbacks.

A second issue is that your authenticateUserAsync() only takes two parameters. You are trying to pass some additional callbacks to it. Those callbacks are ignored. That is why it flows right on by and you get an unhandled promise exception. The unnecessary .then() doesn't help either.

My implementation ended up like this:

function asyncAuthenticateUser(cognitoUser, cognitoAuthenticationDetails) {
  return new Promise(function(resolve, reject) {
    cognitoUser.authenticateUser(cognitoAuthenticationDetails, {
      onSuccess: resolve,
      onFailure: reject,
      newPasswordRequired: resolve
    })
  })
}

async signIn({ commit }, authData) {
  let cognitoUserPool = new CognitoUserPool(config.poolDetails)
  let cognitoAuthenticationDetails = new AuthenticationDetails(authData);
  let userData = { Username: authData.Username, Pool: cognitoUserPool }
  let cognitoUser = new CognitoUser(userData)

  try {
    let result =
      await asyncAuthenticateUser(cognitoUser, cognitoAuthenticationDetails)

    if ('idToken' in result) {
      console.log('We have a token:  ' + JSON.stringify(p));
    }
    else {
      console.log('We need a new password.')
      delete result.email_verified // Not accepted by the challenge call
      delete result.phone_number_verified // Also not accepted

      // Get a new password from the user then call
      // cognitoUser.completeNewPasswordChallenge()
    }
    catch (error) {
      // Probably a mis-typed password
      console.log(error.message)
    }
}

An alternative library to amazon-cognito-identity-js, which uses common ES6 concepts, like async/await would be welcome.

like image 189
Mark Avatar answered Jan 05 '23 01:01

Mark