Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AWS Cognito/React.js newPasswordRequired Challenge

I'm working on a login flow for my web app built in React and I'm using AWS Cognito for user management. I'm working on the login flow, and I have a use case where a user is created via the AWS Console and a temporary password is provided to the user. When the user goes to login to my application for the first time, AWS Cognito returns a newPasswordRequired Challenge, and the user is forced to change their password.

I'm using the amazon-cognito-identity-js API to authenticate the user. The docs for that can be found here. I have the newPasswordRequired callback function setup just like the docs instruct, but I'm struggling to figure out the best way to gather the new password from the user using React within the newPasswordRequiredfunction. I initially used prompt() within the function to get the inputs, but I would like the app to flow to a new page where the user can enter a new password, confirm new password, and login to the app. That new page should be able to call the cognitoUser.completeNewPasswordChallenge() that is required to update the new password. Please HELP! Here's my code below:

onFormSubmission = (username, password) => {
    const poolData = {
      UserPoolId : AWSConfig.cognito.USER_POOL_ID,
      ClientId : AWSConfig.cognito.APP_CLIENT_ID
    }
    const userPool = new CognitoUserPool(poolData);
    const userData = {
      Username: username,
      Pool: userPool
    }
    const authenticationData = {
        Username : username,
        Password : password
    }
    const authenticationDetails = new AuthenticationDetails(authenticationData);

    const cognitoUser = new CognitoUser(userData);

    cognitoUser.authenticateUser(authenticationDetails, {
      onSuccess: function (result) {
          console.log('access token + ' + result.getAccessToken().getJwtToken());
          /*Use the idToken for Logins Map when Federating User Pools with identity pools or when passing through an Authorization Header to an API Gateway Authorizer*/
          console.log('idToken + ' + result.idToken.jwtToken);
      },
      onFailure: function(err) {
        console.log(err);
     },
     newPasswordRequired: function(userAttributes, requiredAttributes) {
          // User was signed up by an admin and must provide new
          // password and required attributes, if any, to complete
          // authentication.

          // userAttributes: object, which is the user's current profile. It will list all attributes that are associated with the user.
          // Required attributes according to schema, which don’t have any values yet, will have blank values.
          // requiredAttributes: list of attributes that must be set by the user along with new password to complete the sign-in.

*** THIS IS WHERE I WANT REACT TO RENDER A NEW PAGE TO GET THE NEW PASSWORD***

          // Get these details and call
          // newPassword: password that user has given
          // attributesData: object with key as attribute name and value that the user has given.
          cognitoUser.completeNewPasswordChallenge(pw, userAttributes, this);
      }
    });
  }

  render() {
    return (
      <div>
        <LoginScreenComponent isInvalidForm={this.state.isInvalidForm} onFormSubmission={this.onFormSubmission}/>
      </div>
    )
  }
like image 851
Dylan Terrell Avatar asked Jun 29 '18 14:06

Dylan Terrell


1 Answers

I had exactly the same problem! Here is my solution:

Login.js react container can render two different components. <NewPassswordForm /> is to ask a new password, <LoginForm /> is for common login. According to isFirstLogin flag you decide which one to render.

Since you have the cognito user in this.state.user you can use it to call completeNewPasswordChallenge to finish the login flow:

handleLogin = (username, password) => {
  const authDetails = new AuthenticationDetails({
    Username: username,
    Password: password,
  });
  const userData = {
    Username: username,
    Pool: getUserPool(),
    Storage: getStorage(),
  };
  const cognitoUser = new CognitoUser(userData);
  cognitoUser.authenticateUser(authDetails, {
    onSuccess: () => {
      // login
    }
    newPasswordRequired: userAttr => {
      this.setState({
        isFirstLogin: true,
        user: cognitoUser,
        userAttr: userAttr,
      });
    },
  });
};

changePassword = (newPassword) => {
  const cognitoUser = this.state.user;
  const userAttr = this.state.userAttr;
  cognitoUser.completeNewPasswordChallenge(newPassword, userAttr, {
    onSuccess: result => {
      // login
    }
  });
};

render() {
  return (
    <div>
      {this.state.isFirstLogin ? (
        <NewPassswordForm changePassword={this.changePassword} />
      ) : (
        <LoginForm handleLogin={this.handleLogin} />
      )}
    </div>
  );
}
like image 129
froston Avatar answered Oct 31 '22 19:10

froston