35

I am trying to implement authentication built on Cognito using their Go SDK. I have been able to get basic username/password authentication to work, but when I add in 2-factor authentication using SMS I am getting stuck.

Steps to reproduce :

  1. I create the user with a username/password and email verification
  2. I validate the email address
  3. I set the phone number and request a verification code
  4. I verify the phone number
  5. I enable 2-factor authentication (via SMS)
  6. I try to sign in and receive the SMS_MFA challenge
  7. I receive the code on my phone and call AdminRespondToAuthChallenge

Problem, I receive an error :

CodeMismatchException: Invalid code or auth state for the user.
status code: 400, request id: 1513894e-8efa-11e8-a8f8-97e5e083c03b

The SMS verification code is certainly correct, so it seems that it must be something to do with the auth state.

The calls to Cognito look like this:

c.cip.SignUp(&cognitoidentityprovider.SignUpInput{
        ClientId: aws.String(c.clientID),
        Username: aws.String(username),
        Password: aws.String(password),
        UserAttributes: []*cognitoidentityprovider.AttributeType{
            {
                Name:  aws.String("email"),
                Value: aws.String(email),
            },
            {
                Name:  aws.String("name"),
                Value: aws.String(fullName),
            },
        },
    })

c.cip.ConfirmSignUp(&cognitoidentityprovider.ConfirmSignUpInput{
    ClientId:         aws.String(c.clientID),
    Username:         aws.String(username),
    ConfirmationCode: aws.String(code),
})


//Add the phone number
c.cip.AdminUpdateUserAttributes(&cognitoidentityprovider.AdminUpdateUserAttributesInput{
            UserPoolId: aws.String(c.userPoolID),
            Username:   aws.String(username),
            UserAttributes: []*cognitoidentityprovider.AttributeType{
                {
                    Name:  aws.String("phone_number"),
                    Value: aws.String(phoneNumber),
                },
            },
        })

//Request a verification code
c.cip.GetUserAttributeVerificationCode(&cognitoidentityprovider.GetUserAttributeVerificationCodeInput{
    AccessToken:   aws.String(accessToken),
    AttributeName: aws.String("phone_number"),
})

//Verify the phone number
c.cip.VerifyUserAttribute(&cognitoidentityprovider.VerifyUserAttributeInput{
    AccessToken:   aws.String(accessToken),
    AttributeName: aws.String("phone_number"),
    Code:          aws.String(code),
})

//Enable SMS 2-factor auth c.cip.AdminSetUserSettings(&cognitoidentityprovider.AdminSetUserSettingsInput{
    UserPoolId: aws.String(c.userPoolID),
    Username:   aws.String(username),
    MFAOptions: []*cognitoidentityprovider.MFAOptionType{
        &cognitoidentityprovider.MFAOptionType{
            AttributeName:  aws.String("phone_number"),
            DeliveryMedium: aws.String("SMS"),
        },
    },
})

c.cip.AdminInitiateAuth(&cognitoidentityprovider.AdminInitiateAuthInput{
    ClientId:   aws.String(c.clientID),
    UserPoolId: aws.String(c.userPoolID),
    AuthFlow:   aws.String("ADMIN_NO_SRP_AUTH"),
    AuthParameters: map[string]*string{
        "USERNAME": aws.String(username),
        "PASSWORD": aws.String(password),
    },
})

c.cip.AdminRespondToAuthChallenge(&cognitoidentityprovider.AdminRespondToAuthChallengeInput{
        ClientId:      aws.String(c.clientID),
        UserPoolId:    aws.String(c.userPoolID),
        ChallengeName: aws.String("SMS_MFA"),
        Session:       aws.String(session),
        ChallengeResponses: map[string]*string{
            "USERNAME":     aws.String(username),
            "SMS_MFA_CODE": aws.String(code),
        },
    })

Doing a GetUser call shows the current state of the user:

User = {
              Enabled: true,
              MFAOptions: [{
                  AttributeName: "phone_number",
                  DeliveryMedium: "SMS"
                }],
              PreferredMfaSetting: "SMS_MFA",
              UserAttributes: [
                {
                  Name: "sub",
                  Value: "bd2bb8bc-dfe6-4216-829c-5ae975ce24e5"
                },
                {
                  Name: "email_verified",
                  Value: "true"
                },
                {
                  Name: "name",
                  Value: "Ben Vogan"
                },
                {
                  Name: "phone_number_verified",
                  Value: "true"
                },
                {
                  Name: "phone_number",
                  Value: "<redacted>"
                },
                {
                  Name: "email",
                  Value: "<redacted>"
                }
              ],
              UserCreateDate: 2018-07-24 03:29:49 +0000 UTC,
              UserLastModifiedDate: 2018-07-24 04:19:51 +0000 UTC,
              UserMFASettingList: ["SMS_MFA"],
              UserStatus: "CONFIRMED",
              Username: "bd2bb8bc-dfe6-4216-829c-5ae975ce24e5"
            }

I do not know if there is a way to query the user's auth state so that I can verify that.

The AWS documentation and unhelpful errors are driving me insane so any help would be greatly appreciated!

Thanks.

Ben
  • 1,793
  • 2
  • 15
  • 22
  • 1
    Read somewhere that using the email as the username might make it work. Can you give it a try? – icza Jul 26 '18 at 18:09
  • In the configuration of the user pool I have selected the radio button that requires usernames to be email address or phone number, and under that selected that usernames but be email addresses. So I believe all usernames are email addresses in my case. – Ben Jul 26 '18 at 23:29
  • 1
    Hi did you find something ? – Sonny Jayet Dec 04 '18 at 15:35
  • Nope haven't found a resolution and effectively put this on hold as it isn't critical at the moment. Eventually I will try using a token based 2 factor authentication and see if that works. – Ben Dec 06 '18 at 02:38
  • 3
    Works for me when USERNAME = email but not when equals cognito Id. – Brice Feb 28 '19 at 15:55
  • The only thing I notice right off the bat is that you're converting statically typed strings to strings as well as types that are not. Could be one has to be passed as a []byte value rather than a string? – Taylor Belland Aug 25 '19 at 03:54
  • It's a small thing, but in your question, you didn't show the AWS responses. In the response to `AdminInitiateAuth`, is the challenge you receive back `SMS_MFA`? Its been awhile since I've worked with Cognito at this level. One other gotcha that I've hit before is that `SECRET_HASH` is required if your Cognito app has a secret enabled. edit: I've successfully used this lib for the SRP auth flow in Go too: https://github.com/alexrudd/cognito-srp – mrosales Feb 23 '21 at 16:42
  • Hey, I was using this with react native, I had to pass user.challengeName to `Auth.confirmSignIn` and it worked. The default value for `mfaType` is null in the method definition. – frozenOne Feb 07 '23 at 19:57

1 Answers1

0

Your questions seems ambiguous.

step 2 you are

  1. I set the phone number and request a verification code
  2. I verify the phone number

this is nothing but MFA in Cognito. if you have configured the pool correctly.

You cannot have both phone for login and MFA. that doesn't make sense.

but if you have phone for login, then Cognito will send SMS every time with code. password is only for email logins.

darcwader
  • 31
  • 4
  • 1
    I don't see how the question is ambiguous. Step 1 clearly indicates that I created the account with a username & password. I do not have phone for login. I obviously have to set the phone number to request an SMS verification code for MFA, which I do in step 2. And yes it is nothing but MFA in Cognito, and it does not work. Having used Cognito for a while now my feeling is that it isn't ready for production use and would strongly recommend anyone reading this not to use it. Aside from random bugs like this, being unable to make a backup of a user pool is a deal breaker. – Ben Jun 02 '20 at 17:22