How to confirm credentials while the user is logged in? (on Firebase) - ios

I am using Firebase in an iOS app to provide server support to the users.
When someone wants to change his credentials (email/password) or his display name, I request to confirm the current credentials. Is there a way (an API) to check that the provided credentials are the ones matching the currently logged in user, other than perform a log out and trying to log in again, hoping the provided credentials are correct?

Use the below method to Re-authenticate a user using firebase
let user = FIRAuth.auth()?.currentUser
var credential: FIRAuthCredential
// Prompt the user to re-provide their sign-in credentials
user?.reauthenticate(with: credential) { error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
}
}
Email:
let credential = FIREmailPasswordAuthProvider.credentialWithEmail(email,` password: password)
Facebook:
let credential = FIRFacebookAuthProvider.credentialWithAccessToken(FBSDKAccessToken.currentAccessToken().tokenString)
Twitter:
let credential = FIRTwitterAuthProvider.credentialWithToken(session.authToken, secret: session.authTokenSecret)
Google:
let authentication = user.authentication
let credential = FIRGoogleAuthProvider.credentialWithIDToken(authentication.idToken, accessToken: authentication.accessToken)
heres the firebase link describing the user management in ios
ios user management in firebase

Related

Update email after Apple Firebase Auth

A problem I have is when a user login with Apple at first, and then he updates the Email, so it causes the refresh token to expire.
According to the document here, refresh token will expire when email address has been updated.
I tried to use reauthenticate function as below, but the credential must be created first as parameters. In order to get the credential, the user must re-login Apple again in order to get a new nonce, is there any way to avoid the prompt of the apple login window again, and also to get the credential smoothly?
let credential = OAuthProvider.credential(withProviderID: "apple.com",
idToken: idToken,
rawNonce: nonce)
let user = Auth.auth().currentUser
user?.reauthenticate(with: credential) { error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
}
}
I have seen an answer from here that I can get a new credential as below, but the key here is nil.
(error! As NSError) .userInfo [AuthErrorUserInfoUpdatedCredentialKey])

Firebase Google log in get credentials

In this example here we can re-authenticate an user by providing first the credentials which vary on the provider. However for google there is this example which says:
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
accessToken: authentication.accessToken)
How do I get user.authentication? There is no authentication property in Auth.auth().currentUser.
credential = EmailAuthProvider.credential(withEmail: email, password: password)
credential = FacebookAuthProvider.credential(withAccessToken: AccessToken.current!.tokenString)
user.reauthenticate(with: credential) { (result, error) in
//
}
The idea behind reauthentication is that for certain sensitive operations such as deleting an account, updating the primary email address, or changing the password, you want to make sure the user has recently signed in.
So what you want to do is
ask them to sign in
get the credential
perform user.reauthenticate with the fresh credential
The Swift docs have a concise example for this:
let user = Auth.auth().currentUser
var credential: AuthCredential
// Prompt the user to re-provide their sign-in credentials
user?.reauthenticate(with: credential) { error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
}
}
For a more detailed code sample, check out FirebaseUI (FUIAccountSettingsOperation.m), which does what I outlined above.

Firebase Authentication Link Facebook to Google

After many tests I decided to create a new xCode project to better understand Firebase authentication with multiple providers.
I set up in Firebase -> SignIn Methods -> An account per email address
An account per email address
Prevents users from creating multiple
accounts using the same email address with different authentication
providers
At this point I have implemented, carefully following the Firebase guide, the login with Facebook and with Google .. Everything seems to work perfectly but I always find myself with the same error that I can't manage:
When my user creates a Firebase account via Google he is no longer able to log in if he decides to use Facebook.
Facebook returns its error when it completes its authentication flow with Firebase:
Firebase Error With Facebook Provider: An account already exists with the same email address but different sign-in credentials. Sign in using a provider associated with this email address.
Continuing to follow the documentation step by step I stopped here (firebase explains how to handle this error)
I have also implemented error handling but after calling Auth.auth().fetchSignInMethods Firebase says I should authenticate the user with the existing provider, at this point how do I get the credentials for authentication with the existing provider?
I wouldn't want to reopen the existing provider controller to get new credentials
Am I obliged to ask the user to log in with the existing provider and show another access controller again (in this case that of Google)?
How should I handle this situation?
override func viewDidLoad() {
super.viewDidLoad()
facebookSetup()
}
func facebookSetup() {
let loginButton = FBLoginButton(permissions: [ .publicProfile, .email ])
loginButton.center = view.center
loginButton.delegate = self
view.addSubview(loginButton)
}
//MARK: - FACEBOOK Delegate
func loginButton(_ loginButton: FBLoginButton, didCompleteWith result: LoginManagerLoginResult?, error: Error?) {
if let error = error {
print(error.localizedDescription)
return
}
let credential = FacebookAuthProvider.credential(withAccessToken: AccessToken.current!.tokenString)
Auth.auth().signIn(with: credential) { (authResult, error) in
if let error = error {
print("\n FIREBASE: ",error.localizedDescription)
// An account with the same email already exists.
if (error as NSError?)?.code == AuthErrorCode.accountExistsWithDifferentCredential.rawValue {
// Get pending credential and email of existing account.
let existingAcctEmail = (error as NSError).userInfo[AuthErrorUserInfoEmailKey] as! String
let pendingCred = (error as NSError).userInfo[AuthErrorUserInfoUpdatedCredentialKey] as! AuthCredential
// Lookup existing account identifier by the email.
Auth.auth().fetchSignInMethods(forEmail: existingAcctEmail) { providers, error in
if (providers?.contains(GoogleAuthProviderID))! {
// Sign in with existing account.
Auth.auth().signIn(with: "? ? ? ?") { user, error in
// Successfully signed in.
if user != nil {
// Link pending credential to account.
Auth.auth().currentUser?.link(with: pendingCred) { result, error in
// Link Facebook to Google Account
}
}
}
}
}
}
}
}

Swift Firebase -How can I verify the `PhoneAuthCredential` and keep the user currently signed in with their current email uid

Users sign into my app with email authentication. Once inside they can browse and search for different things. But if the user wants to post they have to verify their phone number (if they don't want to post their phone number isn't necessary).
The phone number and sms process works fine but once I authenticate the PhoneAuthCredential the uid associated with the email that the user is currently signed in with is replaced with the uid generated from the phone credential. This creates a situation where an entirely new user is inside the app and because of this they don't have access to any of their data (anything associated with the uid from the email).
Basically the Auth.auth().currentUser?.uid was initially the email's uid and now the Auth.auth().currentUser?.uid would be the phone's uid
How can I verify the PhoneAuthCredential and keep the user currently signed in with their current email uid?
var emailUid: String? // a6UVVWWN4CeTCLwvkn...
var verificationId: String?
var phoneUid: String? // tUi502DnKlc19U14xSidP8
// 1. user signs into the app with their email address and their uid is a6UVVWWN4CeTCLwvkn...
Auth.auth().signIn(withEmail: emailTextField.text!, password: self.passwordTextField.text!, completion: {
(authDataResult: AuthDataResult?, error) in
self.emailUid = authDataResult?.user.uid // a6UVVWWN4CeTCLwvkn...
})
// 2. user goes to post something but before they can post they have to verify their phone number
PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumberTextfield.text!, uiDelegate: nil) {
(verificationID, error) in
guard let verificationID = verificationID else { return }
self.verificationId = verificationID
}
// 3. sms code is sent to user's phone and they enter it
let credential = PhoneAuthProvider.provider().credential(withVerificationID: verificationId!, verificationCode: smsTextField.text!)
// 4. now VERIFY sms code by signing the user in with the PhoneAuthCredential
Auth.auth().signInAndRetrieveData(with: credential, completion: {
(authDataResult, error) in
self.phoneUid = authDataResult?.user.uid // tUi502DnKlc19U14xSidP8 this is now the current user's uid
// 5. save phoneNumber and verificationId to the user's uid ref associated with the EMAIL address
var dict = [String: Any]()
dict.updateValue(verificationId!, forKey: "verificationId")
dict.updateValue(phoneNumberTextfield.text!, forKey: "phoneNumber")
dict.updateValue(self.phoneUid!, forKey: "phoneUid")
if Auth.auth().currentUser!.uid == self.emailUid! {
// THIS WILL NEVER RUN
let emailUidRef = Database.database().reference().child("users").child(emailUid!)
emailUidRef?.updateChildValues(dict)
}
})
You can link the two accounts together using Firebase Authentication's account linking. As that documentation says:
Complete the sign-in flow for the new authentication provider up to, but not including, calling one of the FirebaseAuth.signInWith methods.
So you skip signInAndRetrieveData(with: credential), but instead call User.linkAndRetrieveData(with: credential). Once the accounts are linked, you can sign in with either of them to get the "combined" authenticated user.

Firebase Facebook login check if user exist

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...

Resources