aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.42k stars 2.12k forks source link

CUSTOM_AUTH with password does not work with Auth.signIn() #3159

Closed annjawn closed 11 months ago

annjawn commented 5 years ago

Describe the bug

I am using a Custom Auth flow with the 3 Lambda Function to generate a secret and have the user verify that secret. I have set authenticationFlowType: 'CUSTOM_AUTH' in the client side. However, calling Auth.signIn(username, password) shows the error below-

TypeError: First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.

To Reproduce Steps to reproduce the behavior:

  1. Set the three Lambda Functions for a CUSTOM_CHALLENGE in Cognito Define, Create and Verify Auth Challenge Triggers
  2. Set authenticationFlowType: 'CUSTOM_AUTH' in the client (Javascript)
  3. Make a call to Amplify Auth.signIn method with username & password
  4. Notice Error - TypeError: First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.

Expected behavior Expected behavior is to get back an instance of CognitoUser object with custom challenge property so that the user can be prompted to respond to the challenge.

Screenshots

Screen Shot 2019-04-24 at 8 44 22 AM

Desktop (please complete the following information):

Additional context The issue seem to be related to #594 and #525. However, the code being referred to in these threads are already in the latest build . i.e.

else if (this.authenticationFlowType === 'USER_SRP_AUTH' || this.authenticationFlowType === 'CUSTOM_AUTH') {

However, passing the password still doesn't work and continues to show the error. Removing, the password or passing null for password in Auth.signIn works as expected but this would mean paswordless authentication which is not what we intend. Not sure if the SDK supports CUSTOM_AUTH flow with only passwordless authentication.

The error reported above seems to be coming from this line.

It also looks like that initiateAuth() is only called in paswordless mode

https://github.com/aws-amplify/amplify-js/blob/f5cf034d244879f56dca6c11aca74863ed6a340f/packages/auth/src/Auth.ts#L466-L487

b-tiwari commented 5 years ago

I am getting the same error when singing in with 'CUSTOM_AUTH' set as authentication flow in Amplify config.

annjawn commented 5 years ago

@b-tiwari I think this error has a lot to do with how you code the Define Auth Lambda Trigger function I noticed that if you want to do Password verification and then a CUSTOM_AUTH from your app then your Define Auth lambda has to be exactly as shown below especially the SRP_A part which is the first if check. If I do authenticationFlowType: 'CUSTOM_AUTH' with the lambda function as below then it works fine and cognito first verifies the password and then returns the token with the CUSTOM_CHALLENGE back to the App at which point I can prompt the user to enter whatever custom challenge I have defined (i.e. Captcha, or temporary code etc.).

exports.handler = async (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 = 'PASSWORD_VERIFIER';
    } else if (event.request.session.length == 2  &&  event.request.session[1].challengeName == 'PASSWORD_VERIFIER'  && event.request.session[1].challengeResult == true) {
        event.response.issueTokens = false;
        event.response.failAuthentication = false;
        event.response.challengeName = 'CUSTOM_CHALLENGE';
    } else if (event.request.session.length == 3  &&  event.request.session[2].challengeName == 'CUSTOM_CHALLENGE'  && event.request.session[2].challengeResult == true) {
        event.response.issueTokens = true;
        event.response.failAuthentication = false;
    } else {
        event.response.issueTokens = false;
        event.response.failAuthentication = true;
    }
    context.done(null, event);
};

Also, keep in mind, that you will need to atleast have created the Define Challenge trigger as well to be able to even test this whole thing. It's just that the documentation on CUSTOM_AUTH is not very good or detailed and the only meaningful documentation is this blog post- https://aws.amazon.com/blogs/mobile/customizing-your-user-pool-authentication-flow/

buggy commented 5 years ago

@annjawn @b-tiwari I'm not sure if/how it's changed but when I wrote the original PR to add CUSTOM_AUTH support it was for passwordless logins. You would call Auth.signIn() with just the username then respond to custom challenges using Auth.sendCustomChallengeAnswer(). I believe it was expanded later to support passwords but I'm not 100% certain about that.

annjawn commented 5 years ago

@buggy yes it now supports CUSTOM_AUTH with password i.e. first do a SRP_A and then a CUSTOM_CHALLENGE provided the Define Auth Lambda trigger's first if clause is coded exactly as shown in my post above.

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] commented 5 years ago

This issue has been automatically closed because of inactivity. Please open a new issue if are still encountering problems.

obonyojimmy commented 4 years ago

I also noticed that if using Auth.signIn(username, password) , but works if using Auth.signIn({ username, password }) ie an object of {username, password}

devTechi commented 4 years ago

Thanks @obonyojimmy

This is helping a lot. Though it isn't intuitiv at all. I already thought about changing the challenge from SRP_A to CUSTOM_CHALLENGE at the backend.

tsehe commented 3 years ago

I am getting the same error when singing in with 'CUSTOM_AUTH' set as authentication flow in Amplify config.

epratik commented 3 years ago

@annjawn were you able to resolve the issue? I am trying to do a username/password login with custom email OTP using CUSTOM_AUTH. Below are the steps - 1.Auth.signIn(username, password) 2.This executes the first 2 cases of define auth challange lambda trigger 3.The input json to define auth challange lambda looks like this -

  {
    "challengeName": "SRP_A",
    "challengeResult": true,
   "challengeMetadata": null
  },
  {
    "challengeName": "PASSWORD_VERIFIER",
    "challengeResult": true,
    "challengeMetadata": null
  }

4.Its clear that password is validated. 5.It now sets the challange to CUSTOM_CHALLANGE. 6.At this point the client fails with NotAuthorizedException instead of asking for the challange answer.

My define auth challange lambda is like this -

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

Is this supported by amplify?

SvyatHoly commented 3 years ago

Please reopen this issue. We have a critical blocker with amplify. There are tons of bugs with this framework we face every day.

evcodes commented 3 years ago

Hey everyone, I am going to be working on reproducing this issue so please be patient as this is somewhat complex.

SvyatHoly commented 3 years ago

Hey everyone, I am going to be working on reproducing this issue so please be patient as this is somewhat complex.

{"ChallengeName":"CUSTOM_CHALLENGE","ChallengeParameters":{"USERNAME":"e4fed32c-e82d-463d-b4d5-03d5dc92d24d","email":"123@icloud.com"},"Session":"AYABeGc1q3-Hu0PnwQ11WN4763cAHQABAAdTZXJ2aWNlABBDb2duaXRvVXNlclBvb2xzAAEAB2F3cy1rbXMAS2Fybjphd3M6a21zOmV1LXdlc3QtMTo0NTU0NTg0OTMwODE6a2V5L2FiN2U3M2UzLWU2NDEtNDk5Zi1iNzc0LWZkZmM1MWM3NzFhYQC4AQIBAHjD65VylUN4FxI94_djiALdSNFQ3YA-SCdx6BLkTlEydQGRCgncCkjUribtu0_9qN0GAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMC__Uop_CcXUmKyJdAgEQgDuQ6ny12wpXUg1jjO04-4Ftde4mt6wpcDlP1DpyNeVF19ZeL13DSOUKtLK3s4RywMBgWnIsMj3HR1rxTQIAAAAADAAAEAAAAAAAAAAAAAAAAABNER6lhOuM3V1fB9gJCtFi_____wAAAAEAAAAAAAAAAAAAAAEAAAE-OatWzODn1u5MX4aP5QBI6znabEzL_ctibIau8fQWL8QFyH-ef5YM2sSlSlDVUgkLCJIAvZ-uy-hTZ6livuq-9iSBHOjDxT9h6gV9V64Vqo867yd8WSkmQhfzvinvtKQUdpKhqqssacNYw5gukWL76oSiBODtROfkzS5lLf7nugGaEDKBAztf_cXYbLZVJRz6AZschaGtIBzLrifui6E3nvmVMq8npAFRWxkxWpmH7EsYodEoBs5JT6iLGdOjSKs4TR9hNHNwshBiwlL-3LKvqFLd6xh3CYeASuAViV7sx3NNzf1VJ_PBHk9zt-bdzvjx4c0RChQjwnrltRiC3nOn4rsrBroY7VtGLlH4BOr_O4St2eqFhA2pQ3tJh-xOzVNgzyBcsPMPfkgNZlMQ2fWr9XJb6IdOjiW_-8dEiEz4xh9UGCqDoN1wQ1NAIru93A"}

This is what I receive from cognito and this is not array like object, Do you need something else for debug?

ghardin1314 commented 3 years ago

Also running into this bug just now. If using the example define, it works well

Example:

exports.handler = async (event, context, callback) => {

  if (
    event.request.session.length == 1 &&
    event.request.session[0].challengeName == "SRP_A"
  ) {
    event.response.issueTokens = false;
    event.response.failAuthentication = false;
    event.response.challengeName = "PASSWORD_VERIFIER";
  } else if (
    event.request.session.length == 2 &&
    event.request.session[1].challengeName == "PASSWORD_VERIFIER" &&
    event.request.session[1].challengeResult == true
  ) {
    event.response.issueTokens = false;
    event.response.failAuthentication = false;
    event.response.challengeName = "CUSTOM_CHALLENGE";
  } else if (
    event.request.session.length == 3 &&
    event.request.session[2].challengeName == "CUSTOM_CHALLENGE" &&
    event.request.session[2].challengeResult == true
  ) {
    event.response.issueTokens = true;
    event.response.failAuthentication = false;
  } else {
    event.response.issueTokens = false;
    event.response.failAuthentication = true;
  }

  console.log("RETURNED event: ", JSON.stringify(event, null, 2));

  callback(null, event);
};

But if I try to skip the password verification step then I get an error:

exports.handler = async (event, context, callback) => {
  console.log("RECEIVED event: ", JSON.stringify(event, null, 2));

  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;
  }

  console.log("RETURNED event: ", JSON.stringify(event, null, 2));

  callback(null, event);
};

Error:

TypeError: First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.
    at fromObject (index.js:311)
    at from (index.js:137)
    at Function.push../node_modules/buffer/index.js.Buffer.from (index.js:149)
    at CognitoUser.js:298
    at AuthenticationHelper.js:280
    at AuthenticationHelper.js:306
    at BigInteger.bnModPow [as modPow] (BigInteger.js:838)
    at AuthenticationHelper.js:301
    at BigInteger.bnModPow [as modPow] (BigInteger.js:838)
    at AuthenticationHelper.calculateS (AuthenticationHelper.js:295)
    at AuthenticationHelper.getPasswordAuthenticationKey (AuthenticationHelper.js:273)
    at CognitoUser.js:291
    at Client.js:144
zensim commented 1 year ago

I am using username and password to signin. Getting "authentication flow invalid message" when using CUSTOM_AUTH as the authenticationFlowType. Similar to this issue. Is there a fix or how to resolve the error? Thanks

abdallahshaban557 commented 1 year ago

Hi @zensim - can you please submit a Github issue for your scenario? Want to make sure we capture it accurately.

zensim commented 1 year ago

submitted yesterday. authenticationFlowType : CUSTOM_AUTH returns invalid authentication · Issue #10447 · aws-amplify/amplify-js

|

authenticationFlowType : CUSTOM_AUTH returns invalid authentication · Is...

Before opening, please confirm: I have searched for duplicate or closed issues and discussions. I have read the ... |

|

|

Thanks

On Monday, October 10, 2022 at 11:35:29 AM CDT, Abdallah Shaban ***@***.***> wrote:  

Hi @zensim - can you please submit a Github issue for your scenario? Want to make sure we capture it accurately.

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>

purwa-astawa commented 1 year ago

works for me using @obonyojimmy suggestion, passing empty string for the password

Auth.signIn({
            username,
            password: '',
})
nadetastic commented 1 year ago

HI @purwa-astawa glad the work around help you out. Im following up on this issue and had a question for your use case.

Is your DefineAuth lambda verifying the password? In other words has the following check -

if ( ... && event.request.session[1].challengeName === 'PASSWORD_VERIFIER' && ... ) 

If possible please share the function code.

purwa-astawa commented 1 year ago

@nadetastic unfortunately, I'm only handling the client side. lambda was handled by the other team member, so I cannot share the function code

theintellify commented 1 year ago

@nadetastic Is there any update on this bug? This is a road blocker for our production release. Can you please help us out with this?

nadetastic commented 1 year ago

Hi @theintellify - this issue has a few nuances and Im curious how you have your flow setup and what error you are seeing. For example:

theintellify commented 1 year ago

@nadetastic

Notes:

image_2023_04_18T04_09_15_056Z

image_2023_04_18T04_10_11_456Z

theintellify commented 1 year ago

Hi @nadetastic Can you please check the above code and let me know if you can help us with this?

adadgio commented 1 year ago

Issue still not resolved, or not acceptable answer here? :/

nadetastic commented 1 year ago

@theintellify @adadgio Hi folks, following up here. After a bit of investigation and discussion, a few different problems can occur based on the flow you are attempting to use and how the Challenge Lambdas (mainly DefineAuth and CreateChallenge) have been configured.

I have been able to get this working when using a username and password with the following lambda functions:

// Define Auth Challenge Lambda
exports.handler = async event => {

  if (event.request.session.length === 1 && event.request.session[0].challengeName === 'SRP_A') {
    event.response.issueTokens = false;
    event.response.failAuthentication = false;
    event.response.challengeName = 'PASSWORD_VERIFIER';
  } else if (
    event.request.session.length === 2 &&
    event.request.session[1].challengeName === 'PASSWORD_VERIFIER' &&
    event.request.session[1].challengeResult === true
  ) {
    event.response.issueTokens = false;
    event.response.failAuthentication = false;
    event.response.challengeName = 'CUSTOM_CHALLENGE';
  } else if (
    event.request.session.length === 3 &&
    event.request.session[2].challengeName === 'CUSTOM_CHALLENGE' &&
    event.request.session[2].challengeResult === true
  ) {
    event.response.issueTokens = true;
    event.response.failAuthentication = false;
  } else {
    event.response.issueTokens = false;
    event.response.failAuthentication = true;
  }

  return event;
};
// Create Challenge Lambda
exports.handler = async (event) => {
    let verificationCode;

    //Only called once after SRP_A and PASSWORD_VERIFIER challenges. Hence session.length == 2
    if (event.request.session.length === 2) {
       // your own logic to generate and send challenge question and answer
        verificationCode = "some-value"
    } 

    //add to privateChallengeParameters. This will be used by VerifyAuthLambda.
    event.response.privateChallengeParameters = {
        "verificationCode": verificationCode
    };

    //add it to session, so its available during the next invocation.
    event.response.challengeMetadata = verificationCode;

    return event;
};

Could you double check and verify your lambda's? Feel free to share the sample code as well.

nadetastic commented 1 year ago

@adadgio following up here - have you had a chance to review the above comment? https://github.com/aws-amplify/amplify-js/issues/3159#issuecomment-1604495872

nadetastic commented 11 months ago

Hi all, I'm going to close out this issue for now, as it is not an issue with the library but a gap in documentation. We are tracking the documentation improvement here and should have an update published soon.

If you have any additional questions let me know.