I am currently authenticating users using firebase phone Authentication and it works fine, however when I close the app and open it again I am redirected to the Authentication form and I receive an authentication Code each time. I don't want that kind of behavior. Is there is a way to check if the current user or the phone number is already authenticated without saving the user to a database
You can do it like checking for the currentUser in firebase, if the currentUser's phone number is same as the provided phone number in textField, then you can bypass the authentication and take user to home screen. Here is how it can be done.
if Auth.auth().currentUser != nil {
// USER IS SIGNED IN, BUT STILL WE HAVE TO CHECK, IF THE SAME USER IS SIGNING IN OR DIFFERENT.
if let user = Auth.auth().currentUser {
let phone = user.phone
if phone == YOUR TEXTFIELD VALUE OF PHONE {
// YOU HAVE THE LOGGED IN USER NOW, YOU CAN TAKE USER TO HOME SCREEN
} else {
// SIGNOUT THE CURRENT USER AND DO THE AUTHENTICATION FOR NEW USER
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
} catch let signOutError as NSError {
print ("Error signing out: %#", signOutError)
}
// No USER IS SIGNED IN, SO GET CREDENTIAL FROM YOUR SERVER AND SEND IT TO AUTH
Auth.auth().signIn(withCustomToken: customToken ?? "") { (user, error) in
// YOU HAVE THE LOGGED IN USER NOW, YOU CAN TAKE USER TO HOME SCREEN
}
}
}
} else {
// No USER IS SIGNED IN, SO GET CREDENTIAL FROM YOUR SERVER AND SEND IT TO AUTH
Auth.auth().signIn(withCustomToken: customToken ?? "") { (user, error) in
// YOU HAVE THE LOGGED IN USER NOW, YOU CAN TAKE USER TO HOME SCREEN
}
}
Check the procedure, if this is what you wants to achieve.
Related
I was working on the user profile page of my app and I am allowing the user to change their email address. If the user email address is changed successfully, the data in the firebase database of the particular user will be updated. Also, After successfully changing the email address, firebase will send an email to the user's previous email address (the user email address before it was changed to the new one) asking if it was the actual owner of the account who changed the email address and there will be a link to reset their email. If the user chooses to reset the email (for whatever reason), the user's new email will be changed to the previous email. But the problem is that the data in the database will not be updated, how can I detect this change (email reset) and update the database?
authenticateUserAlert.addAction(UIAlertAction(title: "Done", style: .default, handler: { [weak authenticateUserAlert] (_) in
// Print the user email
let emailTextField = authenticateUserAlert?.textFields![0]
print("Email: \(emailTextField!.text!)")
// Print the user password
let passwordTextField = authenticateUserAlert?.textFields![1]
print("Password: \(passwordTextField!.text!)")
// Re-authenticate the user
let user = Auth.auth().currentUser
let credential = EmailAuthProvider.credential(withEmail: emailTextField!.text!, password: passwordTextField!.text!)
user?.reauthenticate(with: credential, completion: { (result, error) in
if error != nil {
// Alert: What ever the error
print(error!.localizedDescription)
Alerts.errorAlert(on: vc, error: error!.localizedDescription, dismissAlert: false)
} else {
print(result!)
let editProfilePage = EditUserProfile()
editProfilePage.updateUserInfo()
}
})
}))
Here is what I tried according to an answer
Auth.auth().currentUser?.reload(completion: { (Error) in
//Completion handler
if let email = Auth.auth().currentUser?.email {
UserDataRetrieval.userEmail = email
self.emailLabel.text = email
print(Auth.auth().currentUser?.email)
}
})
It depends on the type of authentication you are using but honestly you should just use the email that is part of the authenticated account and then you don't need to worry about updating it in the database.
You can always just get the users email by using Auth.auth().currentUser.email
Update
Found a workaround to the issue of the credential data, try using
Auth.auth().currentUser?.reload(completion: { (Error) in
if (Error != nil) {
//Do something with error
} else {
//Do something with success or do nothing
}
})
Just call update credentials at the start of the app if you want to always have to most up to date credentials
You can always build your own custom handler for the email change revocation landing page. In that landing page you can update your database.
Check the official docs on how to do that.
I have the following code that authenticates a user with Facebook and then with Firebase:
func authenticateWithFacebook() {
FBSDKLoginManager().logIn(withReadPermissions: ["public_profile"], from: self) { (user, error) in // Signs up user with Facebook
if let error = error {
print(error.localizedDescription)
} else if (user!.isCancelled) { // User cancels sign up
print("User Cancelled")
} else {
self.authenticateFacebookUserWithFirebase(credential: FacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString))
}
}
}
func authenticateFacebookUserWithFirebase(credential: AuthCredential) {
Auth.auth().signInAndRetrieveData(with: credential) { (user, error) in
if let error = error {
print(error.localizedDescription)
} else {
print("Success")
}
}
}
This code works as expected. Once the user is authenticated with Firebase, what do I do with the Facebook user that has been "created" in the app? Do I need to keep track of the currentAccessToken and alert Firebase auth when the token expires? If not, do I just leave the code as is or should I log the Facebook user out of my app using the FBSDK? I just don't want a Facebook token floating around in my app.
The user is not logged in to Firebase with Facebook as such. Your app does not get the user's facebook email and password credentials in order to log them into your app's Firebase. Instead it gets the access token for that user and then that token is used to authenticate the user with Firebase. Therefore you cannot log out your user from Facebook but what you can do is invalidate the access token.
When a user logs into the app using Facebook, I am able to capture and display their full name; however, neither email nor phone number is coming across. I have tried both the "One account per email address" as well as "Multiple accounts per email". I have tested it with an account whose email address is definitely not already registered in Firebase. What am I missing such that email/phone are not being captured? This all does work with Google accounts.
let name = Auth.auth().currentUser?.displayName // works!
let email = Auth.auth().currentUser?.email // nil- why?
let phone = Auth.auth().currentUser?.phoneNumber // nil -why?
The login process, which is standard Firebase w/ Facebook, looks like this:
func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!)
{
if let error = error
{
print(error.localizedDescription)
}
else
{
if FBSDKAccessToken.current() != nil
{
let credential = FacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString)
Auth.auth().signIn(with: credential) { (user, error) in
if let error = error
{
print (error.localizedDescription)
}
}
}
}
}
To update based on comments below. The following also produces a nil email and phone when inspecting the contents of userInfo. I understand the phone might just be that way, but it seems the email was supposed to work.
let userInfo = Auth.auth().currentUser?.providerData
The top level phoneNumber currentUser.phoneNumber is only for Firebase verified phone numbers. If you have that Facebook phone number, you can use the currentUser.updatePhoneNumber API to verify that number (you will need to go through the whole flow). Otherwise, you can wait for the upcoming Admin node.js API to set phone numbers with Admin privileges on existing users: https://github.com/firebase/firebase-admin-node/commit/68563c4b2c8128fbc45fc65bad3f6730d320b539
As for the email, in the case of "multiple accounts per email" you need to set it yourself via currentUser.updateEmail. You can get the Facebook email from currentUser.providerData which contains the Facebook provider data.
I have an iOS app that uses Firebase as a backend for authentication.
Once a user logs in and then closes the app, I don't want the user to have to re-enter their email and password. My approach is to save the access token after a successful login to the Keychain, and then when the user comes back to the app, use the token from the keychain to signin.
I've tried using the method FIRAuth.auth()?.signInWithCustomToken(customToken) { (user, error) in but that's not quite right as that's for when using custom tokens, which is not what I'm doing.
Is there a way for me to do this?
// login with email / password
FIRAuth.auth()?.signInWithEmail(email, password: password, completion: { (firebaseUser, error) in
if error == nil {
FIRAuth.auth()!.currentUser!.getTokenWithCompletion({ (token, error) in
if error == nil {
// save token to keychain
} else {
print(error)
}
})
} else {
print(error)
}
})
// user comes back to app
do {
// get saved token from keychain
if let myToken = try keychain.get("token") {
FIRAuth.auth()?.signInWithCustomToken(myToken, completion: { (user: FIRUser?, error: NSError?) in
if error == nil {
// show post login screen
} else {
}
})
}
} catch {
// error getting token from keychain
}
}
I was approaching this problem in the wrong way. Saving a token is appropriate when using a 3rd party authentication provider, like Facebook, Google, etc and getting an OAuth token in return from one of those services.
In my case when logging in using email and password, a token is not required and instead the password can be securely saved in the Keychain and used later for login.
I have an facebook login system which works with firebase but I want to check if user exist on my firebase (i don't want to add it, just want to make sure if he exist because I want to redirect user to another page to complete its profile, once its done I'll want to send it to firebase).
I just need to check if user exist on my db. Here is the code that I try but it returns nil error and it automatically add user to firebase.
let credential = FIRFacebookAuthProvider.credentialWithAccessToken(FBSDKAccessToken.currentAccessToken().tokenString)
FIRAuth.auth()?.signInWithCredential(credential) { (user, error) in
// ...
}
You can use reauthenticateWithCredential method to check user is exist or not.
Check this Doc. , section -> Re-authenticate a user
let user = FIRAuth.auth()?.currentUser
var credential: FIRAuthCredential
// Prompt the user to re-provide their sign-in credentials
user?.reauthenticateWithCredential(credential) { error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
}
}
If user re-authenticated successfully that means user is existed...