Firebase automatic login on iOS - ios

Is there a better way to make a user automatically login to the app as long as they have logged in in the past; other than saving their login details directly to the storage which may be insecure?
Thanks
PS: I'm using Firebase and Swift for iOS
EDIT: Here's the code
import UIKit
import Firebase
class loginVC: UIViewController {
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var errorLabel: UILabel!
override func viewDidAppear(animated: Bool) {
automaticLogin()
}
#IBAction func loginTapped(sender: AnyObject) {
errorLabel.text = ""
FIRAuth.auth()?.createUserWithEmail(emailTextField.text!, password: passwordTextField.text!, completion: {
user, error in
if error != nil {
FIRAuth.auth()?.signInWithEmail(self.emailTextField.text!, password: self.passwordTextField.text!, completion: { (user, error) in
if error == nil {
self.login()
} else if error != nil {
self.errorLabel.text = ("Invalid email address or password")
print(error)
}
})
} else {
print("user created")
self.login()
print(user?.displayName)
}
})
}
#IBAction func forgotPasswordTapped(sender: AnyObject) {
let email = emailTextField.text
errorLabel.text = ""
FIRAuth.auth()?.sendPasswordResetWithEmail(email!) { error in
if error != nil {
self.errorLabel.text = ("Invalid email address")
print(error)
} else {
self.errorLabel.text = ("Password reset email successfully sent")
print("password reset email sent")
}
}
}
func login() {
FIRAuth.auth()?.signInWithEmail(emailTextField.text!, password: passwordTextField.text!, completion: {
user, error in
if error != nil {
self.errorLabel.text = ("Invalid email address or password")
print(error)
} else {
print("login successful")
self.checkIfUserIsNew()
}
})
}
func checkIfUserIsNew() {
if FIRAuth.auth()?.currentUser?.displayName != nil || FIRAuth.auth()?.currentUser?.displayName == "" {
self.performSegueWithIdentifier("showChatVC", sender: self)
} else {
self.performSegueWithIdentifier("showOptionsVC", sender: self)
}
}
func automaticLogin() {
FIRAuth.auth()?.addAuthStateDidChangeListener { auth, user in
if user == user {
print("still signed in")
} else {
print("not signed in")
}
}
}
I also used this to logout the user:
#IBAction func logOutTapped(sender: AnyObject) {
try! FIRAuth.auth()?.signOut()
performSegueWithIdentifier("showLoginVC", sender: self)
print(FIRAuth.auth()?.currentUser?.displayName)
}

You might want to use NSUserDefaults. To do this in your login() function you might want to save your user's data once they signed in or create an account. Inside your login function you can start by creating new variable to grab your user's email / password.
let userDefaults = UserDefaults.standard
userDefaults.setValue(self.emailField.text!, forKey: "email")
userDefaults.setValue(self.passField.text!, forKey: "password")
Next, you also need to implement a new function called viewWillAppear, which will be called before the view controller. And you might also put some arguments inside your login function.
override func viewWillAppear(_ animated: Bool) {
let userDefaults = UserDefaults.standard
if userDefaults.string(forKey: "email") != nil {
let email = userDefaults.string(forKey: "email")
let password = userDefaults.string(forKey: "password")
login(email: email!, password: password!)
}
}
the if statement will check wether your user already registered their email account or not yet. Lastly you need to update your login function to have two parameters, to pass in the email and password of the user.

Related

Login authentication problem with firebase in Xcode

I try to do firebase authenticate. I've installed all necessary pods and made all needed methods. When I click "Login" even when I entered bad email and password, I get error message (e.g. bad password) and immediately my view is changed to my target view (next ViewController). I don't know why it is happening, because I made if statement. I want to have my login page unless I enter correct email and password.
#IBAction func loginTapped(_ sender: UIButton) {
if let email = emailTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines), let password =
passwordTextField.text?.trimmingCharacters(in:
.whitespacesAndNewlines) {
Auth.auth().signIn(withEmail: email, password: password) { (authResult, error) in
if let e = error {
// Can't sign in
self.errorLabel.text = error!.localizedDescription
self.errorLabel.alpha = 1
} else {
self.performSegue(withIdentifier: "loginToHome", sender: self)
}
}
}
}
You may try to check if an error exists first. If yes, the error message will be shown. If there's no error, the login success segue will be performed.
#IBAction func loginTapped(_ sender: UIButton) {
if let email = emailTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines), let password = passwordTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) {
Auth.auth().signIn(withEmail: email, password: password) { (authResult, error) in
if error != nil { // there's an error - show error label
self.errorLabel.text = error!.localizedDescription
self.errorLabel.alpha = 1
} else { //no error - perform segue
// no error - perform segue
self.performSegue(withIdentifier: "loginToHome", sender: self) } } } }
Edit: You may also add return after checking that an error exists - the codes that follow after it will not be performed:
#IBAction func loginTapped(_ sender: UIButton) {
if let email = emailTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines), let password = passwordTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) {
Auth.auth().signIn(withEmail: email, password: password) { (authResult, error) in
if error != nil { // there's an error - show error label
self.errorLabel.text = error!.localizedDescription
self.errorLabel.alpha = 1
return // activities that follow will not be performed
} else { //no error - perform segue
self.performSegue(withIdentifier: "loginToHome", sender: self) } } } }

Firebase email verification always return false user not verified. iOS

I am implementing firebase email verification, but when user received an email it's redirecting to app which is fine. When I am trying to sign in with verified email Auth.auth().currentUser?.isEmailVerified always return false. Below is my code:
class ViewController: UIViewController {
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var signInBtn: UIButton!
var link: String!
override func viewDidLoad() {
super.viewDidLoad()
if let link = UserDefaults.standard.value(forKey: "Link") as? String{
self.link = link
signInBtn.isEnabled = true
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
#IBAction func didTapSignInWithEmailLink(_ sender: AnyObject) {
if let email = self.emailTextField.text {
Auth.auth().signIn(withEmail: email, link: self.link) { (user, error) in
if let error = error {
print("\(error.localizedDescription)")
return
}
}
} else {
print("Email can't be empty")
}
print(Auth.auth().currentUser?.email)
if Auth.auth().currentUser?.isEmailVerified == true {
print("User verified")
} else {
print("User not verified")
}
}
#IBAction func didTapSendSignInLink(_ sender: AnyObject) {
if let email = emailTextField.text {
let actionCodeSettings = ActionCodeSettings()
actionCodeSettings.url = URL(string: "https://example.firebaseapp.com")
// The sign-in operation has to always be completed in the app.
actionCodeSettings.handleCodeInApp = true
actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)
actionCodeSettings.setAndroidPackageName("com.example.android",
installIfNotAvailable: false, minimumVersion: "12")
Auth.auth().sendSignInLink(toEmail: email, actionCodeSettings: actionCodeSettings) { error in
if let error = error {
print("\(error.localizedDescription)")
return
}
print("Check your email for link")
}
} else {
print("Email cant be empty")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I've found the solution we need to reload the user, which will update the current user status. Call Auth.auth.currentUser.reload in didTapSignInWithEmailLink button func:
Auth.auth().currentUser?.reload(completion: { (error) in
if error == nil{
if let email = self.emailTextField.text {
Auth.auth().signIn(withEmail: email, link: self.link) { (user, error) in
if let error = error {
print("\(error.localizedDescription)")
return
}
}
} else {
print("Email can't be empty")
}
print(Auth.auth().currentUser?.email)
if Auth.auth().currentUser?.isEmailVerified == true {
print("User verified")
} else {
print("User not verified")
}
} else {
print(error?.localizedDescription)
}
})

Firebase Email Verification Swift

I tried to use the Firebase Authentication, but in the login lets me in even when I haven't used the email to confirm the verification.
I have two Viewcontroller , one for Login and the other for signUP.
I can log in and I get the email for verification, but I can also log in in without verification.
public func sendVerificationMail() {
if self.authUser != nil && !self.authUser!.isEmailVerified {
self.authUser!.sendEmailVerification(completion: { (error) in
// Notify the user that the mail has sent or couldn't because of an error.
})
} else {
// Either the user is not available, or the user is already verified.
}
}
#IBAction func signupButtonTapped(_ sender: Any) {
print("Sign up button tapped")
Auth.auth().createUser(withEmail: self.userEmailTextField.text!, password: self.userPasswordTextField.text!) { (user, error) in
if user != nil {
print("User has Signed Up")
self.sendVerificationMail()
}
if error != nil {
print("User cant Sign Up")
}
}
}
#IBAction func signinButtonTapped(_ sender: Any) {
Auth.auth().signIn(withEmail: self.userEmailTextField.text!, password: self.userPasswordTextField.text!) { (user, error) in
if user != nil {
print("User has Signed In")
}
if error != nil {
print("Cant Sign in user")
} else {
self.performSegue(withIdentifier: "toHome", sender: nil)
}
}
}
Firebase Auth doesn't prevent people from signing in when their email isn't verified. If you want to prevent users from advancing when they aren't verified, you will need to code this on the client using the isEmailVerified boolean.
Auth.auth().signIn(withEmail: self.userEmailTextField.text!, password: self.userPasswordTextField.text!) { (authResult, error) in
if let authResult = authResult {
let user = authResult.user
print("User has Signed In")
if user.isEmailVerified {
self.performSegue(withIdentifier: "toHome", sender: nil)
} else {
// do whatever you want to do when user isn't verified
}
}
if let error = error {
print("Cant Sign in user")
}
}
A more comprehensive solution than that provided by #jen-person (and based on together with another SO answer) is this:
final class MySignInView: UIView { // or possibly MySignInViewController: UIViewController
// ... your properties etc...
#IBAction func signInButtonPressed(sender _: AnyObject) {
trySigningIn()
}
}
// MARK: Private
private extension MySignInView {
func trySigningIn() {
guard
let email = userEmailTextField.text,
let password = userPasswordTextField.text
else {
print("Cannot sign in, email or password is 'nil'")
return
}
do {
try signIn(email: email, password: password) { [unowned self] authResult in
self.userDidSignIn(authResult.user)
}
} catch {
// Display error
}
}
func userDidSignIn(_ user: FIRUser) {
// Creds to Jen: https://stackoverflow.com/a/51389154/1311272
guard user.isEmailVerified else {
// TODO display message about non verified email user?
return
}
performSegue(withIdentifier: "toHome", sender: nil)
}
}
// MARK: Error
private extension MySignInView {
enum Error: Strng, Equatable {
case invalidEmail
case userDisabled
case wrongPassword
case userNotFound
case networkError
case unknownError
}
}
// MARK: Firebae specific
private extension MySignInView {
func signIn(
email: String,
password: String,
onSuccessful: (AuthDataResult) -> Void
) throws {
Auth.auth().signIn(
withEmail: email,
password: password
) { (authResult, anyError) in
if let anyError = anyError {
if let error = Error(anyError: anyError) {
throw error
} else {
fatalError("Unsupported error: \(anyError)")
}
}
onSuccessful(authResult)
}
}
}
private extension MySignInView.Error {
init?(anyError: Swift.Error) {
guard let authErrorCode = FIRAuthErrorCode(rawValue: anyError.code) else {
return nil
}
self.init(fireBaseAuthErrorCode: authErrorCode)
}
// Creds goes to to: https://stackoverflow.com/a/39936083/1311272
init(fireBaseAuthErrorCode: FIRAuthErrorCode) {
switch errCode {
case .ErrorCodeInvalidEmail:
self = .invalidEmail
case .ErrorCodeUserDisabled:
self = .userDisabled
case .ErrorCodeWrongPassword:
self = .wrongPassword
case .ErrorCodeUserNotFound:
self = .userNotFound
case .ErrorCodeNetworkError:
self = .networkError
default:
self = .unknownError
}
}
}
You need to inform the user appropriately about the different errors/incorrect states of course, not only print.
This code should probably me moved to a ViewModel, and if your are not using MVVM, I highly recommend it :).
if let email = emailTextfield.text, let password = passwordTextfield.text {
Auth.auth().signIn(withEmail: email, password: password) { authResult, error in
if let e = error{
print(e.localizedDescription)
}
else {
//Do whatever you want to do after successful login
}
}
}
You can try this also:
if( !Auth.auth().currentUser!.isEmailVerified) {
// do something
}

Firebase DataBase

Problem: For some reason the data is not being sent to my database in firebase. Can anyone figure out why the information is not being sent?
I highlighted the code with //******** to show where I am sending it.
Rules:
{
"rules": {
".read": "true",
".write": "true"
}
}
import UIKit
import Firebase
import FirebaseDatabase
import KeychainSwift
class SignUpController: UIViewController {
//INPUT - from keyboard email & password
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
//Data Base Initialization
//var ref: FIRDatabaseReference!
let dataRef = FIRDatabase.database().reference()
override func viewDidLoad() {
super.viewDidLoad()
}
//PROCESS - Checks is user has already been logged in
override func viewDidAppear(_ animated: Bool) {
let currentUser = FIRAuth.auth()?.currentUser
if currentUser != nil {
performSegue(withIdentifier: "SignIn", sender: nil)
}
}
func CompleteSignIn(id: String){
let keyChain = DataService().keyChain
keyChain.set(id , forKey: "uid")
}
//If SignUp button is pressed user will be directed to sign up page
#IBAction func LoginPressed(_ sender: Any) {
self.performSegue(withIdentifier: "BackToLogin", sender: nil)
}
/*If both password and email contain text lets put them into string variables
called email & password. Creates user and authorizes sign in */
#IBAction func SignIn(_ sender: Any){
if let email = emailField.text, let password = passwordField.text {
FIRAuth.auth()?.signIn(withEmail: email, password: password) { (user, error) in
if error == nil {
self.CompleteSignIn(id: user!.uid) //Completese Database Sign in
//************************************************
//Send information to Database
let userEmail : String = self.emailField.text!
let userPassword : String = self.passwordField.text!
let userID : String = user!.uid
self.dataRef.child("Users").child(userID).setValue(["Email": userEmail, "Password" : userPassword])
//************************************************
print("Sign in Test")
self.performSegue(withIdentifier: "SignIn", sender: nil)
} else {
FIRAuth.auth()?.createUser(withEmail: email, password: password) { (user, error) in
if error != nil {
Alerts().invalidSignUpAlert(sender: self) //Alert for invalid email & Password
print("cant sign in user") //Programmer Debugging
} else {
self.CompleteSignIn(id: user!.uid)
self.performSegue(withIdentifier: "SignIn", sender: nil)
}
}
}
}
}
}
}
Add "Users" in the rules as shown below:
"rules": {
"Users":{
".read": "true",
".write": "true"
}
}
You need as Boolean, not as string "true"
{
"rules": {
".read" : true,
".write":true
}
}

Error while registering a new user on Firebase

When I try to register a new user, I get an error saying value of type 'FIRDatabaseReference' has no member 'createuser'. See image below.
FIRAuth.auth()?.createUserWithEmail(<email: String>, password: <String>, completion: <FIRAuthResultCallback?(FIRUser?, NSError?) -> Void#>)
Here is a screenshot of how the code looks like picture of the code and the imageview:
Try this code it is working absolutely fine for me
//
// ViewController.swift
// FirebaseExample
//
// Created by Belal Khan on 03/10/16.
// Copyright © 2016 Belal Khan. All rights reserved.
//
import UIKit
//importing firebase
import Firebase
class ViewController: UIViewController {
//Textfields for email and password
#IBOutlet weak var textFieldEmail: UITextField!
#IBOutlet weak var textFieldPassword: UITextField!
//label for displaying message
#IBOutlet weak var labelMessage: UILabel!
//button for registration
#IBAction func buttonRegister(sender: UIButton) {
//do the registration operation here
//first take the email and password from the views
let email = textFieldEmail.text
let password = textFieldPassword.text
FIRAuth.auth()?.createUserWithEmail(email!, password: password!, completion: { (user: FIRUser?, error) in
if error == nil {
self.labelMessage.text = "You are successfully registered"
}else{
self.labelMessage.text = "Registration Failed.. Please Try Again"
}
})
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//initialising firebase
FIRApp.configure()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Source: swift firebase tutorial
The code does not match. ref.createUser is different from FIRAuth.auth()?.createUserWithEmail
This is a working piece of code I tried a few days ago in XCode 7.3.1 I am not sure which version of XCode you are using. However, there's no pre-defined function as you tried!
func buttonHandleRegister() {
guard let email = emailTextField.text, password = passwordTextField.text, name = nameTextField.text else {
print("Form is not valid")
return
}
FIRAuth.auth()?.createUserWithEmail(email, password: password, completion: { (user: FIRUser?, error) in
if error != nil {
print("Error")
return
}
guard let uid = user?.uid else {
return
}
//successfully logged in
let ref = FIRDatabase.database().referenceFromURL("https://some-random-name.firebaseio.com/")
let usersReference = ref.child("users").child(uid)
let values = ["name" : name, "email": email, "password": password]
usersReference.updateChildValues(values, withCompletionBlock: { (err, ref)
in
if err != nil {
print(err)
return
}
print("Saved user succesfully")
})
})
}

Resources