I wish to re-authenticate a user prior to allowing them to change their login information. However, due to the recent Firebase update, I found the documentation rather unhelpful. Using this link I produced the following authenticateUser() function.
func authenticateUser()
{
let user = FIRAuth.auth()?.currentUser
var credential: FIRAuthCredential
//prompt user to re-enter info
user?.reauthenticateWithCredential(credential, completion: { (error) in
if error != nil
{
self.displayAlertMessage("Error reauthenticating user")
}
else
{
//user reauthenticated successfully
}
})
}
However, I am unsure what to do with the credential variable of type FIRAuthCredential, in order to re-authenticate the user. The documentation for this class can be found here.
Getting the FIRAuthCredential object depends on what provider you want to use to reauthenticate.
Email:
let credential = EmailAuthProvider.credential(withEmail: email, password: password)
Facebook:
let credential = FacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.currentAccessToken().tokenString)
Twitter:
let credential = TwitterAuthProvider.credential(withToken: session.authToken, secret: session.authTokenSecret)
Google:
let authentication = user.authentication
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken, accessToken: authentication.accessToken)
In Swift 4 and latest firebase 4 the names have changed a bit, but the principle still remains. For your convenience:
let eMail = EmailAuthProvider.credential(withEmail: "some#email.com", password: "somepassword")
let fb = FacebookAuthProvider.credential(withAccessToken: "xxx")
let g = GoogleAuthProvider.credential(withIDToken: "xxx", accessToken: "xxx")
...
Auth.auth().currentUser?.reauthenticate(with: eMail, completion: {
[weak self]
(error) in
...
})
Firebase's documentation is currently outdated. Here is the correct way to handle reauthenticate.
let user = Auth.auth().currentUser
user?.reauthenticate(with: credential, completion: { (result, error) in
if let err = error {
//..read error message
} else {
//.. go on
}
})
Related
Application uses GIDSignIn and Firebase for google authentication in my iOS app.
I am trying to add additional scopes to the authentication flow, however, I do not know the proper way to add the needed scopes.
Google Sign in Documentation
func signInWithGoogle() {
guard let clientID = FirebaseApp.app()?.options.clientID else { return }
// Create Google Sign In configuration object.
let config = GIDConfiguration(clientID: clientID)
// Start the sign in flow!
GIDSignIn.sharedInstance.signIn(with: config, presenting: self) { [unowned self] user, error in
if let error = error {
// ...
return
}
guard
let authentication = user?.authentication,
let idToken = authentication.idToken
else {
return
}
let credential = GoogleAuthProvider.credential(withIDToken: idToken,
accessToken: authentication.accessToken)
// ...
Auth.auth().signIn(with: credential, completion: { (user, error) in
if let err = error {
print("Failed to create a Firebase User with Google account: ", err)
return
}
// Successfully logged in
guard let uid = user?.user.uid else { return }
print("Successfully logged into Firebase with Google", uid)
let mainTabBarController = TabBarViewController()
(UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.changeRootViewController(mainTabBarController)
})
}
}
I need to add the code below to add scopes to the authentication process. I just dont know how to properly ask for the scopes without interrupting the firebase login process.
let additionalScopes = ["https://www.googleapis.com/auth/youtube.readonly", "https://www.googleapis.com/auth/yt-analytics.readonly"]
GIDSignIn.sharedInstance.addScopes(additionalScopes, presenting: self) { user, error in
guard error == nil else { return }
guard let user = user else { return }
// Check if the user granted access to the scopes you requested.
}
A similar post with a similar problem can be found here
I want to login to firebase by phone number, but when I logged in by phone number, the user have already created. My question is: how can I put some details to the new user like name, email and profile pic who created by Auth.auth().signIn(with: credential)?
Button(action: {
let verificationID = UserDefaults.standard.string(forKey: "authVerificationID")
let credential = PhoneAuthProvider.provider().credential(
withVerificationID: verificationID ?? "",
verificationCode: confirm)
Auth.auth().signIn(with: credential) { success, error in
if error == nil {
UserDefaults.standard.set(true, forKey: "isLoggedIn")
Continue = true
}
else{
print(error!)
showConfirmError.toggle()
}
}
}){
You can just link accounts.
Here is official documentation how to do that:
https://firebase.google.com/docs/auth/ios/account-linking
for first you must do request like this:
let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
and then change user
changeRequest?.displayName = displayName
changeRequest?.commitChanges { (error) in
// ...
}
https://firebase.google.com/docs/auth/ios/manage-users#update_a_users_profile
Using email when logging in a new user or creating a new user there are 2 different method signatures. When creating a new user if the email already exists an error will be returned or logging a user in if the email doesn't exist an error will be returned:
// create account
Auth.auth().createUser(withEmail: emailTextField.text!, password: passwordTextField.text!, completion: { (authDataResult, error)
if let error = error {
// if this email address already exists an error will be returned
return
}
})
// login
Auth.auth().signIn(withEmail: emailTextField.text!, password: self.passwordTextField.text!, completion: { (authDataResult, error) in
if let error = error {
// if this email address isn't inside the system then an error will be returned
return
}
})
But when using a user's phone number to log them is or create a new account I have to use the same method signature for both situations.
func loginExistingUserOrCreateNewOne(phoneNumber: String, verificationCode: String) {
PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumber, uiDelegate: nil) { (verificationID, error) in
if let error = error { return }
guard let verificationId = verificationID else { return }
let credential = PhoneAuthProvider.provider().credential(withVerificationID: verificationId, verificationCode: verificationCode)
Auth.auth().signIn(with: credential, completion: { (authDataResult, error) in
guard let authUser = authDataResult else { return }
let checkUsersRef = Database.database().reference().child("users").child(authUser.user.uid)
checkExistingUsersRef.observeSingleEvent(of: .value, with: { (snapshot) in
if !snapshot.exists() {
// this is a new user, now add them to the users ref
let newUserDict = ["signupDate": Date().timeIntervalSince1970]
checkUsersRef.updateChildValues(newUserDict, withCompletionBlock: { (error, ref) in
if let error = error {
// because there is an error this ref was never updated so now I have to sign this user out and they have to start over agin
do {
try Auth.auth().signOut()
} catch let err as NSError {
// alert user there is a major problem
}
return
}
// if no error let them go to HomeVC
})
return
}
// this is a previous user fetch dict data and let them proceed to HomeVC
guard let previousUserDict = snapshot.value as? [String: Any] else { return }
// get newUserDict values and let them go to HomeVC
})
})
}
}
If a user already has an account I need to fetch some data from the users ref and then I let them proceed to HomeVC. If the user has never signed up before then I have to add them to the users ref and then let them proceed. It's a 2 step process.
The problem is these extra steps seems unnecessary. For example using email sign or login an error is returned so there is no need to create and check inside another ref to see if that email already exists.
Outside of using the process in my above code is there any other way that I can determine if a phone number exists before creating a new account or if it doesn't exist when logging in?
You will need to use the admin sdk to lookup a user by phone number:
admin.auth().getUserByPhoneNumber(phoneNumber)
.then(function(userRecord) {
// User exists.
})
.catch(function(error) {
if (error.code === 'auth/user-not-found') {
// User not found.
}
});
You can use a Cloud Function to host an HTTP endpoint. Looking up a user by phone number is only possible via authenticated APIs running server side (using the Firebase Admin SDKs).
I am logging my users in with the Facebook and Google sign-in protocols. I am able to store these users by doing:
let firebaseID = Auth.auth().currentUser?.uid
let userID = user.userID
let fullName = user.profile.name
let email = user.profile.email
let provider = "Google"
guard let authentication = user.authentication else {return}
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken, accessToken: authentication.accessToken)
Auth.auth().signInAndRetrieveData(with: credential) { (authResult, error) in
if let error = error {
print("Failed to create Firebase User with Google: ", error)
return
}
print("Successful creation of Firebase User with Google")
let databaseRef = Database.database().reference()
let key = databaseRef.child("node").childByAutoId().key
let userInfo = ["UID": userID,
"Full Name": fullName,
"Email": email,
"Provider": provider]
let childUpdates = ["/Users/\(key)/": userInfo]
databaseRef.updateChildValues(childUpdates)
Every time I sign in, even with the same credentials a new user is created with the same sign in user. How can I prevent a new instance from being created if a particular user has logged in before.
you should use:
let childUpdates = ["/Users/\(userID)/": userInfo]
and so that the optional does not appear you should put it like this:
let key = databaseRef.child("node").childByAutoId().key ?? ""
or unwrap it if you want to catch/prevent nil
let key = databaseRef.child("node").childByAutoId().key!
However, since the user id does not change when the same user enters new information, and you are using the autokey for every time the user enters, a new child is being created every time.
You should check if user is already registered or no after signInAndRetrieveData:
Auth.auth().signInAndRetrieveData(with: credential) { (authResult, error) in
if let error = error {
return
}
if(isNewUser()) {
// create new user in Firebase
}
else {
// continue and skip creating user ..
}
}
I have implemented email and Facebook login in my app using firebase. My code is as below:
For fb login :
FireBaseHelper.fireBaseHelper.BASE_REF.authWithOAuthProvider("facebook", token: "123") { (error, data) -> Void in
APP_DELEGATE.hideActivityIndicator()
if error != nil {
print(error)
self.loginErrorAlert("Oops!", message: "Check your username and password.")
} else {
// Be sure the correct uid is stored.
NSUserDefaults.standardUserDefaults().setValue(data.uid, forKey: "uid")
// Enter the app!
self.performSegueWithIdentifier("CurrentlyLoggedIn", sender: nil)
}
}
For email signup :
let username = usernameField.text
let email = emailField.text
let password = passwordField.text
if username != "" && email != "" && password != "" {
APP_DELEGATE.showActivityIndicator()
FireBaseHelper.fireBaseHelper.BASE_REF.createUser(email, password: password, withValueCompletionBlock: { error, result in
if error != nil {
APP_DELEGATE.hideActivityIndicator()
// There was a problem.
self.signupErrorAlert("Oops!", message: "Having some trouble creating your account. Try again.")
} else {
// Create and Login the New User with authUser
FireBaseHelper.fireBaseHelper.BASE_REF.authUser(email, password: password, withCompletionBlock: {
err, authData in
let user = ["provider": authData.provider!, "email": email!, "username": username!]
FireBaseHelper.fireBaseHelper.USER_REF.childByAppendingPath(authData.uid).setValue(user, withCompletionBlock: { (error, firebase) -> Void in
if error != nil {
APP_DELEGATE.hideActivityIndicator()
self.signupErrorAlert("Oops!", message: "Having some trouble creating your account. Try again.")
}else
{
APP_DELEGATE.hideActivityIndicator()
// Store the uid for future access - handy!
NSUserDefaults.standardUserDefaults().setValue(result ["uid"], forKey: "uid")
// Enter the app.
self.performSegueWithIdentifier("NewUserLoggedIn", sender: nil)
}
})
})
}
})
}else
{
signupErrorAlert("Oops!", message: "Don't forget to enter your email, password, and a username.")
}
And helper class code:
class FireBaseHelper: NSObject {
static let fireBaseHelper = FireBaseHelper()
private var _BASE_REF = Firebase(url: "https://poc-demoApp.firebaseio.com")
private var _USER_REF = Firebase(url: "https://poc-demoApp.firebaseio.com/users")
var BASE_REF: Firebase {
return _BASE_REF
}
var USER_REF: Firebase {
return _USER_REF
}
var CURRENT_USER_REF: Firebase {
let userID = NSUserDefaults.standardUserDefaults().valueForKey("uid") as! String
let currentUser = Firebase(url: "\(BASE_REF)").childByAppendingPath("users").childByAppendingPath(userID)
return currentUser!
}
}
Problem is when i sign up with email user I can see that user data on dashboard of firebase and when i am sign up with fb , that user's data are not displaying on dashboard, let me know what is issue. Anything i miss? Its showing fb signup successfully
Firebase only stores a list of email+password users. It doesn't store any data for users of your app that are signed with social providers (such as Facebook). So there is nothing to show on the Firebase dashboard for those users.
If you want to store information about Facebook users in your database, see the section on storing user data.
I also had the same issue and #RenanSilveira is right. There are just some updates in the FBSDKLoginKit package needing "AccessToken.current!.tokenString" instead. This is what it looks like:
let credential = FacebookAuthProvider.credential(withAccessToken: AccessToken.current!.tokenString)
Auth.auth().signIn(with: credential) { (user, error) in
if let error = error {
print("Facebook authentication with Firebase error: ", error)
return
}
print("User signed in!")
}
And this is the console after Facebook login
I was facing the same problem and I found out that after a user successfully signs in with Facebook, you need to get an access token for the signed-in use, exchange it for a Firebase credential and finally authenticate with Firebase using the Firebase credential.
Here is an example (don't forget to import FBSDKLoginKit and FirebaseAuth libraries):
let credential = FacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString)
Auth.auth().signIn(with: credential) { (user, error) in
if let error = error {
print("Facebook authentication with Firebase error: ", error)
return
}
print("User signed in!") // After this line the Facebook login should appear on your Firebase console
}
Here is a screen shot of the console after Facebook login: