State your question
Hi all. I'm trying to use AWS Cognito on a iOS app and I can signup users, verify their emails with my own UI and everything seems to be ok. But when I perform a signout using signOutAndClearLastKnownUser() method, and everything on keychain is cleared, I cannot begin the forgotPassword cycle. The reason is so simple: this app doesn't use username for identify users but email and phone number at your choice. Both are configured as aliases on AWS Cognito as you can see on this screenshot:

Surely, I'm forgetting something but I cannot finish a complete onboarding cycle because of this.
Just for clarification:
SignUp -> Send Verification Code -> Verify email -> SignIn ----> OK
SignOut (cleaning keychain and last known user) -> Forgot password ----> Don't know how to
I've tried to provide one of the possible aliases (phone number or email) as if it were the username, and fetch the AWSCognitoIdentityUser right from the user pool using:
cognitoPool.getUser(phone).forgotPassword().continueWith(block: { (awsTask) -> Any? in
...
})
or
cognitoPool.getUser(email).forgotPassword().continueWith(block: { (awsTask) -> Any? in
...
})
but with no succeed. I always get the error:
Error Domain=com.amazonaws.AWSCognitoIdentityProviderErrorDomain Code=34 "(null)" UserInfo={__type=UserNotFoundException, message=Username/client id combination not found.}
So the question is: Is there any way to begin the forgotPassword cycle knowing only the phone or email, used as an alias? Note that this aliases are now working for sign in.
Thank you
Which AWS Services are you utilizing?
AWS Cognito, AWS DynamoDB, AWS AppSync
Environment(please complete the following information):
Device Information (please complete the following information):
Finally I've ended up using the workaround proposed by @mrdavidrees on StackOverflow.
Basically, yo need to calculate a secret hash using the client secret, and username (or alias), with this extension
extension AWSCognitoIdentityUserPool {
func calculateSecretHash(username: String) -> String? {
guard let key = "<Your Cognito Client Secret here>".data(using: String.Encoding.ascii) else { return nil }
guard let data = (username + "<Your Cognito Client ID here>").data(using: String.Encoding.utf8) else { return nil }
let hmac = sign256(data: data, key: key)
return hmac.base64EncodedString()
}
fileprivate func sign256(data: Data, key: Data) -> Data {
let algorithm: CCHmacAlgorithm = CCHmacAlgorithm(kCCHmacAlgSHA256)
let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
let signature = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLength)
defer { signature.deallocate() }
data.withUnsafeBytes { dataBytes in
key.withUnsafeBytes { keyBytes in
CCHmac(algorithm, keyBytes, key.count, dataBytes, data.count, signature)
}
}
return Data(bytes: signature, count: digestLength)
}
}
And then, launch a forgot password request using:
let request = AWSCognitoIdentityProviderForgotPasswordRequest()!
request.username = "[email protected]"
request.clientId = "<Your Cognito Client ID here>"
request.secretHash = cognitoPool.calculateSecretHash(username: email)
AWSCognitoIdentityProvider.default().forgotPassword(request, completionHandler: { (response, error) in
...
})
It's working properly so I think this question can be closed
Hello @jorgej-ramos
While this works, I think the SDK should be able to handle this case as well. If the service allows using phone number, the forgot password should be able to trigger the flow. I will look into this and try to reproduce on my side.
Thanks,
Rohan
Hello @rohandubal
Agreed. I'm posting this workaround only for the sole purpose of illustrating the answer. But the SDK should provide an "official" method to do that.
Thank you
Hello @jorgej-ramos
I created a User Pool with a configuration similar to yours and used it with this sample app:
https://github.com/awslabs/aws-sdk-ios-samples/tree/master/CognitoYourUserPools-Sample/Objective-C
I was able to use the forgot password functionality with my registered email (without specifying my username)
Is it possible for you to try the above sample application and see if the forgot password functionality works using the email of the user?
Thanks,
Rohan
Thank you @rohandubal I'll give it a try. Just an observation: yesterday I made it work by using AWSMobileClient.sharedInstance() directly for managing signup, signin and password recovery flows (Xcode 10.1, Swift 4.1).
I think the AWS documentation has a serious lack of clarity when it comes to explaining the steps to follow to achieve an end, for example, the case that concerns us. It is very confusing when looking for any type of information, and contradictory sometimes.
Hi @jorgej-ramos,
Thank you for the feedback. Are you referring to this documentation page? https://aws-amplify.github.io/docs/ios/authentication
Hello @jorgej-ramos
As @muellerfr pointed out, we are centralizing our docs to above link.
AWSMobileClient.sharedInstance() is the recommended flow for all your auth/ user pool related operations. Were you able to do the forgotPassword flow with it as well?
Thanks,
Rohan
Hello @rohandubal and @muellerfr
Thank you both for your time and help. As @rohandubal said, I'm able to do the forgotPassword flow with AWSMobileClient.
At the beginning, I could not guess that the documentation that @muellerfr points, is the official and newest, and I was browsing many pages until I found it. Finally, it is clear to me. Thanks again to both of you.
Most helpful comment
Hello @jorgej-ramos
While this works, I think the SDK should be able to handle this case as well. If the service allows using phone number, the forgot password should be able to trigger the flow. I will look into this and try to reproduce on my side.
Thanks,
Rohan