Closed anees17861 closed 4 years ago
Hi @anees17861
Thanks for detailed explanation of your issue, just a quick note before we dive further, we are currently working on custom auth with AWSMobileClient and will be releasing that soon (in PR here https://github.com/aws-amplify/aws-sdk-ios/pull/1860 ). Looping in @royjit to see if he can identify that this would work for your use case as he has some more context on custom auth.
Yes, looks like you can use AWSMobileClient with custom authentication. This should be available in the next release.
What is the sequence of your auth flow? Do you have username/password as the first flow? Can you share your Define Auth Challenge
lambda trigger?
I have something similar to Amazon app login. User can login with either phone number/password or with otp. For password flow I call getsession with username and password. For otp flow I set a delegate and call getsession without any parameters.
As for define auth challenge, I don't have access to a desktop right now, so I'll share it later. But it's a word to word copy of the trigger provided in the aws reinvent video (link in previous comment)
Define auth challenge
exports.handler = function (event,context,callback) {
console.log('EVENT request', event.request);
// TODO implement
if(event.request.session.length === 0){
event.response.issueTokens = false;
event.response.failAuthentication = false;
event.response.challengeName = 'CUSTOM_CHALLENGE';
}
else if(event.request.session.length === 1
&& event.request.session[0].challengeName === 'CUSTOM_CHALLENGE'
&& event.request.session[0].challengeResult === true){
event.response.issueTokens = true;
event.response.failAuthentication = false;
}
else {
event.response.issueTokens = false;
event.response.failAuthentication = true;
}
console.log('EVENT response', event.response);
callback(null,event);
};
Create auth challenge
var AWS = require('aws-sdk');
exports.handler = function (event,context,callback) {
console.log('EVENT request', event.request);
// TODO implement
if(event.request.session.length === 0
&& event.request.challengeName === 'CUSTOM_CHALLENGE' ){
//Create a otp
var answer = Math.random().toString(10).substr(2,6);
//sns sms
var sns = new AWS.SNS({region:'ap-southeast-1'});
sns.publish({
Message: 'your otp: '+answer,
PhoneNumber: event.request.userAttributes.phone_number
},function(err,data){
if(err){
console.log(err.stack);
return;
}
console.log(`SMS sent to ${event.request.userAttributes.phone_number} and otp = ${answer}`);
});
//set return params
event.response.publicChallengeParameters = {};
event.response.privateChallengeParameters = {};
event.response.privateChallengeParameters.answer = answer;
event.response.challengeMetadata = 'PASSWORDLESS_CHALLENGE';
}
console.log('EVENT response', event.response);
callback(null,event);
};
Verify Auth Challenge
exports.handler = function (event,context,callback) {
console.log(`EVENT request`,event.request);
// TODO implement
event.response.answerCorrect = (event.request.privateChallengeParameters.answer === event.request.challengeAnswer);
console.log(`isAnswerCorrect = ${event.response.answerCorrect}`)
if(!event.response.answerCorrect){
console.log(`Correct answer = ${event.request.privateChallengeParameters.answer}`);
console.log(`Given answer = ${event.request.challengeAnswer}`);
}
console.log(`EVENT response`,event.response);
callback(null,event);
};
Sorry for the delay, got occupied elsewhere
@anees17861 Thank you for providing the details. I was able to make this work by returning an empty value for the first invocation of getCustomChallengeDetails
and on the second invocation pass the challenge response to the continuation.
if (authenticationInput.challengeParameters.count > 0) {
let details = AWSCognitoIdentityCustomChallengeDetails(challengeResponses: ["ANSWER" : "1133"])
customAuthCompletionSource.set(result: details)
} else {
let details = AWSCognitoIdentityCustomChallengeDetails()
customAuthCompletionSource.set(result: details)
}
Can you please check if this works for you?
self.otpLoginContinuation = customAuthCompletionSource
if(authenticationInput.challengeParameters.count == 0) {
print("sending user name")
customAuthCompletionSource.set(result: AWSCognitoIdentityCustomChallengeDetails())
}
Currently I am doing this since I need to wait for the user to enter the otp. It works fine. But is this the expected way?
Also how should I handle didCompleteStepWithError(_ error : Error?)? It gets fired in both cases. I have to give feedback to the user if otp was success. The only way I can think of is simply maintaining a boolean flag.
Unfortunately this is how the SDK is setup and I cannot find an easy way to handle this. One work around is to listen to the getSession() task and handle success:
self.currentUser?.getSession().continueWith(block: { (task) -> Any? in
if let session = task.result {
// Auth completed.
}
return nil
})
We will take this up as a feature request and will update here when we implement this.
Hi,
Thanks for all the help. The login functionality is working fine. But there is another issue I'm facing now.
I'm using Cognito to sign network api calls. To achieve this I'm calling getsession before every api call. This causes the delegate to fire up again. And I end up receiving multiple otps. I tried to set delegate to nil on continueWith() but that was not possible since it's not an optional. How should I solve this. Also if there is a better way to sign the api than calling get session please let me know
@royjit does AWSMobileClient work with this type of custom auth (passwordless) ?
Apologies for the delayed response
@anees17861
getCustomChallengeDetails
delegate method will be fired only when the refresh token is expired and the user has to sign In again.
If you are still seeing this problem please open a new issue with details and we will investigate.
@dholdren Yes, AWSMobileClient supports custom auth. You can find more details here.
This issue has been automatically closed because of inactivity. Please open a new issue if are still encountering problems.
Hi @royjit,
I'm also facing the same issue that @anees17861 was facing.
So, My log in functionality is working fine but receiving multiple OTPs. Can you please help how to solve this.
@anees17861 Could you please help me if how you solved the multiple OTPs issue??. Thanks in advance :)
My code:
CreateAuthChallenge
const AWS = require('aws-sdk'); exports.handler = (event, context, callback) => { //Create a random number for otp const challengeAnswer = Math.random().toString(10).substr(2, 6); const phoneNumber = event.request.userAttributes.phone_number; //sns sms const sns = new AWS.SNS({ region: 'us-east-1' }); sns.publish( { Message: 'your otp: ' + challengeAnswer, PhoneNumber: phoneNumber, MessageStructure: 'string', MessageAttributes: { 'AWS.SNS.SMS.SenderID': { DataType: 'String', StringValue: 'AMPLIFY', }, 'AWS.SNS.SMS.SMSType': { DataType: 'String', StringValue: 'Transactional', }, }, }, function (err, data) { if (err) { console.log(err.stack); console.log(data); return; } console.log("Data In Create", data); return data; } ); //set return params event.response.privateChallengeParameters = {}; event.response.privateChallengeParameters.answer = challengeAnswer; event.response.challengeMetadata = 'CUSTOM_CHALLENGE'; callback(null, event); };
Define Auth Challenge
exports.handler = (event, context) => { if (event.request.session.length === 0) { event.response.issueTokens = false; event.response.failAuthentication = false; event.response.challengeName = 'CUSTOM_CHALLENGE'; } else if ( event.request.session.length === 1 && event.request.session[0].challengeName === 'CUSTOM_CHALLENGE' && event.request.session[0].challengeResult === true ) { event.response.issueTokens = true; event.response.failAuthentication = false; } else { event.response.issueTokens = false; event.response.failAuthentication = true; } context.done(null, event); };
Pre sign-up Function
exports.handler = (event, context, callback) => { // Confirm the user event.response.autoConfirmUser = true; // Set the email as verified if it is in the request if (event.request.userAttributes.hasOwnProperty('email')) { event.response.autoVerifyEmail = true; } // Set the phone number as verified if it is in the request if (event.request.userAttributes.hasOwnProperty('phone_number')) { event.response.autoVerifyPhone = true; } // Return to Amazon Cognito callback(null, event); };
Verify function
exports.handler = (event, context) => { if (event.request.privateChallengeParameters.answer === event.request.challengeAnswer) { event.response.answerCorrect = true; } else { event.response.answerCorrect = false; } context.done(null, event); };
@Purnachndar In my case problem was on iOS side. Can you verify wether your lanbda triggers properly when done through Android? The sdk in Android is pretty stable and straightforward for custom auth.
@Purnachndar In my case problem was on iOS side. Can you verify wether your lanbda triggers properly when done through Android? The sdk in Android is pretty stable and straightforward for custom auth.
Thanks for the reply. The thing is we are building the app in react which will be using for both android, IOS, and web as well. Btw my lambda triggers are working fine but I'm receiving multiple OTPs once I give a user phone number to sign in.
@Purnachndar Sorry to say but in my case issue was purely on using native iOS cognito sdk. By react I'm guessing you are using the amplify library which I've not yet gotten around to use.
What happened in iOS was there was a function called getcustomauthchallenge. In Android this function was called only after create auth challenge trigger sent otp which made it straightforward to use. In case of iOS it was called twice. At first it was called independently with no challenge parameters. At this point i had to set response with default object which actually caused the OTP to be sent. After sending, the getcustomauthchallenge was called again making it fall into an infinite loop. I fixed this by making a check for challenge parameters count. If 0 means it was called first time by the sdk. If greater than 0, it was called after sending OTP and hence nothing to do but wait until user enters OTP.
I've not gone through the docs of amplify react so I'm not sure about the callbacks, but if similar then problem could be same.
State your question How to use Cognito iOS SDK for custom Authentication?
I have followed AWS re:Invent 2016: Add User Sign-In, User Management, and Security with Amazon Cognito (MBL310) to setup the lambdas.
I'm able to successfully receive the sms and login using android sdk but not sure how the process works in iOS
I've attached the code below. Here the startCustomAuthentication gets called properly and right after that getCustomChallengeDetails is called as well. But the authentication input returns empty. In android when the same callback is called the cloud has done sending the sms and all I have to is set the result to the continuation once the user enters it
I observed that the DefineAuthChallenge trigger is never fired in case of iOS and thus the sms is never sent
One more thing I observed is if I set an empty result in getCustomChallengeDetails (commented in the code) it sends the sms but startCustomAuthentication gets called again making it fall into an infinite loop.
Which AWS Services are you utilizing? Cognito Lambda
Provide code snippets (if applicable)
Call to initiate custom auth
Interactive Authentication delegate
Custom Auth delegate
Once user enters the OTP
Environment(please complete the following information):
Device Information (please complete the following information):