amplify-js: Auth.setupTOTP - Invalid session for the user

Describe the bug I set up Amplify with Cognito authentication with SMS MFA enabled. I have configured my own UI to provide the credentials and it used to work just fine. However, the SMS text messages suddenly stopped being delivered. I could tell the messages were trying to be sent. CloudWatch had the errors logged and said “Phone is currently unreachable/unavailable.” In all the research I’ve done, I haven’t been able to find a reason why. Increased spending limits did not solve the problem. I encountered another issue that sounded similar [#5137] and although it wasn’t technically resolved I decided to implement the workaround from that issue.

The workaround was to implement TOTP MFA. However, I am unable to get it to work. Every time

Auth.setupTOTP(user)

is called, I get this error:

{code: "NotAuthorizedException", name: "NotAuthorizedException", message: "Invalid session for the user."}

This is the CognitoUser object returned from the sign in method provided by Amplify:

{
  Session: "xxxxxx"
  authenticationFlowType: "USER_SRP_AUTH"
  challengeName: "SMS_MFA"
  challengeParam: {CODE_DELIVERY_DELIVERY_MEDIUM: "SMS", CODE_DELIVERY_DESTINATION: "+*******9088"}
  client: Client {endpoint: "https://cognito-idp.us-west-2.amazonaws.com/", userAgent: "aws-amplify/0.1.x js"}
  keyPrefix: "CognitoIdentityServiceProvider.xxx"
  pool: CognitoUserPool {userPoolId: "us-west-2_xxx", clientId: "xxx", client: Client, advancedSecurityDataCollectionFlag: true, storage: Storage}
  signInUserSession: null
  storage: Storage {aws.cognito.identity-id.us-west-2:xxx: "us-west-2:xxx", aws.cognito.identity-id.us-west-2:xxx: "us-west-2:xxx", CognitoIdentityId-us-west-2:xxx: "us-west-2:xxx", CognitoIdentityId-us-west-2:xxx: "us-west-2:xxx", length: 4}
  userDataKey: "CognitoIdentityServiceProvider.xxx.michael.userData"
  username: "michael"
}

The Session variable has a value, but the signInUserSession variable is null. I assume the signInUserSession being null is the reason why this isn’t working.

To Reproduce Steps to reproduce the behavior:

  1. Configure app via amplify cli to have MFA (TOTP) required
  2. Sign up a new user (confirm email and everything)
  3. Sign in newly created user using Auth.signIn
  4. Attempt to set up MFA TOTP via Auth.setupTOTP with the user object returned from the sign in method

Expected behavior Auth.setupTOTP(user) would accept the user object returned from the Auth.signIn(username, password) method to allow MFA TOTP to be configured. With this, the setupMFA method would return a code to generate a QR code for end user to configure their Authenticator application.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 27 (7 by maintainers)

Most upvoted comments

Just wanna confirm that following workflow works: initiateAuth -> associcateSoftwareToken -> VerifySoftwareToken -> RespondToAuthChallenge.

I’m currently seeing this issue.

this topic helped clarify the TOTP MFA registration process for me so thank you for that.

Just wanna confirm that following workflow works: initiateAuth -> associcateSoftwareToken -> VerifySoftwareToken -> RespondToAuthChallenge.

One thing to note: If even after associating a token with a user you are getting “Invalid User Session”, maybe because the user left the app or the session timed out for whatever reason, you just need to let the user generate another token.

for anyone else in a similar situation:

  1. not having time or scope to create a custom UI to process a MFA TOTP for a user registration.
  2. frustrated with cognito hosted ui not having this feature by default in the standard workflow.
  3. no plans to integrate cognito into your already running and validated authentication system.
  4. simply want to MFA auth some alb routes to guard 3rd party tools (like traefik dashboard).

you can hack apart the below (hacks) to fit your needs. in our case we are using cognito to secure alb traffic to a 3rd party app dashboard (traefik) and basic auth just wont side on the security side.

using a python script for secret_hash (because we have app_client secret) and a bash script to init/verify i was able to register MFA. the below is given without warranty and your mileage may very 😃

the trick is getting the output of the bash script into the MFA generator quickly enough to confirm a response before the session ends. if your set up in advance you can copy/paste it + send it to your phone for quick access to enter into google authenticator. you can use any (hopefully more secure) means of doing this then say text message or email.

the starting point for these scripts came from (credits):

  1. python - aws white paper
  2. bash - SO gist link

secrethash.py

import sys
import hmac, hashlib, base64

username = sys.argv[1]
app_client_id = sys.argv[2]
app_client_secret = sys.argv[3]

if (username is None):
  raise Error('username required')

if (app_client_id is None):
  raise Error('app client id required')

if (app_client_secret is None):
  raise Error('app client secret required')

message = bytes(sys.argv[1]+sys.argv[2],'utf-8')
app_client_secret = bytes(sys.argv[3],'utf-8')
secret_hash = base64.b64encode(hmac.new(app_client_secret, message, digestmod=hashlib.sha256).digest()).decode()

print("SECRET HASH:",secret_hash)

initauth.sh

#!/bin/bash

export AWS_PROFILE="<add your profile here>"
debug=false #set to true if you need to see additional output

username="$1"
password="$2"
clientid="$3"
secretHash="$4"
twoFactCode="$5"

callHelp=false
[[ -z \
"${username}" || -z \
"${password}" || -z \
"${clientid}" || -z \
"${secretHash}" ]] &&
  callHelp=true

if [ "${callHelp}" == true ]; then
  cat <<-EOF
  initauth.sh
    First run attempts to establish a MFA secret which you can use to configure a MFA TOTP device.
    Second run requires all the same parameters and also the MFA two factor token to use for verification.

    Once the above two steps are complete you can log in using cognito default hosted UI via TOTP MFA.

    Note: this is set up for app clients with secret keys which require SECRET_HASH variable in the api calls.
    Requires environment parameters:
      1: username, value: '${username}' - the username (not email) of the cognito user
      2: password, value: '${password}' - the user's password
      3: clientid, value: '${clientid}' - the app client id
      4: secretHash, value: '${secretHash}' - the secret key generated in a previous step (see secrethash.py)

    Second run additional parameters:
      5: twoFactCode, value: '${twoFactCode}' - the 6 digit code generated by your authenticator app (google authenticator in my case)
EOF

  exit 1
fi

response=""
if [ -z "${twoFactCode}" ]; then
  response=$(aws cognito-idp initiate-auth --auth-flow USER_PASSWORD_AUTH --client-id $clientid --auth-parameters USERNAME=$username,PASSWORD=$password,SECRET_HASH=$secretHash)
  cName=$(echo "${response}" | jq .ChallengeName | xargs)

  if [ "${debug}" == "true" ]; then
    echo "Response: ${response}"
    echo "ChallengeName: ${cName}"
  fi

  if [ "${cName}" == "MFA_SETUP" ]; then
    session=$(echo "${response}" | jq .Session | xargs)
    tokenResponse=$(aws cognito-idp associate-software-token --session "${session}")
    session=$(echo "${tokenResponse}" | jq .Session | xargs)
    secret=$(echo "${tokenResponse}" | jq .SecretCode | xargs)

    echo
    echo "Token Response: ${tokenResponse}"
    echo "If everything Worked You need to enter this value into google authenticator manually: ${secret}"
    echo
    echo "Once your done & your app is generating two fact codes you will need to execute the script again with the following parameters followed by your new two fact code: ./initauth.sh ${username} ${password} ${clientid} ${session} yourTwoFactCodeHere"
  fi
else
  echo "Attempting Two Factor Verification"

  response=$(aws cognito-idp verify-software-token --session "${secretHash}" --user-code "${twoFactCode}" --friendly-device-name "${username} authenticator app")
  isSuccess=$(echo "${response}" | jq .Status | xargs)

  if [ "${debug}" == "true" ]; then
    echo "Response: ${response}"
    echo "isSuccess: ${isSuccess}"
  fi

  if [ "${isSuccess}" == "SUCCESS" ]; then
    echo "Success code indicated in response. Try to log into cognito, if logged in previously use the logout url to trigger a logout first to expire an old session."
    return 0
  fi
fi

Hi @chrisbonifacio , I’ve used @aws-sdk/client-cognito-identity-provider with the same flow proposed by you and still getting this Invalid session for the user. in the same way as I did with Amplify.

Everything was good unless I call to VerifySoftwareToken using Session returned from InitAuth that is requiring “MFA_SETUP” challenge. I know it’s not an Amplify’s issue. Shall I report the issue in the aws-sdk repo or you can help me somehow?

Hi @chrisbonifacio , thank you for investigation. Let me try to reproduce it with amazon-cognito-identity-js as I’m using it on my backend.