swift 3 store code on the cloud - swift

I just started working with servers in swift and I'm using parse server hosted by AWS to store a database of users when they create an account for my app. I have this function called sign up that is linked to a sign up button which works fine and properly stores user's info into my parse database.:
#IBAction func signUp(_ sender: AnyObject) {
//I first check to see if the users left any of the fields blank
if firstName.text == "" || lastName.text == "" || email.text == "" || userName.text == "" || password.text == "" {
createAlert(title: "Error in form", message: "Please fill in all text fields")
//If everything is filled in
}else{
let user = PFUser()
user.username = userName.text
user["firstname"] = firstName.text
user["lastname"] = lastName.text
user.email = email.text
user.password = password.text
user.signUpInBackground(block: { (success, error) in
if error != nil {
var displayErrorMessage = "Please try again later."
if let errorMessage = (error! as NSError).userInfo["error"] as? String {
displayErrorMessage = errorMessage
}
self.createAlert(title: "Signup error", message: displayErrorMessage)
}else{
print("User signed up")
}
})
}
}
Now i need help storing this code on the cloud.
The reason why I ask this is because I had previously posted a question a little different on stack overflow: Deleting PFUser objects from database in swift
And in the response, I was told to store this exact function in cloud code (rather than locally) and I don't know how to do that.
Can anyone show me how it's done?
Thanks a lot in advance!

I'm assuming what it means when you want to "store your code in the cloud" is that all personal info (name, email, password) should be handled from the backend, not that you actually store the code in the cloud. You should handle sensitive personal data from the server-side and not the client-side. This involves server-side encryption and handling requests with SSL exchanges. This is beyond the scope of my expertise and this question, and I highly encourage that you do more research before you try to tackle this problem in your app. Here are some links I found:
http://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html
https://www.raywenderlich.com/92667/securing-ios-data-keychain-touch-id-1password

What they told you is to implement that code in the server, the server should be the one to process the function you ask for in the past question, this one, I think you have already done it because you said the info is stored successfully, but what the answer above this says is that to implement that code in an actual app you should investigate more about security and encryption to protect users information.

Related

Swift 5 & Firebase: Creating a shared sign in and sign-out button

I thought I'd return to StackOverflow with another question because you guys helped me significantly with my last issue
Anyway, I currently have my authentication system setup so that the sign in and signup button are shared. I am looking to have firebase reference storage when an email is entered to have it checked against other accounts in the database. As of right now, a user can enter an email address for their account and then if they enter the wrong password it just sends them right to the sign up even though they currently have an account. This is a serious problem as it will cause confusion
I want it to work like so:
If the email address is taken, I want an alert to be displayed for the user says "Incorrect password"
If the email address is not taken, I want it to tell the user that they need to enter a password with at least 10 characters, 1 number, and 1 special character, which I have already figured out using
I only want it to segue to create a new user if the email is not taken and the password and email field meet the criteria fields that I have already set within my code. I just need help preventing it from switching to the create new user VC if the email is already taken, and I need to to say
func isValidPassword(_ email: String) -> Bool {
let emailRegEx = "##$%^&+=^.*(?=.{10,})(?=.*d)(?=.*[a-z])(?=.*[A-Z])(?=.*[##$%^&+=]).*$"
let emailPred = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
return emailPred.evaluate(with: email)
}
Anyway, here is the code so far attached to the IBAction
if let email = emailField.text, let password = passwordField.text {
Auth.auth().signIn(withEmail: email, password: password, completion:
{ (user,error) in
if error == nil {
if let user = user {
self.userUid = user.user.uid
self.goToFeedVC()
}
} else {
self.goToCreateUserVC()
Here is a picture of the interface
I want it to be intuitive but I have been unable to code this myself so if anyone is able to help advise me on how to finish this block it would be incredibly appreciated
Firebase gives pretty detailed error responses for their Auth call:
So you can check to see what the error is inside of your call:
Below are the two error that they give (I only added the two scenarios that you mentioned)
Description: The password is invalid or the user does not have a password.
FIRAuthErrorUserInfoNameKey: ERROR_WRONG_PASSWORD
&
There is no user record corresponding to this identifier. The user may have been deleted.
FIRAuthErrorUserInfoNameKey: ERROR_USER_NOT_FOUND
Auth.auth().signIn(withEmail: email, password: password, completion:
{ (user,error) in
if error == nil {
if let user = user {
self.userUid = user.user.uid
self.goToFeedVC()
}
} else {
guard let error = error?.localizedDescription else { return } // but actually handle this
print(error)
if error == wrong password {
// show alert for email taken/wrong password
} else if error == user doesnt exists {
// self.goToCreateUserVC()
}
}
}
Just replace the if and else if conditions with the actual errors. I'd avoid comparing the strings and use the key/code in case the strings change in the future.
Official list of error codes can be found here
And if you print the full error instead of the error?.localizedDescription you'll get the full details, as can be seen here:
Optional(Error Domain=FIRAuthErrorDomain Code=17011 "There is no user record corresponding to this identifier. The user may have been deleted." UserInfo={NSLocalizedDescription=There is no user record corresponding to this identifier. The user may have been deleted., FIRAuthErrorUserInfoNameKey=ERROR_USER_NOT_FOUND})

How To Fetch Error Code From Firebase?

Auth.auth().signIn(withEmail: emailTextField.text!, password: passwordTextField.text!)
{ (user, error) in
if error != nil {
print(error!)
self.warningLabel.isHidden = false;
self.passwordTextField.text = "";
} else {
print("Log in succesful")
self.performSegue(withIdentifier: "welcomeSeg", sender: self)
}
}
Whenever I sign in or sign up a user I just print a generic warning label instead of the actual issue. I print the error I receive and it's too verbose to show to the user.
Error Domain=FIRAuthErrorDomain Code=17009 "The password is invalid or the user does not have a password." UserInfo={NSLocalizedDescription=The password is invalid or the user does not have a password., error_name=ERROR_WRONG_PASSWORD}
Error Domain=FIRAuthErrorDomain Code=17008 "The email address is badly formatted." UserInfo={NSLocalizedDescription=The email address is badly formatted., error_name=ERROR_INVALID_EMAIL}
Is there any way to fetch the error code so I can be more specific with my error messages? I've looked through the documentation but have been unsuccessful in coming up with anything.
I would recommend creating an AuthErrorCode object (provided by the Firebase SDK) from the error you receive and using that as you see fit. If I remember correctly, AuthErrorCode is an enum with cases like .wrongPassword, .invalidEmail, etc.
A simple pseudocode example:
if error != nil {
if let error = AuthErrorCode(rawValue: error?.code) {
switch error {
case .wrongPassword:
// do some stuff with the error code
}
}
Also, I feel your pain. I've found that the Swift SDK documentation lags quite a bit when changes come along.

Deleting PFUser objects from database in swift

I just started working with servers in swift and I'm using parse server to store a database of users when they create an account for my app. I have this function called sign up that is linked to a sign up button which works fine and properly stores user's info into my parse database.:
#IBAction func signUp(_ sender: AnyObject) {
//I first check to see if the users left any of the fields blank
if firstName.text == "" || lastName.text == "" || email.text == "" || userName.text == "" || password.text == "" {
createAlert(title: "Error in form", message: "Please fill in all text fields")
//If everything is filled in
}else{
let user = PFUser()
user.username = userName.text
user["firstname"] = firstName.text
user["lastname"] = lastName.text
user.email = email.text
user.password = password.text
user.signUpInBackground(block: { (success, error) in
if error != nil {
var displayErrorMessage = "Please try again later."
if let errorMessage = (error! as NSError).userInfo["error"] as? String {
displayErrorMessage = errorMessage
}
self.createAlert(title: "Signup error", message: displayErrorMessage)
}else{
print("User signed up")
}
})
}
}
Can anyone help me write a function that deletes a specified user or many loops through all users and deletes them.
Thanks
Don't put that code on a client. Yuck.
Deleting a user is something you want to safeguard very carefully. Put that in cloud code if you really need it, and do validation to ensure it's used properly.
I.e., a user should only be able to make the request to delete their own user. Make a cloud code function that has the parameter userId, and check that param against request.user.id. If they don't match, return an error. Otherwise you can call the destroy method on that User.
You should also put a CLP in place to only allow the master key to delete a User object.

compare textfield.text to firebase string swift

I have a database in Firebase that will have individual user nodes. In each user's node will be data pertaining to them and will be private. In addition to that I want to create a node that is JUST a collection of registered emails. The reason is when a user is on the Sign In VC and the user types an email in..If the email is already registered an image view will turn green. However, if the email is not in the database (or if it doesn't match email address format) the image will be red.
A previous answer on my previous question(s) illustrated that I need to change the '.' to a ',' in email addresses. So #gmail.com would be stored as gmail,com
I have that down.
FIRAuth.auth()?.createUser(withEmail: email, password: password, completion: { (user, error) in
if error == nil {
let email = firstContainerTextField.text ?? ""
let newString = email.replacingOccurrences(of: ".", with: ",", options: .literal, range: nil)
self.ref.child("users").child("allUsers").child(newString).setValue(true)
self.ref.child("users").child((user?.uid)!).setValue(["Email": email])
FIRAuth.auth()!.signIn(withEmail: email,
password: password)
} else {
//registration failure
}
This is the code from the New User VC (partial).
So the node that says "users" and "allUsers" looks like this on Firebase Console
users
allUsers
bob#bob,com: true
ted#ted,com: true
The 'true' part was just so I could get the bob#bob,com onto the database...the true part will never be used for anything.
On the log in VC I honestly cannot figure out what to do
A previous answer said to use
hasChildren()
And I used that and then googled what to do with that
and I tried using something like this
ref.child("users").child("allUsers").queryEqual(toValue: newString)
.observe(.value, with: { snapshot in
if snapshot.hasChildren() {
for child in snapshot.children.allObjects as! [FIRDataSnapshot] {
....
}
});
But I just cannot seem to get anywhere with it.
How can I simply see if a textfield.text == an email already stored in firebase?
(I did convert the '.' to ',' when comparing)
Please don't use email addresses as keys. Email addresses are dynamic and may change (as in if the users wants to change it) and if they do, you'll have a mess on your hands as every node that directly used that email would have be deleted and re-created.
Best practice is to disassociate key's from the data they contain.
Here's the structure to use
emails
-Yiaisjpa90is
email: "dude#test.com"
-Yijs9a9js09a
email: "thing#test.com"
then you simply query the email node for the email you are looking for, and handle accordingly if it exists.
And some code
emailsRef.queryOrdered(byChild: "email").queryEqual(toValue: "dude#test.com")
.observe(.value, with: { snapshot in
if snapshot.value is NSNull {
print("the snapshot was null, no email found")
} else {
print("email was found, YIPEE")
}
})
For completeness it would be a little more Swifty to use
if snapshot.exists() {
print("found it")
} else {
print("no email found")
}

How to check if user needs to re-authenticate using Firebase Authentication

I am using Firebase to log in users into my app, but when I am adding the capability to manage their account like changing their email, password and so on. The documentation says that if the user have not recently signed in they need to re-authenticate, but my question is: How can I check if the user have signed in recently or not? According to the docs the error will return FIRAuthErrorCodeCredentialTooOld, but how can I check this?
Swift 3
I had to do this yesterday when trying to delete a user. One thing to note is FIRAuthErrorCodeCredentialTooOld is now FIRAuthErrorCode.errorCodeRequiresRecentLogin
What I did was trigger a UIView to ask for log in details if that error is thrown. Since I was using email and password, that's what I collected from the user in my example.
private func deleteUser() {
//get the current user
guard let currentUser = FIRAuth.auth()?.currentUser else { return }
currentUser.delete { (error) in
if error == nil {
//currentUser is deleted
} else {
//this gets the error code
guard let errorCode = FIRAuthErrorCode(rawValue: error!._code) else { return }
if errorCode == FIRAuthErrorCode.errorCodeRequiresRecentLogin {
//create UIView to get user login information
let loginView = [yourLoginUIViewController]
self.present(loginView, animated: true, completion: nil)
}
}
}
Once I had the login information I ran this function to reauthenticate the user. In my case I ran it the loginView in the above code if the login in was successful:
func reauthenticateUserWith(email: String, password: String) {
FIRAuth.auth()?.signIn(withEmail: email, password: password) { (user, error) in
if error == nil {
//display UIView to delete user again
let deleteUserView = deleteUserView()
present(deleteUserView, animated: true, completion: nil)
} else {
//handle error
print(error!.localizedDescription)
}
}
}
The deleteUserView in my case calls deleteUser() on a button tap from the user. You can also use UIAlertController in place of the custom UIViews, but that's up to you.
Hope this helps.
Update for current Swift 5
let user = Auth.auth().currentUser
user?.delete { error in
if let error = error {
let authErr = AuthErrorCode(rawValue: error.code)
if authErr == .requiresRecentLogin {
// reauthenticate
}
// other error
} else {
// delete success
}
}
According to the documents, there is currently no way to check FIRAuthErrorCodeCredentialTooOld other than going through the deleting of the account or the other sensitive cases mentioned.
If you are like me and ended up here because you are trying to figure out how to handle removing someone from Auth and removing other user data in Cloud Firestore, Realtime Database, and/or Cloud Storage, then there is a better solution.
Check out the Delete User Data Extension from Firebase to handle this. In short, when a user profile is deleted from Auth, you can use this also to delete data associated with the uid from those other Firebase data storage tools.