How to enable TOTP software token MFA on Amazon Cognito

How to enable TOTP software token MFA on Amazon Cognito

Yami
Yami

What is Amazon Cognito?

Amazon Cognito is a AWS service provide authentication and authorization with various provider as email/password or social OAuth (Google, Facebook, ...)

What is TOPT?

TOPT is shorthand for Time-based one-time password
It will generate a temporary unique passcode (is generated with a standardized algorithm that uses the current time as an input ) and it is valid in a short time( normally 30 seconds), after that, It will automatically expire.

Enable TOPT Software token MFA feature on Amazon Cognito?

  1. Create a User pool and setting MFA for it like below.

2. Create App client without secret code and make sure that 'ALLOW_USER_SRP_AUTH' is checked.

3. Create a sample user.


Connect Amazon Cognito for setup MFA Software token using Javacript

We will demo how setup MFA and login with MFA by using Javascript Code snippet below:

const prompt = require("prompt");
const qrcode = require("qrcode-terminal");
const AmazonCognitoIdentity = require("amazon-cognito-identity-js");

const CONFIG = {
  UserPoolId: "YOUR_POOL_ID",
  ClientId: "YOUR_CLIENT_ID",
};

main = function () {
  prompt.start();

  const promptSchema = {
    properties: {
      username: { required: true },
      password: { hidden: true },
    },
  };

  prompt.get(promptSchema, function (err, result) {
    let username = result["username"];
    let password = result["password"];

    const poolData = {
      UserPoolId: CONFIG.UserPoolId,
      ClientId: CONFIG.ClientId,
    };

    const userData = {
      Username: username,
      Pool: new AmazonCognitoIdentity.CognitoUserPool(poolData),
    };

    const cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);

    const authenticationData = {
      Username: username,
      Password: password,
    };

    const authenticationDetails =
      new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);

    const totpMfaSettings = {
      PreferredMfa: true,
      Enabled: true,
    };

    // authenticate user
    cognitoUser.authenticateUser(authenticationDetails, {
      onSuccess: function (result) {
        var accessToken = result.getAccessToken().getJwtToken();
        console.info(accessToken);
        cognitoUser.getUserData((err, data) => {
          if (err) {
            alert(err.message || JSON.stringify(err));
            return;
          }
          const { PreferredMfaSetting } = data;
          if (PreferredMfaSetting != "SOFTWARE_TOKEN_MFA") {
            setupMFA();
          }
        });
      },
      newPasswordRequired: function () {
        console.info("Set your password again");
        cognitoUser.completeNewPasswordChallenge(password, {}, this);
      },
      totpRequired: function () {
        console.info("Need to set TOTP");
        let _this = this;
        let getValue = "Input code from Authenticator App";
        prompt.get([getValue], function (err, result) {
          var challengeAnswer = result[getValue];
          cognitoUser.sendMFACode(challengeAnswer, _this, "SOFTWARE_TOKEN_MFA");
        });
      },
      onFailure: function (err) {
        console.log(err.message || JSON.stringify(err));
      },
    });

    const setupMFA = () => {
      cognitoUser.associateSoftwareToken({
        associateSecretCode: function (secretCode) {
          console.info("AssociateSecretCode");
          console.info("Display QR Code");

          const url = `otpauth://totp/${username}?secret=${secretCode}&issuer=Cognito-TOTP-MFA`;
          console.info("Your secret Code: " + secretCode);
          qrcode.generate(url, { small: true });

          const getValue =
            "Open Authenticator App And Scan this image or enter manually";
          prompt.get([getValue], function (err, result) {
            challengeAnswer = result[getValue];
            cognitoUser.verifySoftwareToken(
              result[getValue],
              "My TOTP device",
              {
                onFailure: function (err) {
                  console.error(err.message || JSON.stringify(err));
                },
                onSuccess: function (result) {
                  console.info(result);
                  enableMFA();
                },
              }
            );
          });
        },
        onFailure: function (err) {
          console.error(err.message || JSON.stringify(err));
        },
      });
    };

    const enableMFA = () => {
      cognitoUser.setUserMfaPreference(
        null,
        totpMfaSettings,
        function (err, result) {
          if (err) {
            alert(err.message || JSON.stringify(err));
          }
          console.log("call result " + result);
        }
      );
    };
  });
};

main();

cognito-mfa-auth.js

The result of code execution:

Enable MFA
Login with MFA

TOTP MFA considerations and limitations at currently

  1. AWS Coginito built-in UI currently does not support TOTP setup. Our application must provide this feature. After the user setups and verifies a TOTP software token in our application, they can input their passcode in the built-in UI.
  2. AWS Coginito only supports software token MFA or SMS MFA. It does not support hardware-base MFA.

Thanks you for reading!

Document references:

TOTP software token MFA - Amazon Cognito
Activate TOTP software token MFA.
Time-based one-time password - Wikipedia