I'm trying to migrate a batch of users into Cognito via a CSV (i.e. one-time import). This part works successfully and I can see the users in the User Pool. As expected, I see the user with a status of "Enabled / RESET_REQUIRED". So far so good, right?
When I try to sign-in to the user in my mobile app, it throws an error of "userNotFound" instead of the "passwordResetRequired" which would trigger the proper flow.
Has anyone seen this? Or know how to solve this problem?
Just for grins, here is my code:
private func signInWithAWS(email: String, password: String) -> Observable<AWSSignInResult> {
Observable<AWSSignInResult>.create { observer in
AWSMobileClient.default().signIn(
username: email.lowercased(),
password: password,
completionHandler: { signInResult, error in
if let signInResult = signInResult {
switch signInResult.signInState {
case .signedIn:
observer.onNext(.success)
observer.onCompleted()
default:
observer.onCompleted()
}
} else if let error = error as? AWSMobileClientError {
if case AWSMobileClientError.userNotConfirmed = error {
observer.onNext(.unconfirmed)
observer.onCompleted()
return
} else if case AWSMobileClientError.passwordResetRequired = error {
observer.onNext(.resetPasswordRequired)
observer.onCompleted()
}
let message = getCognitoErrorMessage(error: error)
observer.onError(AuthServiceError.signIn(message))
}
}
)
return Disposables.create()
}
}
It fails the "userNotConfirmed" and "passwordResetRequired" checks into the generic handler because the actual error is "userNotFound".
Related
I am using AWS authentication on my app. And I want to create a global function for fetching the user token so I can access it anywhere in the app whenever I need to fetch the new id token, the token is changing every 5 mins so I need to call it from time to time. I do it like this
func fetchSession() {
Amplify.Auth.fetchAuthSession { result in
do {
let session = try result.get()
// Get cognito user pool token
if let cognitoTokenProvider = session as? AuthCognitoTokensProvider {
let tokens = try cognitoTokenProvider.getCognitoTokens().get()
return tokens.idToken
}
} catch {
print("Fetch auth session failed with error - \(error)")
}
}
}
but I am getting this error
Unexpected non-void return value in void function
I also try to make it like this
func fetchSession() {
let token = Amplify.Auth.fetchAuthSession { result -> AnyObject in
do {
let session = try result.get()
// Get cognito user pool token
if let cognitoTokenProvider = session as? AuthCognitoTokensProvider {
let tokens = try cognitoTokenProvider.getCognitoTokens().get()
return tokens.idToken
}
} catch {
print("Fetch auth session failed with error - \(error)")
}
}
}
and this is the error I got
Cannot convert value of type '(AmplifyOperation<AuthFetchSessionRequest, AuthSession, AuthError>.OperationResult) -> AnyObject' (aka '(Result<AuthSession, AuthError>) -> AnyObject') to expected argument type '((AmplifyOperation<AuthFetchSessionRequest, AuthSession, AuthError>.OperationResult) -> Void)?' (aka 'Optional<(Result<AuthSession, AuthError>) -> ()>')
Looks like you need to specify what fetchSession will return. Right now you are saying it will return Void because you have neglected the return type.
Here is an example of specifying the returned type.
func fetchSession() -> String? {
// ...
do {
// ...
return tokens.idToken // I assume idToken is a String
} catch {
print("Fetch auth session failed with error - \(error)")
return nil
}
}
Do not forgot to return nil in the catch or rethrow the error.
Swift docs on functions: https://docs.swift.org/swift-book/LanguageGuide/Functions.html
I am trying to chain some API calls and I think I am confusing some concepts. Would love some clarification & code samples.
I have implemented these functions...
func promiseFetchPayments(for accountId: String) -> Promise <[OperationResponse]> {
return Promise <[OperationResponse]> { seal in
payments(for: accountId) { (records, error) in
if let recs = records {
seal.resolve(.fulfilled(recs))
return
}
if let e = error {
seal.reject(e)
return
}
}
}
}
and
func payments(for accountId: String, completion: #escaping (_ records: [OperationResponse]?, _ error: Error?) -> Void) {
stellar.payments.getPayments(
forAccount: accountId,
order: Order.descending,
limit: 10
) { response in
switch response {
case .success(let paymentsResponse):
DispatchQueue.main.async {
completion(paymentsResponse.records, nil)
}
case .failure(let error):
DispatchQueue.main.async {
completion(nil, error)
}
}
}
}
I am trying to use it like so:
firstly {
promiseFetchPayments(for: "XXX")
}.done { records in
print(records)
} .catch { error in
print(error)
}
Now this actually ^^^ works OK!!!! My problem is I want to be able to change done to then and be able to chain another function / response or many more.
But the error I keep getting is:
Cannot conform to Thenable.
I am looking for something very similar to this (I know the syntax isn't right just logically follow the chain....
firstly {
stellar.promiseFetchPayments(for: "")
}.done { records in
print(records)
}.then {
// call some other method
}.done { data in
// more data
}.catch { error in
print(error)
}
Is this actually possible? Can't seem to get any tutorials on the interwebs to compile. Seems Swift compiler really doesn't like PMK syntax or something.
Any ideas?
The problem is because you're chaining off of a done, which doesn't like that you're trying to then do a call to then off of that.
Instead, you'll want to save the promise and use it for the later calls. You can do something like this:
let promise = firstly {
stellar.promiseFetchPayments(for: "")
}
promise.done { records in
print(records)
}
promise.then {
// call some other method
}.done { data in
// more data
}.catch { error in
print(error)
}
You can even return that promise from a method to use in other places, or pass it around to another method.
I'm actually trying to some code logic with swift for training purpose at the moment, I was wondering what is the proper way to throw my error from my init ?
So the flow is Controller ask for account creation when initializing Model is asking my sql manager to create the account and this method return the result from a closure.
But something feels wrong, should I just use a return from the sql manager who contained both my Int? and Error? ?
init(_ username: String, _ password: String) throws {
self.id = 0
self.username = username
self.password = password
var toThrow: Error? = nil
// Register in database
userManager.create(self) { (id: Int?, err: Error?) in
Thread.sleep(forTimeInterval: 10)
if let error = err {
// Register in database goes wrong
debugPrint("Handle error from user creation...")
toThrow = error
} else {
// There is no id and no error ?
guard let _ = id else { return }
self.id = id!
}
}
if let error = toThrow {
throw error
}
}
If you are on Swift 5 you could look into using Result and define your closure like
(id: Int) -> Result<Int, Error>
and change your code to
userManager.create(self) { (id: Int?) -> Result<Int, Error> in
Thread.sleep(forTimeInterval: 10)
if let error = err {
// Register in database goes wrong
debugPrint("Handle error from user creation...")
return .failure(error)
} else {
// There is no id and no error ?
guard let _ = id else { return }
return .success(id)
}
}
If you have your own error enum for the Db class like
enum DbError {
case create
case update
//...
}
Then you can use that type in the closure declaration
(id: Int?) -> Result<Int, DbError>
and return a specific error for this action
return .failure(.create)
Note that I haven't compiled this so consider it an example
Here is the solution if people want to see:
#IBAction func didPressRegister() {
guard let username = usernameField.text else { return }
guard let password = passwordField.text else { return }
let user = UserModel(username, password)
userManager.create(user) { result in
switch(result) {
case .failure(let error):
// TODO: UIAlert
debugPrint(error)
case .success(let int):
// TODO: Generate user token and redirect main
debugPrint(int)
}
}
}
// TODO
public func create(_ user: UserModel, _ complete: #escaping (Result<Int, Error>) -> ()) {
debugPrint("Requested to create the user... \(user)")
complete(.failure(toThrow.ACCOUNT_ERROR))
}
I am working on a function that handles user registration and in the process, check if the selected username entered by the user is taken or not to inform the user to select a different one. I have the below code to accomplish this scenario:
#IBAction func proceedPressed(sender: AnyObject) {
/**********************Perform Validation************************/
if(self.emailTxtField.text != "" && self.passwordTxtField.text != "")
{
print("Email and Password not empty")
self.usernameValidation({(result) -> Void in
if(result == false)
{
print("Result False")
self.usernameErrorLabel.text = "Username Taken"
}else{
print("Result True")
//Username is available...Proceed
self.usernameErrorLabel.text = ""
FIRAuth.auth()?.createUserWithEmail(self.emailTxtField.text!, password: self.passwordTxtField.text!) { (user, error) in
if(error == nil)
{
print("Creating User with Email")
/*Create the user object as submitted*/
self.dbReference.child("users").child(user!.uid).setValue(["username": self.emailTxtField.text!,"name":self.nameTxtField.text!, "email":self.emailTxtField.text!, "mobile":self.mobileTxtField.text!, "homeAddress":"N", "workAddress":"N", "otherAddress":"N", "profilePictureRef":"N","telephone":"0","friendsCount":0, "retailersCount":0])
}else{
print("Error occured: \(error?.description)")
}
}//end of createUserWithEmail
}
})
}else{
print("Error: Email or Password field is empty")
}
}
and to check the username:
func usernameValidation(completion: (result: Bool) -> Void)
{
print("Username is: \(self.usernameTxtField.text!)")
dbReference.child("usernamesTaken").queryOrderedByValue().queryEqualToValue(self.usernameTxtField.text!).observeEventType(.Value, withBlock: { (snapshot: FIRDataSnapshot!) -> Void in
print(snapshot.childrenCount)
if(snapshot.childrenCount == 0)
{
print("result is true in username validation")
//Username Available
completion(result:true)
}else{
print("result is false in username validation")
//Username Taken
completion(result:false)
}
})
}
The problem with the above is that the full code doesn't seem to execute. When button pressed, I get the following messages in console:
- Email and Password not empty
- Username is: [value entered in usernameTxtField.text
and then nothing more. Although I wrote many print statements to try and see where this is stopping, but this is the furthest the code went in terms of printing the statements.
Is there something wrong here that I am missing out?
Thanks in advance.
I did some more testing and then discovered the issue through the xcode console. I copied the following from the firebase website to test fetching the data:
ref.child("users").child(userID!).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
// Get user value
let username = snapshot.value!["username"] as! String
let user = User.init(username: username)
// ...
}) { (error) in
print(error.localizedDescription)
}
The above showed an error that is "Permission Denied". Following that I edited the Rules in the database section in the console and allowed .read and .write and that did it. I thought I would post the details just in case someone else gets stuck.
I am having an issue using Xcode 6.2 and Swift where I have a tuple that I am returning from a function. I have a subfunction that is running inside the function that is authenticating a user into our datastore and then returning the authentication tokens. If the user does not authenticate, i.e., has an error, then I am returning that correctly to the calling function. If the user passes authentication then I am only passing nil back to the calling controller even though I am making the same variable assignments. Here is the code that we are using:
func login(email:String, password:String) -> (uid: String?, provider: String?, error: NSError?) {
var errorStatement: NSError?
var provider: String?
var testResult: String?
var authData: FAuthData
ref.authUser(email, password: password) {
error, authData in
if error != nil {
// an error occured while attempting login
println("error is: \(error)")
errorStatement = error
testResult = "failed"
provider = "Error"
} else {
// user is logged in, check authData for data
testResult = "passed"
provider = authData.provider
userUID = authData.uid
println("User uid = \(userUID) and provider = \(provider)")
}
}
return (testResult, provider, errorStatement)
}
I am getting perfectly what I would expect when it has an error, but nothing when it is fine. I know this is going to be some simple fix but I cannot seem to find it. Thanks for any help as I am new to this language.
Here is the corrected code after I implemented the completion as suggested by #rdelmar
func login(email:String?, password:String?, completion: (result: String?, errorDesc: String?) -> Void) {
var errorStatement: String?
var testResult: String?
ref.authUser(email, password: password) {
error, authData in
if error != nil {
// an error occured while attempting login
println("error is: \(error)")
println(error.domain)
errorStatement = "errpor"//error.localizedDescription
testResult = "failed"
completion(result: testResult, errorDesc: errorStatement) //send the calling function the attached information
} else {
// user is l ogged in, check authData for data
testResult = "passed"
userUID = authData.uid
completion(result: testResult, errorDesc: errorStatement)//send the calling function the attached information
}
}
}