amplify-swift icon indicating copy to clipboard operation
amplify-swift copied to clipboard

"Amplify.Auth.signIn(username, nil)" triggers "SRP_A" challenge

Open Ramesh707 opened this issue 4 years ago • 13 comments

Describe the bug

When attempting to trigger the custom auth challenge by using Amplify.auth.signin(username, nil) we receive a session containing SRP_A in the define auth challenge trigger, when instead we are expecting CUSTOM_CHALLENGE

Following Amplify documentation here : > https://docs.amplify.aws/sdk/auth/custom-auth-flow/q/platform/ios

Response:

{
    "version": "1",
    "region": "xxxxxxx",
    "userPoolId": "xxxxxxxx",
    "userName": "xxxxxxxxx",
    "callerContext": {
        ...
    },
    "triggerSource": "DefineAuthChallenge_Authentication",
    "request": {
        "userAttributes": {
           ...
        },
        "session": [
            {
                "challengeName": "SRP_A",
                "challengeResult": true,
                "challengeMetadata": null
            }
        ]
    },
    "response": {
        "challengeName": null,
        "issueTokens": null,
        "failAuthentication": null
    }
}

Expected:

{
    "version": "1",
    "region": "xxxxxx",
    "userPoolId": "xxxxxxx",
    "userName": "xxxxxxx,
    "callerContext": {
        ...
    },
    "triggerSource": "DefineAuthChallenge_Authentication",
    "request": {
        "userAttributes": {
     ...
        },
        "session": []
    },
    "response": {
        "challengeName": "CUSTOM_CHALLENGE",
        "issueTokens": false,
        "failAuthentication": false
    }
}

We are using 2 methods of sign-in in our mobile application utilising Amplify Authentication with Cognito, which are:

  1. email and password login with the following method:
self.signInOperation = Amplify.Auth.signIn(username: email, password: "Tester321&")
                        .resultPublisher
                        .sink {
                            if case let .failure(authError) = $0 {
                                print(authError)
                            }
                        }
                        receiveValue: { pUser in
                            switch pUser.nextStep {
                            case .confirmSignInWithCustomChallenge(_):
                                print(pUser)

                            case .done:
                                print(pUser)
                            default:
                                print(pUser)
                            }
                        }
  1. phone_number with passwordless login using the following method:
self.signInOperation = Amplify.Auth.signIn(username: phone, password: nil)
                        .resultPublisher
                        .sink {
                            if case let .failure(authError) = $0 {
                                print(authError)
                            }
                        }
                        receiveValue: { pUser in
                            switch pUser.nextStep {
                            case .confirmSignInWithCustomChallenge(_):
                                print(pUser)

                            case .done:
                                print(pUser)
                            default:
                                print(pUser)
                            }
                        }

Method of initialisation:

     do {
                try Amplify.add(plugin: AWSCognitoAuthPlugin())
                try Amplify.configure()
                AWSDDLog.sharedInstance.logLevel = .debug
              print("Amplify configured with auth plugin")
       
        } catch {
            print("Failed to initialize Amplify with \(error)")
        }

To Reproduce Steps to reproduce the behavior:

  1. Attempt to call Amplify.auth.signin(..) with parameters:
username: phone_number
password: nil

Observed Behavior When attempting a passwordless login triggering custom auth challenge the SDK returns SRP_A as challenge type when instead it should return CUSTOM_AUTH as the challenge type for the login to succeed

Expected Behavior By calling Amplify.Auth.signIn(username, nil) we are looking to receive CUSTOM_AUTH as the challenge type

Stack Trace

SDK response

AuthError: Incorrect username or password.
Recovery suggestion: Check whether the given values are correct and the user is authorised to perform the operation.

Cloudwatch Logs

Received

{
    "version": "1",
    "region": "ap-southeast-1",
    "userPoolId": "xxxxxxxxxxxxxxxx",
    "userName": "tester001",
    "callerContext": {
        "awsSdkVersion": "aws-sdk-unknown-unknown",
        "clientId": "xxxxxxxxxxxx"
    },
    "triggerSource": "DefineAuthChallenge_Authentication",
    "request": {
        "userAttributes": {
            "sub": "xxxxxxxxxxx",
            "email_verified": "true",
            "cognito:user_status": "CONFIRMED",
            "cognito:email_alias": "[email protected]",
            "cognito:phone_number_alias": "+6600000000",
            "phone_number_verified": "true",
            "phone_number": "+6600000000",
            "given_name": "test",
            "family_name": "test",
            "email": "[email protected]"
        },
        "session": [
            {
                "challengeName": "SRP_A",
                "challengeResult": true,
                "challengeMetadata": null
            }
        ]
    },
    "response": {
        "challengeName": null,
        "issueTokens": null,
        "failAuthentication": null
    }
}

Expected

{
    "version": "1",
    "region": "ap-southeast-1",
    "userPoolId": "xxxxxxxxxxx",
    "userName": "xxxxxxxxxxx",
    "callerContext": {
        "awsSdkVersion": "aws-sdk-unknown-unknown",
        "clientId": "xxxxxxxxxxxxx"
    },
    "triggerSource": "DefineAuthChallenge_Authentication",
    "request": {
        "userAttributes": {
            "sub": "xxxxxxxxxxx",
            "email_verified": "true",
            "cognito:user_status": "CONFIRMED",
            "cognito:email_alias": "[email protected]",
            "cognito:phone_number_alias": "+6600000000",
            "phone_number_verified": "true",
            "phone_number": "+6600000000",
            "given_name": "test",
            "family_name": "test",
            "email": "[email protected]"
        },
        "session": []
    },
    "response": {
        "challengeName": "CUSTOM_CHALLENGE",
        "issueTokens": false,
        "failAuthentication": false
    }
}

Unique Configuration Custom Authentication using the following triggers:

  • Define Auth
  • Create Auth Challenge
  • Verify Auth Challenge

Areas of the SDK you are using (AWSMobileClient, Cognito, Pinpoint, IoT, etc)? Cognito User Pools

Environment(please complete the following information):

  • SDK Version: 2.23.2
  • Dependency Manager: Cocoapods
  • Swift Version : Swift 5.0 (Swift UI)
  • Xcode Version: 12.3

Device Information (please complete the following information):

  • Device: iPhone 11 and Simulators
  • iOS Version: 14.3

Additional context Add any other context about the problem here like your specific use case.

Relevant Console Output In some cases, it might be helpful to add any logs from your console that better help you tell the story of this issue. You can easily enable logging in your app by putting the following code in your app delegate.

AWSDDLog.sharedInstance.logLevel = .debug
AWSDDLog.add(AWSDDTTYLogger.sharedInstance)

Ramesh707 avatar Apr 01 '21 07:04 Ramesh707

Hi, @Ramesh707 Thanks for reaching out

The link your provided is for aws-sdk, but based on your description, you are using Amplify library. This is the link for Amplify Auth Custom Flow.

And since you are using custom flow. We need detailed steps that we can follow to reproduce the issue. How does your user do sign up? And did you do some changes to your AWS Cognito Console?

618coffee avatar Apr 01 '21 18:04 618coffee

Hey there @ruiguoamz ,

Thank you for the swift reply, ok so in regards to your questions. Indeed we are using Amplify SDK, the following is how we make signup calls:

func signUp() {
     let phone = "+66XXXXXXXX"

     var aUserAttributeArray = [
         AuthUserAttribute(.email, value: "[email protected]")
     ]
         aUserAttributeArray.append(AuthUserAttribute(.givenName, value: "dssssd"))

         aUserAttributeArray.append(AuthUserAttribute(.familyName, value: "scssdcds"))

         aUserAttributeArray.append(AuthUserAttribute(.phoneNumber, value: phone))

     let options = AuthSignUpRequest.Options(userAttributes: aUserAttributeArray)
     Amplify.Auth.signUp(username: randomAlphaNumericString(length: 7), password: "Test123*", options: options) { result in
         switch result {
         case .success(let signUpResult):
             self.signIn(username: phone)
         case .failure(let error):
             print("An error occurred while registering a user \(error)")
         }
     }
 }

And this is the function we use for sign-in:

func signIn(username: String) {
      Amplify.Auth.signIn(username: username, password: nil) { result in
          switch result {
          case .success:
              print("Sign in succeeded")
          case .failure(let error):
              print("Sign in failed \(error)")
          }
      }
  }

In regards to special configuration in AWS Cognito:

  • We have set ability for users to sign-in with email or phone number
Screen Shot 2564-04-02 at 14 10 49
  • We have a Define Auth challenge, Create Auth Challenge, Verify Auth Challenge Triggers

Reproducing error:

  1. Use the functions above to make a signup
  2. Use function above to make a login without password as to initiate the CUSTOM AUTH
  3. Console log the event being received in Define Auth Challenge

Expected:

{
    "version": "1",
    "region": "ap-southeast-1",
    "userPoolId": "xxxxxxxxxxx",
    "userName": "xxxxxxxxxxx",
    "callerContext": {
        "awsSdkVersion": "aws-sdk-unknown-unknown",
        "clientId": "xxxxxxxxxxxxx"
    },
    "triggerSource": "DefineAuthChallenge_Authentication",
    "request": {
        "userAttributes": {
            "sub": "xxxxxxxxxxx",
            "email_verified": "true",
            "cognito:user_status": "CONFIRMED",
            "cognito:email_alias": "[email protected]",
            "cognito:phone_number_alias": "+6600000000",
            "phone_number_verified": "true",
            "phone_number": "+6600000000",
            "given_name": "test",
            "family_name": "test",
            "email": "[email protected]"
        },
        "session": []
    },
    "response": {
        "challengeName": "CUSTOM_CHALLENGE",
        "issueTokens": false,
        "failAuthentication": false
    }
}

Actual:

{
    "version": "1",
    "region": "ap-southeast-1",
    "userPoolId": "xxxxxxxxxxxxxxxx",
    "userName": "tester001",
    "callerContext": {
        "awsSdkVersion": "aws-sdk-unknown-unknown",
        "clientId": "xxxxxxxxxxxx"
    },
    "triggerSource": "DefineAuthChallenge_Authentication",
    "request": {
        "userAttributes": {
            "sub": "xxxxxxxxxxx",
            "email_verified": "true",
            "cognito:user_status": "CONFIRMED",
            "cognito:email_alias": "[email protected]",
            "cognito:phone_number_alias": "+6600000000",
            "phone_number_verified": "true",
            "phone_number": "+6600000000",
            "given_name": "test",
            "family_name": "test",
            "email": "[email protected]"
        },
        "session": [
            {
                "challengeName": "SRP_A",
                "challengeResult": true,
                "challengeMetadata": null
            }
        ]
    },
    "response": {
        "challengeName": null,
        "issueTokens": null,
        "failAuthentication": null
    }
}

Please let us know if theres anything else you require will be happy to provide.

Ramesh707 avatar Apr 02 '21 07:04 Ramesh707

While @ruiguoamz investigates, can you also confirm that you configured your Amplify library for custom authentication (e.g., via the amplifyconfiguration.json file) with a value of CUSTOM_AUTH for the authenticationFlowType key?

palpatim avatar Apr 02 '21 15:04 palpatim

Hi,

@ruiguoamz Thank you for the reply. Please find below the amplifyconfiguration.json file attached (The attachment is in .txt file since .json file is not supported to attach here)

amplifyconfiguration.txt

Ramesh707 avatar Apr 08 '21 02:04 Ramesh707

Pasting config inline for easier evaluation:

{
    "UserAgent": "aws-amplify-cli/2.0",
    "Version": "1.0",
    "auth": {
        "plugins": {
            "awsCognitoAuthPlugin": {
                "UserAgent": "aws-amplify/cli",
                "Version": "0.1.0",
                "IdentityManager": {
                    "Default": {}
                },
                "CognitoUserPool": {
                    "Default": {
                        "PoolId": "ap-southeast-XXXXXXX",
                        "AppClientId": "XXXXXXXXX",
                        "Region": "ap-southeast-1"
                    }
                },
                "Auth": {
                    "Default": {
                        "OAuth": {
                            "WebDomain": "XXXXX.XXXXX",
                            "AppClientId": "XXXXXXXXX",
                            "SignInRedirectURI": "xxxxxxx",
                            "SignOutRedirectURI": "xxxxxxxxxx",
                            "Scopes": [
                                "phone",
                                "email",
                                "openid",
                                "profile",
                                "aws.cognito.signin.user.admin"
                            ]
                        },
                        "authenticationFlowType": "CUSTOM_AUTH"
                    }
                }
            }
        }
    }
}

palpatim avatar Apr 09 '21 16:04 palpatim

Please refer this note here - https://docs.amplify.aws/lib/auth/signin_with_custom_flow/q/platform/ios#lambda-trigger-setup. AWSCognitoAuthPlugin assumes that the initial step is a SRP_A flow, this is a current limitation because of the underlying AWSMobileClient SDK that we use. You can override this behavior by making the Define Auth Challenge Lambda trigger to bypass the initial username/password verification and proceed to the custom challenge.

exports.handler = (event, context) => {
  if (event.request.session.length === 1 && 
    event.request.session[0].challengeName === 'SRP_A') {
    event.response.issueTokens = false;
    event.response.failAuthentication = false;
    event.response.challengeName = 'CUSTOM_CHALLENGE';
  } else if (
    event.request.session.length === 2 &&
    event.request.session[1].challengeName === 'CUSTOM_CHALLENGE' &&
    event.request.session[1].challengeResult === true
  ) {
    event.response.issueTokens = true;
    event.response.failAuthentication = false;
  } else {
    event.response.issueTokens = false;
    event.response.failAuthentication = true;
  }
  context.done(null, event);
};

Does this fix works for your use case?

royjit avatar Apr 13 '21 18:04 royjit

@royjit In our case we are using 2 forms of login method in a single application which are:

  • Email and Password
  • Phone Number and Passwordless

Therefore the above method wouldn't work in our case, this seems to only be the case with IOS SDK however, the Android SDK seems to satisfy our requirements for both login methods, is there any chance that Amplify team/ our team could propose a fix for standardising the session responses to match Android hence fixing our issue we are facing ?

dan62 avatar Apr 15 '21 06:04 dan62

This is what we are expecting for the first session from IOS SDK:

  • Note: Android SDK already gives us this response
{
    "version": "1",
    "region": "xxxxxx",
    "userPoolId": "xxxxxxx",
    "userName": "xxxxxxx,
    "callerContext": {
        ...
    },
    "triggerSource": "DefineAuthChallenge_Authentication",
    "request": {
        "userAttributes": {
     ...
        },
        "session": []
    },
    "response": {
        "challengeName": "CUSTOM_CHALLENGE",
        "issueTokens": false,
        "failAuthentication": false
    }
}

dan62 avatar Apr 15 '21 06:04 dan62

Amplify iOS does not currently support the ability to change auth methods at runtime. We'll take this as a feature request and update this issue when we have more info.

palpatim avatar Jun 21 '21 18:06 palpatim

@palpatim Please let us know if you need any additional information from us, currently we have a work around to ensure we can use both login methods at runtime, however would be great if we could have that in the SDK thanks.

daniel-g-pomelo avatar Jun 22 '21 02:06 daniel-g-pomelo

@daniel-g-pomelo would you mind sharing your workaround for supporting both login methods? Facing the same issue. Thanks!

corleymj avatar Oct 12 '21 16:10 corleymj

Hey there @corleymj , sure !

So the way we do it is by setting the amplify apps to CUSTOM_CHALLENGE in the config and then adding an additional attribute in Cognito itself (eg) loginMethod which would contain either SRP_A or CUSTOM_CHALLENGE for every user. Then in the define auth challenge we detect if its a first session login and we read this attribute from Cognito to make the decision on how to respond to client apps.

*Keep in mid you will need an endpoint of sorts to perform the updating of the attribute depending on what user decides to login with prior to callingt he auth.signin(..) SRP_A been a username and password login and CUSTOM_CHALLENGE been a phone number login.

I hope this helps !

daniel-g-pomelo avatar Oct 13 '21 00:10 daniel-g-pomelo

PR for the fix - https://github.com/aws-amplify/amplify-ios/pull/1983

royjit avatar Sep 21 '22 19:09 royjit

Released in v2.0.0, Amplify Library for Swift now support changing authflow type at runtime as well as starting custom auth flow without SRP as the first step.

royjit avatar Oct 20 '22 02:10 royjit