Skip to content
This repository has been archived by the owner on Feb 24, 2018. It is now read-only.

Custom Auth flow throwing error #228

Closed
caldwecr opened this issue Dec 20, 2016 · 22 comments
Closed

Custom Auth flow throwing error #228

caldwecr opened this issue Dec 20, 2016 · 22 comments
Labels

Comments

@caldwecr
Copy link

caldwecr commented Dec 20, 2016

While trying to follow Jeff Barr's blog post on using custom authentication I get this error. I'm actually trying to execute a passwordless flow, but even if I send the password along it still produces the same error.

aws-cognito-sdk.min.js:20 Uncaught Error: Cannot read property 'replace' of undefined
    at Object.toBits (sjcl.js:16)
    at constructor.<anonymous> (amazon-cognito-identity.min.js:19)
    at constructor.<anonymous> (aws-cognito-sdk.min.js:20)
    at constructor.callListeners (aws-cognito-sdk.min.js:20)
    at constructor.emit (aws-cognito-sdk.min.js:20)
    at constructor.emitEvent (aws-cognito-sdk.min.js:20)
    at constructor.a (aws-cognito-sdk.min.js:20)
    at d.runTo (aws-cognito-sdk.min.js:21)
    at aws-cognito-sdk.min.js:21
    at constructor.<anonymous> (aws-cognito-sdk.min.js:20)

My authenticate method looks like this

function customAuthenticateUser(username) {
    var authenticationData = {
        Username : 'tom',
        Password : '*********',
    };
    var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
    var pd = poolData();
    var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(pd);
    var userData = {
        Username : 'tom',
        Pool : userPool
    };
    var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
    cognitoUser.setAuthenticationFlowType('CUSTOM_AUTH');
    cognitoUser.authenticateUser(authenticationDetails, {
        onSuccess: function (result) {
            console.log('access token + ' + result.getAccessToken().getJwtToken());
        },

        customChallenge: function (challengeParameters) {
            //gather user responses in challengeResponses based on challengeParameters
            console.log("reached custom challenge area");
            cognitoUser.sendCustomChallengeAnswer(123, this);
        },

        onFailure: function(err) {
            alert(err);
        },
    });
}

My define auth challenge

exports.handler = function(event, context) {
    console.log('data', "event: " + JSON.stringify(event));
    console.log('data', "context: ", 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);
};

My create auth challenge

exports.handler = function(event, context) {
    console.log('data', "event: " + JSON.stringify(event));
    console.log('data', "context: ", context);
    if (event.request.session.length == 1 && event.request.challengeName == 'CUSTOM_CHALLENGE') {
        event.response.publicChallengeParameters = {};
        
        event.response.publicChallengeParameters.expectedAuthenticationFactor = 'signUpR';
        event.response.privateChallengeParameters = {};
        
        event.response.privateChallengeParameters.answer = 123;
        event.response.challengeMetadata = 'COOKIE_CHALLENGE';
    }
    console.log('data', "event with new auth challenge: " + JSON.stringify(event));
    context.done(null, event);
};

According to cloudwatch my verify auth challenge is never invoked

@caldwecr
Copy link
Author

I updated my sjcl, amazon-cognito-identity.min.js, and aws-cognito-sdk.min.js just in case, but it made no difference

@caldwecr
Copy link
Author

FYI this must be something with my lambda, b/c when I use jeff's example verbatim it does work. Trying to work from his implementation forwards to mine to find root cause.

@caldwecr
Copy link
Author

caldwecr commented Dec 20, 2016

So when I try to remove the password verifier stage from the define and create lambdas from Jeff's code I get the same error as I was originally experiencing.

Here are the exact lambdas

define

exports.handler = function(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);
}

create

exports.handler = function(event, context) {
    if (event.request.session.length == 1 && event.request.challengeName == 'CUSTOM_CHALLENGE') {
        event.response.publicChallengeParameters = {};
        event.response.publicChallengeParameters.captchaUrl = 'url/123.jpg';
        event.response.privateChallengeParameters = {};
        event.response.privateChallengeParameters.answer = '5';
        event.response.challengeMetadata = 'CAPTCHA_CHALLENGE';
    }
    context.done(null, event);
}

verify

exports.handler = function(event, context) {
    if (event.request.privateChallengeParameters.answer == event.request.challengeAnswer) {
        event.response.answerCorrect = true;
    } else {
        event.response.answerCorrect = false;
    }
    context.done(null, event);
}

@caldwecr
Copy link
Author

caldwecr commented Dec 20, 2016

Is there a hard dependency in amazon-cognito-identity-js of all custom auth flows including PASSWORD_VERIFIER as the second step; this seems to be the problem?

@caldwecr
Copy link
Author

This might be my answer

A custom authentication flow can also use a combination of built-in challenges such as SRP password verification and MFA via SMS, and custom challenges such as CAPTCHA or secret questions. If you want to include SRP in a custom authentication flow, you need to start with it. To initiate SRP password verification, the DefineAuthChallenge Lambda trigger returns SRP_A as the challenge name and SRP_A in the authentication parameters map. Once the password is verified the DefineAuthChallenge Lambda trigger will be called again with PASSWORD_VERIFIER in the previous challenges array. MFA will be done automatically if it is enabled for a user.

@caldwecr
Copy link
Author

The question now, is there a way to perform a non-srp custom authentication flow using amazon-cognito-identity-js sdk, or do I need to use the APIs instead?

@itrestian
Copy link
Contributor

Yes, if you want to do SRP as part of the authentication flow, the limitation is that you need to start with SRP and add custom challenges.

You should be able to achieve a passwordless flow by generating the code in lambda and delivering it by SNS and verifying it in the corresponding lambda functions.

Basically this SDK just helps with the BigInteger SRP math. Calling the specific APIs from the Cognito client should be straightforward. If there's an interest in providing more functionality, we can include the APIs in this SDK as well.

@caldwecr
Copy link
Author

caldwecr commented Dec 20, 2016

@itrestian thank you for the response. In our case you can imagine that we want to do captcha only authentication (that's not what we're doing, but for flow purposes it's suitably analogous). I'd like to call authenticateUser in the sdk and not perform any SRP, instead just perform the special passwordless auth.

Specifically in our case the verify auth challenge lambda needs the value of a cookie in the user's browser and the username to perform this particular authentication scenario.

If I roll it myself the challenge is that it's not integrated into the SDK and creates confusion for other developers going forward. (session, local storage, etc to say the least)

It seems completely reasonable that I should be able to do a totally custom srpless authentication using the SDK.

Does that make sense, can you explain how I do that?

@itrestian
Copy link
Contributor

Yes, I agree that it makes sense to have it integrated for the reasons you mentioned (local storage, session etc) and we will take that on. In the meanwhile our team will reach out to you with an example of passwordless authentication.

@caldwecr
Copy link
Author

Appreciated.

@ovatto
Copy link

ovatto commented Jun 13, 2017

Struggling with the same issue. Can the example for handling passwordless login be shared here?

@nenadlukic
Copy link

nenadlukic commented Jun 22, 2017

@itrestian Can you please provide an example of a passwordless server-side authentication for the rest of us that want to implement it?

@itrestian
Copy link
Contributor

There is an example in Tim Hunt's re:invent presentation (he is the last presenter):

https://www.youtube.com/watch?v=8DDIxqIW1sM

@gdw2
Copy link

gdw2 commented Jul 19, 2017

@itrestian , is Tim using this sdk or the sdk for browser (docs here).

Screenshot from YouTube video seems to indicate to me the latter:

image

In this SDK, it seems that the challenge is hard-coded to SRP_A:

https://github.com/aws/amazon-cognito-identity-js/blob/3e960090751b4d03f4acae568d2fe9c7cac21110/src/CognitoUser.js#L158

@itrestian
Copy link
Contributor

He is using the aws sdk for browser that you pointed to. The initiateAuth and respondToAuthChallenge APIs are defined there and in his example are basically used to pass parameters. We could provide wrappers for those methods in this SDK for better integration.

@gdw2
Copy link

gdw2 commented Jul 19, 2017

@itrestian , what's your recommendation? Would there be issues if we used the Cognito SDK for everything except initateAuth, for which we used the Amazon-SDK?

@itrestian
Copy link
Contributor

Don't think there would be any issue, the Cognito SDK is just providing the SRP protocol math, most of the other functions are just wrappers against the Amazon-SDK methods. And one can integrate the tokens provided by respondToAuthChallenge into the session in the Cognito SDK.

@itrestian
Copy link
Contributor

itrestian commented Jul 24, 2017

So the options are as follows:
(1) Using the main AWS SDK, call initiateAuth and respondToAuthChallenge to retrieve the session, integrate the session into a CognitoUser object from this SDK using the obtained result below.

cognitoUser.signInUserSession = cognitoUser.getCognitoUserSession(dataAuthenticate.AuthenticationResult);

(2) We can provide wrappers in this SDK in CognitoUser around initiateAuth and respondToAuthChallenge (also a custom challenge callback) that in case tokens are obtained, a session is generated. I can make this change if necessary. Any other suggestions are appreciated.

@mahbub02
Copy link

@itrestian ,
Getting the wrappers in this SDK in CognitoUser would be helpful I think.

@SebastianSchuenemann
Copy link

+1

@itrestian
Copy link
Contributor

itrestian commented Aug 4, 2017

The wrapper for initiateAuth has been added. There was already a wrapper for respondTuAuthChallenge if the challenge is a custom challenge.

@jonalexander
Copy link

does anyone have a working example of this?
i'm working on a passwordless flow but confused as how to signUp the user without a password.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

8 participants