Determine the existence of a Firebase Facebook user without the need to first create a user from an anonymous account

When working with Firebase, I am faced with the challenge of checking if a Facebook user exists without automatically creating a new user account. The situation arises when an anonymous user attempts to log in using their Facebook credentials, and I want this login attempt to fail if the Facebook account is not already linked to an existing user in my system.

My expectation was that using Auth.signInAndRetrieveDataWithCredential would result in an "auth/user-not-found" error if no matching user was found. However, instead of returning the expected error, a new user account is created. Is this behavior a bug or is it intentional?

let credential = firebase.auth.FacebookAuthProvider.credential(
        event.authResponse.accessToken)
    firebase.auth().signInAndRetrieveDataWithCredential(credential).then( (userCredential) => {
        let user = userCredential.user
        app.debug("DEBUG: Existing user signed in:"+user.uid)
        this.loginSuccess(user)
    }).catch( (err) => {
        app.error("ERROR re-signing in:"+err.code)
        $("#login_status_msg").text(err)
    })

On the other hand, using User.reauthenticateAndRetrieveDataWithCredential results in an "auth/user-mismatch" error when trying to use the Facebook credential with the anonymous user. While this error makes sense given the current user's status, I was hoping to receive an "auth/user-not-found" error if the credential does not exist - which unfortunately did not occur.

I am struggling to find a solution where I can allow an anonymous user to log in with Facebook and check if the provided credentials are linked to an existing user without creating a new account if one doesn't already exist.

The scenario driving this requirement is as follows: The system supports anonymous users;

  1. A user initially logs in anonymously and later converts to a registered user by linking their account with Facebook.
  2. If the app is uninstalled and then reinstalled,
  3. The user starting up the app will be anonymous once again.
  4. Upon attempting to log in using Facebook, the goal is to prevent the creation of a new user account if one is not already associated with the provided credentials. If a user ID already exists, the code should successfully transition from an anonymous account to the original user account.

Answer №1

After some trial and error, I managed to come up with a solution that, although not too difficult to implement, does feel a bit like a hack.

When dealing with Facebook login using

signInAndRetrieveDataWithCredential(cred)
, there is an issue where the account gets created even if it doesn't already exist. To address this problem, we have to make sure we handle three key things:

  1. Check if the account is new
  2. Delete the current firebase-created account
  3. Throw an error to break out of the current flow and return to the previous state

Having tested and implemented this solution, it appears to be functioning as intended:

// ...perform necessary steps for fb login and obtaining credentials:
const userInfo = await firebase.auth().signInAndRetrieveDataWithCredential(credential)

// Check if account is new from user info
const isNewUser = _.get(userInfo, 'additionalUserInfo.isNewUser', true)

// Delete newly created account and throw error if new user
if (isNewUser) {
  firebase.auth().currentUser.delete()
  throw new Error('Unable to locate existing account.')
}

// Proceed with normal login flow if user exists
return userInfo.user

I decided on this approach to ensure that users follow the "create account" process in my app. Adapting this to your scenario should be straightforward, something along these lines:

let credential = firebase.auth.FacebookAuthProvider.credential(event.authResponse.accessToken)

firebase.auth().signInAndRetrieveDataWithCredential(credential)
  .then(userCredential => {
    const isNewUser = userCredential.additionalUserInfo.isNewUser
    if (isNewUser) {
      firebase.auth().currentUser.delete()
      // Error will be caught in the catch block
      throw new Error("Unable to locate existing account.")
    }
    // Process login for existing user
    const user = userCredential.user
    app.debug("DEBUG: Existing user signed in:"+user.uid)
    this.loginSuccess(user)
  }).catch( (err) => {
    app.error("ERROR re-signing in:"+err.code)
    $("#login_status_msg").text(err)
  })

Answer №2

If you want to link and retrieve data using a credential, you can utilize the

linkAndRetrieveDataWithCredential
method:

let credential = firebase.auth.FacebookAuthProvider.credential(
    event.authResponse.accessToken);
anonymousUser.linkAndRetrieveDataWithCredential(credential).then( (userCredential) => {
  // The anonymous account is now upgraded to a permanent Facebook account.
}).catch( (err) => {
  // Handle error code: auth/credential-already-in-use
})

Answer №3

To find out if an email is linked to a specific provider, you can utilize the fetchSignInMethodsForEmail method. By using this method, you can determine whether the SignInMethods associated with the email contain Facebook.com or not.

Below, I have provided an example of how I handle these scenarios in my application. Although I am using an RxJavaWrapper in my code, the main concept remains the same:

 RxFirebaseAuth.fetchSignInMethodsForEmail(authInstance, email)
                .flatMap { providerResult ->
                    if (!providerResult.signInMethods!!.contains(credential.provider)) {
                        return@flatMap Maybe.error<AuthResult>(ProviderNotLinkedException(credential.provider))
                    } else {
                        return@flatMap RxFirebaseAuth.signInWithCredential(authInstance, credential)
                    }
                }
                .subscribe({ authResult ->
                  //Handle success
                }, { error ->
                  //Handle error
                })
  • Initially, I examine the providers linked to the user's email (this information can be obtained from the provider).
  • If the list of SignInMethods includes my credential provider, I generate an error; otherwise, I proceed to call my signInWithCredential method to create the user.
  • Continue with your workflow accordingly.

Answer №4

To tackle this issue without depending on the call to

linkAndRetrieveDataWithCredential
failing and resorting to the catch block for signing in the existing user, I opted to store the userID value returned by getCurrentAccessToken.

const { userID } = data;
this.props.setFacebookId(userID); // preserving userID on the server

In future, I can verify if this userID already exists when the user registers with facebook again.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using JavaScript to open a window in ASPX page

When I use the code below to open a new window and receive a success message, I also want to close the current window. However, I am encountering an error: Error: newwindow is not defined var value = getQueryStrings(); var cust_id = value["cust_id ...

Ending a connection to MS SQL server in node.js with mssql library

In my journey to write a node.js script for querying an MSSQL database, I find myself navigating the realm of JavaScript, node.js, and VSCode with limited SQL knowledge. While I have managed to piece together working code, the issue lies in the connection ...

Why is my react-hook-form sending an array with no data when making an axios request?

Within a container, I have mui Chips that contain labels. The objective is to utilize these chips as selectors, using onClick to add the label values to a list that will be sent in an Axios request. This needs to be achieved without the use of a textfield, ...

Can one initiate a server within Zapier Code?

Is there a way to send an HTTP CODE = 200 with a response body of 'OK' when receiving a notification on Zapier? I attempted to use the following code snippet in Zapier: var http = require('http'); const server = http.createServer((r ...

jqGrid display/hide columns options dialogue box

I am looking to implement a feature that allows users to toggle columns in my jqGrid. I came across a module for this purpose, but unfortunately, it is no longer supported in jqGrid 4.x. I have searched for a replacement module without success. Are there ...

Once upon a time in the land of Storybook, a mysterious error message appeared: "Invalid version. You must provide a string

I keep encountering an issue while attempting to set up storybook. Can anyone help me figure out what's causing this problem? npx storybook@latest init • Detecting project type. ✓ TypeError: Invalid version. Must be a string. Got type "obje ...

Accessing external data in Angular outside of a subscription method for an observable

I am struggling to access data outside of my method using .subscribe This is the Service code that is functioning correctly: getSessionTracker(): Observable<ISessionTracker[]> { return this.http.get(this._url) .map((res: Response) => ...

AngularJS controllers and global scope management

As per AngularJS's tutorial, the controller function is said to reside within the global scope. http://docs.angularjs.org/tutorial/step_04 Are the controller functions themselves automatically encapsulated in a scope, or do they actually exist withi ...

Tips for maintaining the data type of a typed array in typescript while clearing it?

During a recent class, I defined an array with the type CustomType as shown below: class Example { public exampleArray: CustomType[]; public clearArray() { this.exampleArray = []; } } It appears that the clearArray method assigns an UNDEFINED t ...

NodeJS making seven successful Ajax requests

I'm delving into the world of JavaScript, NodeJS, and electron with a goal to create a presenter-app for remote control over powerpoint presentations. My setup involves an electron server structured like this: const electron = require('electron ...

Incomplete data retrieval issue encountered during .ajax call

I am having trouble retrieving all 4 key|value pairs from a page that displays an object as text in the body and pre tag. It seems to be missing one pair - any ideas why? Just a heads up, I've tweaked some of the URLs and data in the JSON output for ...

WooCommerce Checkout and My Account Edit Address now feature dynamic selectable fields for improved customization options

After finding a solution on Stack Overflow to show sub Areas dropdown based on the selected city in WooCommerce checkout, I customized the code for my specific requirements: function cities_areas_settings() { $text_domain = 'woocommerce'; ...

React-Native: Issue with state not being updated despite using useEffect and fetch

Currently, I am attempting to make a GET fetch request to a specific endpoint. The issue I am facing is that upon running the application, I am encountering the following error message: Possible Unhandled Promise Rejection (id: 39): TypeError: Cannot read ...

The reason why Array.indexOf struggles to identify identical-looking objects

I am facing a problem with finding objects in an array. Here is an example of what I have: var arr = new Array( {x:1, y:2}, {x:3, y:4} ); When I use the following code: arr.indexOf({x:1, y:2}); It returns -1. If I have strings or numbers or other ...

Improving the adaptive design of a breadcrumb navigation trail

.breadcrumb input[type="radio"] { display: none; } .breadcrumb input[type="checkbox"] { display: none; } .question-header input[type="radio"] { display: none; } .panel-body input[type="radio"]~label { cursor: pointer; width: 100%; text-align ...

What is the relationship between Angular's $watch function and a slider component?

Hello everyone! I am a beginner in the world of Web Development and I've run into an issue. Luckily, I was able to create a fiddle showcasing my problem for better understanding. The challenge at hand involves two sliders: one for a value and another ...

What is the solution to resolving the error "null property 'role' cannot be read"?

I'm encountering an issue whenever I enter the following code: if(message.member.roles.cache.has(`something here`)) An error is displayed stating "Cannot read property 'role' of null" Does anyone have any solutions or suggestions to resol ...

How to eliminate spacing between slides in a 3D Carousel Slider using Bootstrap 5

My website utilizes Bootstrap along with a carousel slider, which is functioning properly. However, I am encountering an issue when the slider transitions from right to left. It briefly displays a white space between slides, which I wish to eliminate. I wa ...

Display the elements of a div at a reduced size of 25% from its original dimensions

I'm currently developing a JavaScript-based iOS simulator that allows users to view their created content on an iPhone and iPad. This involves using AJAX to load the content/page into the simulator, but one issue is that the simulator isn't life- ...

Is there a way to extract the values from a range slider individually and then display them as the minimum and maximum values on the screen?

Currently, I am facing an issue with a range slider where the value I am retrieving is concatenated. For example, when printed, it appears as 2080, with 20 and 80 being separate values visually combined on screen. My goal is to extract the minimum and maxi ...