aws-amplify / amplify-android

The fastest and easiest way to use AWS from your Android app.
https://docs.amplify.aws/lib/q/platform/android/
Apache License 2.0
250 stars 117 forks source link

no proper error callback triggers for `confirmSignIn()` when incorrect OTP is entered #2352

Closed sameera26 closed 1 year ago

sameera26 commented 1 year ago

Before opening, please confirm:

Language and Async Model

Kotlin

Amplify Categories

Authentication

Gradle script dependencies

```groovy // Amplify core dependency implementation 'com.amplifyframework:core:2.2.2' implementation 'com.amplifyframework:aws-auth-cognito:2.2.2' ```

Environment information

``` # Put output below this line ```

Please include any relevant guides or documentation you're referencing

https://github.com/aws-amplify/amplify-android/issues/2227

Describe the bug

I tried version 2.2.2 on Android. But the issue is still exist as mentioned on this ticket https://github.com/aws-amplify/amplify-android/issues/2227. SDK doesn't return anything when I enter wrong OTP or auth challenge. Therefore there is no way to handle retrying Amplify.Auth.confirmSignIn(). Please kindly help to resolve this issue.

Also we found that iOS also has reported a similar issue and We verified that iOS issue has resolved in the latest SDK https://github.com/aws-amplify/amplify-swift/pull/2617

Reproduction steps (if applicable)

Steps to reproduce the behavior:

  1. Call SignIn
  2. Call Amplify.Auth.confirmSignIn() with the wrong code or auth challenge
  3. SDK doesn't return anything

Code Snippet

// Put your code below this line.
try {
        Amplify.Auth.confirmSignIn(
                otpNumber,
                { result ->
                    if (result.isSignedIn) {
                        LogUtil.printDebug("AuthQuickstart", "Confirm sign in succeeded: $result")

                    } else {
                        LogUtil.printError(
                            "AuthQuickstart",
                            "Confirm sign in not complete. There might be additional steps: ${result.nextStep}"
                        )
                    }
                },
                { error ->
                    LogUtil.printError("AuthQuickstart", "Failed to confirm sign in - $error")
                }
            )
        } catch (error: Exception) {
            LogUtil.printError("AuthQuickstart", "Failed to confirm sign in - $error")
        }

Log output

``` // Put your logs below this line ```

amplifyconfiguration.json

No response

GraphQL Schema

```graphql // Put your schema below this line ```

Additional information and screenshots

Device Logs

Screenshot 2023-03-21 at 6 24 55 PM
gpanshu commented 1 year ago

I can confirm that the flow works correctly and you should see the error response back something like

NotAuthorizedException{message=Failed since user is not authorized., cause=NotAuthorizedException(message=Incorrect username or password.), recoverySuggestion=Check whether the given values are correct and the user is authorized to perform the operation.}

github-actions[bot] commented 1 year ago

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see. If you need more assistance, please either tag a team member or open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.

ininmm commented 1 year ago

Hi @gpanshu I have the same issue as @sameera26. I used CUSTOM_AUTH_WITHOUT_SRP auth flow and has no response after calling confirmSignIn with incorrect OTP code. However the auth flow would be triggered AuthSignInStep.DONE as expected and signed in successfully if I entered a correct OTP code.

Here is my pseudocode and logs:

   suspend fun signIn(
        account: String
    ) = suspendCancellableCoroutine<CognitoAuthResult> { continuation ->
        Timber.i("Ready to sign in: $account")
        Amplify.Auth.signIn(
            account,
            null,
            AWSCognitoAuthSignInOptions.CognitoBuilder()
                .authFlowType(AuthFlowType.CUSTOM_AUTH_WITHOUT_SRP)
                .build(),
            {
                Timber.i("signIn ${it.nextStep.additionalInfo?.get("USERNAME")} ${it.nextStep.additionalInfo?.get("XXXX")}")
                continuation.resume(
                    CognitoAuthResult(
                        it.isSignedIn,
                        it.nextStep.additionalInfo?.get("USERNAME") ?: "",
                        it.nextStep.additionalInfo?.get("XXXX") ?: ""
                    )
                )
            },
            {
                continuation.cancel(it)
                Timber.e(it)
            }
        )
    }

   suspend fun confirmSignIn(
        challengeCode: String,
        XXXX: String,
        YYYY: String
    ) = suspendCancellableCoroutine<CognitoAuthResult> { continuation ->
        Timber.i("confirmSignIn input: $challengeCode")
        Amplify.Auth.confirmSignIn(
            challengeCode,
            AWSCognitoAuthConfirmSignInOptions.builder()
                .metadata(
                    mapOf(
                        "XXXX" to XXXX,
                        "YYYY" to YYYY
                    )
                )
                .build(),
            {
                Timber.i("confirmSignIn result: $it")
                continuation.resume(
                    CognitoAuthResult(
                        it.isSignedIn,
                        "",
                        serviceSessionId
                    )
                )
            },
            {
                Timber.e(it)
                continuation.cancel(it)
            }
        )
    }
2023-04-28 13:47:34.686 CognitoLoginDelegate    com.example.ininmm                    I  Ready to sign in: 0988556687_TW
2023-04-28 13:47:37.254 CognitoLog...ate$signIn com.example.ininmm                    I  signIn 0988556687_TW 644b5df9af9285befe702d47
2023-04-28 13:47:41.685 CognitoLoginDelegate    com.example.ininmm                    I  confirmSignIn input: 000000
gpanshu commented 1 year ago

Hi @ininmm I have reopened the issue to see if I can troubleshoot it again. Please stand by.

hazemzuhair commented 1 year ago

Hi @gpanshu I have the same issue , I used "authenticationFlowType": "CUSTOM_AUTH_WITHOUT_SRP" auth flow and has no response after calling confirmSignIn with incorrect OTP code. However the auth flow would be triggered AuthSignInStep.DONE as expected and signed in successfully if I entered a correct OTP code.

 fun awsTOTPConfirm(code: String) {
        viewModelScope.launch {
            try {
                getAWSLoginOTPConfirm.postValue(Resource.loading(null))
                val amplify = Amplify.Auth

                amplify.confirmSignIn(code, { result ->
                    Log.e("AuthCode", result.toJson().toString())
                    getAWSLoginOTPConfirm.postValue(Resource.success(result))

                    // Check if the user is signed in
                    if (result.isSignedIn) {
                        Log.e("AuthCode", "Sign in succeeded")
                        // Handle successful sign-in
                        getAWSLoginOTPConfirm.postValue(Resource.success(result))
                    } else {
                        Log.e("AuthCode", "Invalid Code")
                        // Handle invalid code
                        getAWSLoginOTPConfirm.postValue(Resource.error("Invalid Code", null))
                    }
                }, { exception ->
                    Log.e("AuthCode", "Failed to sign in", exception)
                    // Handle exceptions
                    getAWSLoginOTPConfirm.postValue(Resource.error("An error occurred during sign in.", null))
                })
            } catch (exception: Exception) {
                Log.e("AuthCode", "Failed to sign in", exception)
                // Handle exceptions
                getAWSLoginOTPConfirm.postValue(Resource.error("An error occurred during sign in.", null))
            }
        }
    }

No Reponse , No anything ,

gpanshu commented 1 year ago

@hazemzuhair can you share your lambda code for incorrect code check? I would also recommend adding console.log(event); in your lambda code and checking cloudwatch for logs to understand what comes in and out of your lambdas (Create, Verify and Define).

Domnis commented 1 year ago

Hello ! I'm facing the same issue in my project. But I found something that could help to resolve the issue. The Amplify SDK (version 2.8.5 in my case) works as expected only if the flow is configured to accept only 1 attempt. In that case, if the code provide by the user is incorrect, the lambda fill the response object with this values

event.response.issueTokens = false;
event.response.failAuthentication = true;

and in app, method Amplify.Auth.confirmSignIn(challengeResponse) throw an exception NotAuthorizedException(message=Incorrect username or password.).

On the other hand, if our signIn flow is configured to accept multiple attempts, in case of wrong code provide by user, our lambda will the response with

event.response.issueTokens = false;
event.response.failAuthentication = false;
event.response.challengeName = "CUSTOM_CHALLENGE";

to let know we're still waiting for a correct response and user can still retry for the same last code sent. But in this case, SDK's method Amplify.Auth.confirmSignIn(challengeResponse) never return a response nor throw an exception, so app stay in pending result state.

As noted in first post, iOS SDK have fix this issue. I also found this ticket https://github.com/aws-amplify/aws-sdk-android/issues/2653 which seems to be related to this issue.

sameera26 commented 1 year ago

@gpanshu May I know are you going to provide any solution for this issue or we should inspect the lambda response object to allow retrying enter correct OTP.

gpanshu commented 1 year ago

Hi @Domnis that is certainly the kind of information I was looking for and will help me investigate further.

I will get back to you soon.

hazemzuhair commented 1 year ago

case 'DefineAuthChallenge_Authentication':

        console.log("DefineAuthChallenge_Authentication");
        console.log(event);

        if (session.length === 0) {
            event.response = {
                challengeName: 'CUSTOM_CHALLENGE',
                failAuthentication: false,
                issueTokens: false
            };
        }
        else {
            if (session[currentSession].challengeName === 'CUSTOM_CHALLENGE') {

                if (session[currentSession].challengeResult === true) {
                    event.response.issueTokens = true;
                    event.response.failAuthentication = false;
                }
                else {

                    let metaData = JSON.parse(session[currentSession].challengeMetadata);
                    if (metaData.attempts <= 3) {
                        event.response = {
                            challengeName: 'CUSTOM_CHALLENGE',
                            failAuthentication: false,
                            issueTokens: false
                        };
                    }
                    else {
                        event.response.issueTokens = false;
                        event.response.failAuthentication = true;
                    }
                }
            }
        }
        console.log(event);
        break;
{
  "errorType": "TypeError",
  "errorMessage": "Cannot read property 'session' of undefined",
  "trace": [
    "TypeError: Cannot read property 'session' of undefined",
    "    at Runtime.exports.handler (/var/task/index.js:4:35)",
    "    at Runtime.handleOnceNonStreaming (/var/runtime/Runtime.js:74:25)"
  ]
}
{
    "version": "1",
    "region": "XXXXXXXX",
    "userPoolId": "XXXXXXX",
    "userName": "XXXXXXXXX",
    "callerContext": {
        "awsSdkVersion": "aws-sdk-kotlin-0.21.1-beta",
        "clientId": "XXXXXXXXXX"
    },
    "triggerSource": "CreateAuthChallenge_Authentication",
    "request": {
        "userAttributes": {
            "cognito:token_nbf": "XXXXXXXX",
            "sub": "XXXXXXXX",
            "cognito:email_alias": "XXXXXXXX",
            "cognito:user_status": "CONFIRMED",
            "email_verified": "true",
            "preferred_username": "XXXXXXX",
            "email": "XXXXXXX"
        },
        "challengeName": "CUSTOM_CHALLENGE",
        "session": [
            {
                "challengeName": "CUSTOM_CHALLENGE",
                "challengeResult": false,
                "challengeMetadata": "{\"_sid\":\"1234\",\"challengeType\":\"OTP_CODE\",\"attempts\":1}"
            }
        ],
        "userNotFound": false
    },
    "response": {
        "publicChallengeParameters": {
            "challengeType": "OTP_CODE",
            "errorCode": "NotAuthorizedException"
        },
        "privateChallengeParameters": {
            "answer": "1234"
        },
        "challengeMetadata": "{\"_sid\":\"1234\",\"challengeType\":\"OTP_CODE\",\"attempts\":2}"
    }
}
{
    "version": "1",
    "region": "XXXXXXXX",
    "userPoolId": "XXXXXXXX",
    "userName": "XXXXXXXXXX",
    "callerContext": {
        "awsSdkVersion": "aws-sdk-kotlin-0.21.1-beta",
        "clientId": "XXXXXXXXX"
    },
    "triggerSource": "DefineAuthChallenge_Authentication",
    "request": {
        "userAttributes": {
            "cognito:token_nbf": "XXXXXXXXX",
            "sub": "XXXXXXXXX",
            "cognito:email_alias": "XXXXXXXXXX",
            "cognito:user_status": "CONFIRMED",
            "email_verified": "true",
            "preferred_username": "XXXXXXXX",
            "email": "XXXXXXXX"
        },
        "session": [
            {
                "challengeName": "CUSTOM_CHALLENGE",
                "challengeResult": false,
                "challengeMetadata": "{\"_sid\":\"1234\",\"challengeType\":\"OTP_CODE\",\"attempts\":1}"
            }
        ],
        "userNotFound": false
    },
    "response": {
        "challengeName": null,
        "issueTokens": null,
        "failAuthentication": null
    }
}

@gpanshu if this code help you to track error

sameera26 commented 1 year ago

I wanted to follow up on the status of this issue. can we expect a fix anytime soon or could you kindly provide us a workaround solution to prevent this. We are getting complaints from our production users.

gpanshu commented 1 year ago

Hi @sameera26 I am actively looking into this and Will provide an update shortly.

sameera26 commented 1 year ago

Friendly follow-up on my previous comment.

gpanshu commented 1 year ago

The code is merged in @sameera26 tracking for release this week. Once released I will communicate here with the release version. Thank you.

gpanshu commented 1 year ago

The fix is present in release 2.10.0. Please update and let us know if you have any issues. I will keep this ticket open for a week in case there are any issues.

sameera26 commented 1 year ago

@gpanshu I tested the latest SDK. It's working now. Thanks!

github-actions[bot] commented 1 year ago

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see. If you need more assistance, please either tag a team member or open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.

ininmm commented 1 year ago

@gpanshu Although now we can receive the error callback, we still can not distinguish how the sign in error was caused. I created another issue #2539 to discuss this feature.

blabs-afzal-ahmad-137 commented 1 week ago

i have issue in react native if i am verifying with wrong otp so confirmSignIn is generating new otp without triggering resend otp this my block of code try { setLoading(true); let session = null; try { session = await fetchAuthSession(); } catch (error) { console.error('Handle Verify error :: ', error); } if (!session) { await confirmSignIn({ challengeResponse: otp }); session = (await fetchAuthSession()) as AuthSession; if (session) { try { const url = ${BASE_URL}${ApiUrl.sellers.recordOptInSecure}; const currentDeviceInfo = await getcurrentDeviceInfo(); (await httpHelper.post(url, { phoneNumber: phoneNumber, otherDetails: { device: currentDeviceInfo }, })) as any; } catch (error: any) { console.log('Opt-in Error :: ', error); } } setLoading(false); } if (session) { await fetchSellerData(session as CognitoUserSession);

    setSuccessModalVisible(true);
    setLoading(false);
  } else {
    setLoading(false);
    throw Error('Session fetch error');
  }
} catch (error) {
  console.error('OtpVerification error: ', error);
  setInvalidOtp(true);
  setLoading(false);
}

handelling otp with this 
challengeResponse
tylerjroach commented 1 week ago

@blabs-afzal-ahmad-137 Please open a new ticket with the issue you are experiencing on our Amplify JS repo.