Automatic merge when existing account with email&password provider on Firebase - firebase

I've been using invertase#react-native-apple-authentication to implement Sign in with Apple combined with firebase authentication.
When I perform this code snippet and choosing "Share my Email" and I already have an existing “email & password-provider” on Firebase with the same email as the one I have on my Apple ID, the providers automatically merge.
const appleAuthRequestResponse = await appleAuth.performRequest({
requestedOperation: AppleAuthRequestOperation.LOGIN,
requestedScopes: [AppleAuthRequestScope.EMAIL, AppleAuthRequestScope.FULL_NAME],
})
const { user, email, nonce, identityToken } = appleAuthRequestResponse
const appleCredential = firebase.auth.AppleAuthProvider.credential(identityToken, nonce)
const userCredential = await firebase.auth().signInWithCredential(appleCredential)
If I instead were to use for example Facebook sign in through react-native-fbsdk to sign in with these lines:
const credential = firebase.auth.FacebookAuthProvider.credential(accessToken)
firebase.auth().signInWithCredential(credential)
the auth module correctly throws an error ‘auth/account-exists-with-different-credentials’ since it detects that an account already occupies the email-address on firebase.
The issue is that I cannot stop the merge from happening during the sign in flow, unless I manually prevent it by looking up the authenticated Apple email's firebase login providers in-between the request and the sign-in.
I’d wish to be able to handle an error such as auth/account-exists-with-different-credentials even when signing in with Apple, but unfortunately nothing gets thrown.
Is this behaviour intended?
react-native-firebase version 5.6.0
Firebase/Auth version 6.15.0
Kind Regards, Jonathan

This is the expected behavior since Firebase consider some providers for instance Facebook as not trusted provider.
Is it possible to check if an email is confirmed on Facebook?
In case of trusted providers for instance Apple / Google the merge can happen automaticly.
Note: if you first sign in via non trusted provider later you sign in via trusted provider, then the non trusted provider will be unlinked / overwritten for security reasons. After all if you want to login via non trusted provider (for instance your unlinked provider) then 'auth/account-exists-with-different-credential' will be thrown since the email is already registered with another provider in this case you have to ask user to login with a known provider then manualy link the two auth providers.

Related

Is there a way to log out a specific user using firebase auth go sdk?

background of this question
I'm using firebase auth for user authentication on my app.
I realized that firebase doesn't have a log of user information changes, so I can't answer user questions about it.
So, I'm planning to move the feature of changing user account info (like email, display name, and password) from using the client-side firebase auth library to using server-side firebase auth SDK for the purpose of taking logs of these changes to use for user support. Also, I'd like to make logout a user who changes account info.
I've looked for the appropriate API on the document firebase.google.com/go/v4/auth and found UpdateUser function. The struct UserToUpdate which is a parameter of UpdateUser can set a new email address, new password and new display name, but I can't find to set the parameter to make a user logout.
my question
Is there a way to log out a specific user by firebase auth go SDK?
Firebase Authentication's client-side sign-in is based on ID tokens, which are valid until their built-in expiration (by default: an hour after they are minted). Since no server keeps a list of all the ID tokens it has minted, there is no way to mark a token as invalid on such a list either.
The common approach to revoke access for a user is to:
Revoke the refresh token, so that they can no longer mint new ID tokens with it.
Add the ID token(s) of the user to a self-managed list of revoked ID tokens.
Detect the presence of an ID token in this list from your server-side code and security rules.
Optionally detect the refresh token revocation on the client
Instead of logging the user out, you can also force-refresh their ID token/profile on the client to get the latest information from the server.

Firebase cookies without user login [duplicate]

I use Flutter and Firebase, just ask myself how to allow users to add items to cart when they're not logged in yet and keep the cart when they logging in, anyone have an idea for this ?
On Firebase you'd typically start the user off with an anonymous authentication account. With that sort of account, Firebase generates a user ID (UID) for the user, without them having to enter any credentials. You then associate the cart/items with the UID, and then when you/they are ready to identify themselves, you can sign them in with another provider, and link that to the anonymous account.
I've provided links to the documentation for Android developers above, but the same functionality is available in the FlutterFire libraries too. For example, anonymous sign-in on Flutter is as simple as:
UserCredential userCredential = await FirebaseAuth.instance.signInAnonymously();
you can store this data in a separate database SQL lite as an example

firebase react-native: convert anonymous user to a permanent user via phone auth

My react-native application allows users to sign in anonymously using firebase. After that I am hoping to allow user to create an account via firebase phone auth and convert this anonymous account into a permanent account.
I think this is possible based on the api & documentation provided by firebase
anonymous account creation:
https://rnfirebase.io/docs/v5.x.x/auth/reference/auth#signInAnonymously
phone auth creation & retrieve credential:
https://rnfirebase.io/docs/v5.x.x/auth/reference/PhoneAuthProvider#credential
Linking an existing user with a new credential:
https://rnfirebase.io/docs/v5.x.x/auth/reference/User#linkWithCredential
https://firebase.google.com/docs/auth/web/account-linking
Here's what I did
anonymous account creation:
const {user: {uid: userID}} = await firebase.auth().signInAnonymously();
// successfully created and save this user's id
get the users to authenticate and get the phone auth credential:
const credential = firebase.auth.PhoneAuthProvider.credential(verificationId, code);
at this point I would have a valid credential & an existing anonymous user ready for linking
I then provide the phone auth credential to my anonymous user for linking
const authUser = await firebaseAuth.currentUser.linkWithCredential(credential);
I expected a successful linking to happen based on my inputs, but I'm getting errors instead, the error I'm getting is
Error: This credential is already associated with a different user account.
Error code is: ERROR_CREDENTIAL_ALREADY_IN_USE
in the documentation under ERROR_CREDENTIAL_ALREADY_IN_USE, they did mention:
this error could be thrown if you are upgrading an anonymous user to a Google user by linking a Google credential to it and the Google credential used is already associated with an existing Firebase Google user.The fields error.email, error.phoneNumber, and error.credential (AuthCredential) may be provided, depending on the type of credential. You can recover from this error by signing in with error.credential directly via auth#signInWithCredential.
But I have not been able to find error.credential under the error object to come back from the error.
I have seen many working example of linking anonymous user with other auth providers, but never seen the linking of a firebase anonymous user with a firebase phone auth crendential. Does anyone know if this type of linking is supported? Could anyone point out what I'm doing wrong here?
Follow up:
So I actually found out that my code did work, and that is how you'd implement firebase anonymous user -> firebase phone auth account linking.
The reason this didn't work for me was because..... for our app, we had a micro-service creating accounts in the backend every time the user inputs the confirmation code. In hindsight I should have really double checked our own codebase, lesson learned.

When using Firebase Authentication service, how to throw error when login by Facebook/Google with no existing account

I'm developing a Flutter app using Firebase Authentication service.
The following Flutter plugins are used:
Firebase_auth
Google_sign_in
Facebook_login
The login flow is:
Login with either Facebook, Google or Email&Password
If account has already been created, logged in
If not, throws error
This is possible if the logging method is Email&Password. In Firebase_auth plugin, there are two separate methods for Email&Password scenario: createUserWithEmailAndPassword and signInWithEmailAndPassword. When the sign in method is called with a non existed account, it will throw a wrong id/password error.
However, for the Facebook/Google login method, the plugin only provides
signInWithFacebook and signInWithGoogle and the way they work is that the first time user uses facebook/google account to sign in, Firebase will automatically create an account and return the newly created account. (no separate sign up and sign in process)
I also read on the Firebase doc for Android Google Sign in
After a user signs in for the first time, a new user account is created and linked to the credentials—that is, the user name and password, phone number, or auth provider information—the user signed in with. This new account is stored as part of your Firebase project, and can be used to identify a user across every app in your project, regardless of how the user signs in.
So, my question is: If user logins with Fb/G account that has not yet been used to register with my app on Firebase, how do I make Firebase authentication throw error instead of automatically create a new account ?
Malcolm from the Firebase team here! Great question.
Given the functionality that currently exists in the open source Flutter plugins, you can likely get the result you desire by using the method #fetchProvidersForEmail(). Here are the logical steps you'll follow for the federated IDPs:
Do normal sign in the with IDP and get a token.
Parse that returned token for the user's email (usually using a JWT parsing library).
Call #fetchProvidersForEmail() with the extracted email.
If the providers that come back for the email are empty, then it's a new account. Otherwise, it's an existing account.
Alternatively, you could update the Flutter plugin to return more of the AuthResult, which includes whether or not the user is new. If the user is new, then you just call FirebaseAuth#getCurrentUser()#delete() and throw whatever error you wanted. (Which you would also have to add to the plugin).

Authenticate with signInWithCredential()

I'm trying to connect to the second Firebase app and authenticate with signInWithCredential(), but I don't know how to get valid idToken for the second app:
connect(accessToken: string, config: FirebaseAppConfig) {
let one: firebase.app.App = this.angularFireTwo.database["fbApp"];
one.auth().currentUser.getToken()
.then(idToken => firebase.auth.GoogleAuthProvider.credential(idToken, accessToken))
.then(credential => {
let two = firebase.initializeApp(config, `[${config.apiKey}]`);
return two.auth().signInWithCredential(credential);
})
.catch(console.warn)
.then(console.info);
}
I'm getting and error from https://www.googleapis.com/identitytoolkit/v3/:
Invalid id_token in IdP response
If I use signInWithPopup() I can authenticate and connection is working:
two.auth().signInWithPopup(new firebase.auth.GoogleAuthProvider())
Anyone knows what should I do to get valid idToken?
UPDATE:
I've been trying to figure out authentication process and, as far I understand it , it's something like this:
from config: FirebaseAppConfig firebase reads apiKey and authDomain
it contacts the servers and gets Web Client ID for enabled Google provider 123.apps.googleusercontent.com
with this Web Client ID and authDomain it contacts www.googleapis.com, which returns idToken
this idToken is then used to identify the app that's asking user for permission to access user's profile, etc.
when user agrees, callback returns user details + credential used for this authentication, which contains idToken of the web app and accessToken of the user
Now, if I use signInWithPopup() steps 2-3-4 are done in the background (popup window). I just need a way to generate idToken for the step 4, so I can use it to generate credential firebase.auth.GoogleAuthProvider.credential(idToken, accessToken) and sign-in using signInWithCredential().
I have access to everything I need to sign-in to the second app - it's; apiKey, authDomain, Web Client id 456.apps.googleusercontent.com, and user's unique accessToken.
But still can't figure out how to do it. I tried white-listing apps' one and two Web client IDs in their auth configurations, hoping that will allow them to accept each others idTokens, but that didn't work...
When you call:
firebase.auth.GoogleAuthProvider.credential(idToken, accessToken))
The first parameter should be a Google OAuth Id token. You are using the Firebase Id token and that is why you getting the error. Besides, if you are already logged in, why are you logging in again with signInWithCredential?
If you need to sign in with a Google credential you need either a Google OAuth Id token or a Google OAuth access token.
To duplicate Firebase OAuth sign-in state from one app to another, you get the credential from signInWithPopup result and use it to signInWithCredential in the second instance.
two.auth().signInWithPopup(new firebase.auth.GoogleAuthProvider())
.then(function(result) {
return one.auth().signInWithCredential(result.credential);
});

Resources