Firebase check if account is disabled - swift

I am currently trying to create a function that automatically detects if an account has been disabled or not.
By this, I mean that I want the user to be logged out automatically, and not have the permission to do anything.
I know the stuff about permissions are done in firebase security & rules. However, I have no idea on how to disallow activity from disabled accounts. I am only familiar with the auth.uid and variables.
How should I proceed if I want to do this?
I have played with the idea of re-authenticating the user for each form it proceeds to, but I quickly figured out that this would be unnecessary use of data.
Or is this the way to go?

One of the way is use of authentication. You need to re-authenticate the firebase user:
user?.reauthenticateWithCredential(credential) { error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
}
}
You get FIRAuthErrorCodeUserDisabled error if account is disabled.
Run this in a loop at some time-interval.

I reloaded the current User, if it's disabled you will get an error:
if let userInfo = Auth.auth().currentUser {
userInfo.reload(completion: { (error) in
guard error == nil else {
debugPrint(error.debugDescription)
return
}
})
}

Your asked a while ago, hope answer still helps:
You can add an observer for authStateDidChange, so it got fired if user logged out
NotificationCenter.default.addObserver(forName: NSNotification.Name.AuthStateDidChange, object: Auth.auth(), queue: nil) { _ in
if ((Auth.auth().currentUser) == nil) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let mvc = storyboard.instantiateViewController(withIdentifier: "MainViewController")
self.window!.rootViewController = mvc
let options: UIView.AnimationOptions = .transitionCrossDissolve
let duration: TimeInterval = 0.1
UIView.transition(with: self.window!, duration: duration, options: options, animations: {}, completion:
{ completed in
self.window?.rootViewController?.performSegue(withIdentifier: "showWelcomeView", sender: nil)
})
}
}
The cool thing now is if you call the reload command, like MegaChan mentioned before, and the user got disabled or deleted, the firebase controller logs the user out, the observer fires authstate did change and your login screen, in my case welcomeviewcontroller shows up ;)

Related

How to Sign out and Delete Users Register By FirebaseUI Swift 5

I want to give my app the ability to Log out/delete the user and when I type this code
#IBAction func deleteTheAccountButtonHasBeenTapped(_ sender: Any) {
let user = Auth.auth().currentUser
var credential: AuthCredential
user?.reauthenticateAndRetrieveData(with: credential, completion: {(authResult, error) in
if let error = error {
// An error happened.
print(error)
}else{
//user re-authenticated
user?.delete { error in
if let error = error {
// An error happened.
print(error)
} else {
// Account deleted.
let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "LoginVC") as! LoginVC
self.present(vc, animated:true, completion:nil)
}
}
}
})
}
I got this error:
Variable 'credential' used before being initialized
any one can help me?
The error message is pretty explicit: you're using credential before you initialized it.
In order to delete the user, you need to first reauthenticate them, as shown in the documentation on reauthenticating the user. Your version doesn't implement this comment from that code:
// Prompt the user to re-provide their sign-in credentials
How to reauthenticate the user depends on the provider. For example for email/password, you'd get them with:
credential = EmailAuthProvider.credential(withEmail: email, password: password)
A handy place to find similar snippets for other providers is in the documentation on linking accounts.

Checking Sign-in Status for Each ViewController

I am a beginner. I am developing an app with swift. I am using Firebase for signin. But I was wondering if I need to write codes to check user's sign-in status for each viewController for security. Or sign-in is needed only for the sign-in viewController like locking just a front door. My code may not be needed but below is the code that I wrote:
// Mark: User Sign-in Status Recheck
Auth.auth().addStateDidChangeListener { auth, user in
if user != nil {
if let user = user {
let userUid = user.uid
let userEmail = user.email
}
} else {
let InitialSignInFirstViewController = self.storyboard?.instantiateViewController(withIdentifier: "SignInFirstViewController") as! InitialSignInViewController
self.present(SignInFirstViewController, animated: false, completion: nil)
}
}
In my app I have a swift page that checks if the user is signed in when the app loads. If they are signed in they continue to the app, and you don't need to check until the app quits, and opens again. If they aren't signed in they go to another view controller to get signed in.
You don't need to manage the state or run the listener, there is more simpler way to do that, i.e.,
if Auth.auth().currentUser != nil {
// User is signed in.
// ...
} else {
// No user is signed in.
// ...
}
Update
You can create one class which just provide the user sign in status like below:
struct UserSignInStatus {
var isLoggedIn: Bool {
return (Auth.auth().currentUser != nil)
}
}
Usage: UserSignInStatus.isLoggedIn
You can put this code any where like in your AppDelegate class where you check the signin status and depending on that managing your rootViewController. Let me know if you need any more help.

How to check if email already exist before creating an account (Swift)

I know different variations of this question have been asked. However I seem to keep running into the same issue every time.
I want to check if an email already exist before the user pushes onto the next view. I will enter an email that exist in the database and the performSegue func is always called and pushes the user as if that email does not exist.
The only way I can check officially is when the user reaches the final sign up VC and the Auth.auth().createUser(withEmail: email as! String, password: password as! String ) { (user, error) in code will check for all errors.
However for good user experience I would hate for the user to have to click back three times to change the email address. Here is the code I have for the enter email view controller.
// Check if email is already taken
Auth.auth().fetchSignInMethods(forEmail: emailTextField.text!, completion: { (forEmail, error) in
// stop activity indicator
self.nextButton.setTitle("Continue", for: .normal)
self.activityIndicator.stopAnimating()
if let error = error {
print("Email Error: \(error.localizedDescription)")
print(error._code)
self.handleError(error)
return
} else {
print("Email is good")
self.performSegue(withIdentifier: "goToCreateUsernameVC", sender: self)
}
})
First off am I even entering the create property in the forEmail section? I added emailTextField.text because its the only way I know how even get the email the user typed. Does anyone know a better way I can do this?
How I create user accounts
This is an example of what I use. When a user provides credentials, FirebaseAuth checks if these credentials can be used to make a user account. The function returns two values, a boolean indicating whether the creation was successful, and an optional error, which is returned when the creation is unsuccessful. If the boolean returns true, we simply push to the next view controller. Otherwise, we present the error.
func createUserAcct(completion: #escaping (Bool, Error?) -> Void) {
//Try to create an account with the given credentials
Auth.auth().createUser(withEmail: emailTextField.text!, password: passwordConfirmTextField.text!) { (user, error) in
if error == nil {
//If the account is created without an error, then we will make a ProfileChangeRequest, i.e. update the user's photo and display name.
if let firebaseUser = Auth.auth().currentUser {
let changeRequest = firebaseUser.createProfileChangeRequest()
//If you have a URL for FirebaseStorage where the user has uploaded a profile picture, you'll pass the url here
changeRequest.photoURL = URL(string: "nil")
changeRequest.displayName = self.nameTextField.text!
changeRequest.commitChanges { error in
if let error = error {
// An error happened.
completion(false, error)
} else {
//If the change is committed successfully, then I create an object from the credentials. I store this object both on the FirebaseDatabase (so it is accessible by other users) and in my user defaults (so that the user doesn't have to remotely grab their own info
//Create the object
let userData = ["email" : self.emailTextField.text!,"name": self.nameTextField.text!] as [String : Any]
//Store the object in FirebaseDatabase
Database.database().reference().child("Users").child(firebaseUser.uid).updateChildvalues(userData)
//Store the object as data in my user defaults
let data = NSKeyedArchiver.archivedData(withRootObject: userData)
UserDefaults.standard.set(data, forKey: "UserData")
UserDefaults.standard.set([Data](), forKey: "UserPhotos")
completion(true, nil)
}
}
}
} else {
// An error happened.
completion(false, error)
}
}
}
Here is an example of how I would use it. We can use the success boolean returned to determine if we should push to the next view controller, or present an error alert to the user.
createUserAcct { success, error in
//Handle the success
if success {
//Instantiate nextViewController
let storyboard = UIStoryboard(name: "Main", bundle: .main)
let nextVC = storyboard.instantiateViewController(withIdentifier: "NextVC") as! NextViewController
//Push typeSelectVC
self.navigationController!.pushViewController(viewController: nextVC, animated: true, completion: {
//We are no longer doing asynchronous work, so we hide our activity indicator
self.activityIndicator.isHidden = true
self.activityIndicator.stopAnimating()
})
} else {
//We now handle the error
//We are no longer doing asynchronous work, so we hide our activity indicator
self.activityIndicator.isHidden = true
self.activityIndicator.stopAnimating()
//Create a UIAlertController with the error received as the message (ex. "A user with this email already exists.")
let alertController = UIAlertController(title: "Error", message: error!.localizedDescription, style: .alert)
let ok = UIAlertAction(title: "OK", style: .cancel, action: nil)
//Present the UIAlertController
alertController.addAction(ok)
self.present(alertController, animated: true, completion: nil)
}
}
Let me know if this all makes sense, I know there is a lot to it. I'm just considering things you'll maybe find you need done anyways that you may not be aware of (like making change requests, or storing a data object on FirebaseDatabase).
Now for checking if the email is already taken:
Remember when I said that I post a user object to FirebaseDatabase upon account creation? Well we can query for the given email to see if it already exists. If it doesn't we continue with the flow as normal, without having actually created the account. Otherwise, we simply tell the user to pick another email address.
Pushing a user object to your database (taken from the above code):
if let firebaseUser = Auth.auth().currentUser {
//Create the object
let userData = ["email" : self.emailTextField.text!,"name": self.nameTextField.text!] as [String : Any]
//Store the object in FirebaseDatabase
Database.database().reference().child("Users").child(firebaseUser.uid).updateChildvalues(userData)
}
And now querying to see if somebody already has that email:
func checkIfEmailExists(email: String, completion: #escaping (Bool) -> Void ) {
Database.database().reference().child("Users").queryOrdered(byChild: "email").queryEqual(toValue: email).observeSingleEvent(of: .value, with: {(snapshot: DataSnapshot) in
if let result = snapshot.value as? [String:[String:Any]] {
completion(true)
} else {
completion(false)
}
}
}
Then we can call this like so:
checkIfEmailExists(email: emailTextField.text!, completion: {(exists) in
if exists {
//Present error that the email is already used
} else {
//Segue to next view controller
}
})

Firebase not storing logged in user

Was working with Firebase for almost half year now and never had this issue. Using code just like before to create new user, he shows up in console, when i close app i can sign in but using same code as always it keeps prompting me for password. In first VC i have:
if let user = Auth.auth().currentUser {
performSegue(withIdentifier: "authorized", sender: self)
}
Which will check if there is auth token stored and perform segue to loading screen. However this never happens. The code is ran but doesn't do anything, no errors in compiler and no errors in console.
EDIT 1: also tried to create new project where i rewrote all the code getting same result, but my other project works without any issues, also i'm able to do anything else besides this using firebase such as access Firestore just not storing the user, went over all the documentation but couldn't find any solution.
EDIT 2: sign in code
if(email.text != "" && password.text != ""){
Auth.auth().signIn(withEmail: email.text!, password: password.text!) { user, error in
if(error == nil && user != nil) {
userID = (Auth.auth().currentUser?.uid)!
print("Signed in as: \(userID)")
self.performSegue(withIdentifier: "load", sender: self)
} else {
print("Error found: \(error?.localizedDescription)")
}
}
}
Solution:
DispatchQueue.main.async(){
if let user = Auth.auth().currentUser {
userID = (Auth.auth().currentUser?.uid)!
self.performSegue(withIdentifier: "authorized", sender: self)
}
}
Loaded with async so firebase will configure before checking for auth
The issue is likely that you are calling your code before firebase has properly initialized. I recommend using the entry view controller's viewDidAppear for things such as these. If you were using viewDidLoad, it is likely that firebase did not load yet.

How do you connect to AWS Cognito in Swift for user signing up and logging in?

I'm going to start this question by apologizing for such an open ended question, but I am really struggling and all the documentation is outdated.
Amazon's provided sample app hasn't been updated and I get nothing but errors when attempting to run it. I have a login page setup and ready to go with email and password, and I have tried this code:
#IBAction func signInButtonTouched(sender: UIButton) {
if (emailTextField.text != nil) && (passwordTextField.text != nil) {
let user = (UIApplication.sharedApplication().delegate as! AppDelegate).userPool!.getUser(emailTextField.text!)
user.getSession(emailTextField.text!, password: passwordTextField.text!, validationData: nil, scopes: nil).continueWithExecutor(AWSExecutor.mainThreadExecutor(), withBlock: {
(task:AWSTask!) -> AnyObject! in
if task.error == nil {
// user is logged in - show logged in UI
} else {
// error
}
return nil
})
} else {
// email or password not set
}
}
but unfortunately I'm getting App Delegate has no member "userPool" errors. It does though! I am very new at this, and I have searched github and read through all of Amazon's samples, but the documentation is either in Objective-C, or outdated and doesn't work properly.
Any help with this would be vastly appreciated.