Having trouble getting Firebase phone number authentication to work with Vue.js

I am currently in the process of developing a new Vue.js application using the Webpack template. Within this app, I have implemented a /sign-in route that displays a component named SignIn. To authenticate users, I am utilizing Firebase Phone Number authentication through the Firebase SDK.

My approach involved installing Firebase with npm install firebase, and setting it up in my main.js file as shown below:

/src/main.js

import firebase from 'firebase';

import Vue from 'vue';
import App from './App';
import router from './router';

Vue.config.productionTip = false;

// Initialize Firebase
const config = {
  apiKey: 'MY_API_KEY',
  authDomain: 'MY_PROJECT.firebaseapp.com',
  databaseURL: 'https://MY_PROJECT.firebaseio.com',
  projectId: 'MY_PROJECT_ID',
  storageBucket: 'MY_PROJECT.appspot.com',
  messagingSenderId: 'MY_SENDER_ID',
};

firebase.initializeApp(config);

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  template: '<App/>',
  components: { App },
});

The credentials have been hidden for security reasons in the above example.

When a user is on the /sign-in page, they will encounter the following component:

/src/components/pages/SignIn.vue

<template>
  <div>
    <!-- Number Input Form -->
    <div v-if="showNumberInput">
      <form v-on:submit.prevent>
        <div class="form-group">
          <input type="text" class="form-control form-control-lg" v-model="numberInputForm.number" placeholder="Phone number" required>
        </div>
        <div class="form-group">
          <button type="submit" id="get-sign-in-code" class="btn btn-block btn-lg success theme-accent">{{ getSignInCodeButton.text }}</button>
        </div>
      </form>
    </div>

    <!-- SMS Verification Form -->
    <div v-if="showCodeInput">
      <form>
        <div class="form-group">
          <input type="text" class="form-control form-control-lg" value="9944" placeholder="Verification Code" required>
        </div>
        <div class="form-group">
          <a href="javascript:void" class="btn btn-block btn-lg success theme-accent" @click="signIn">{{ signInButton.text }}</a>
        </div>
      </form>
    </div>
  </div>
</template>

<script>
import firebase from 'firebase';

export default {
  name: 'SignIn',

  data() {
    return {
      // UI States
      showNumberInput: true,
      showCodeInput: false,

      // Forms
      numberInputForm: {
        number: '',
      },

      // Buttons
      getSignInCodeButton: {
        text: 'Get sign in code',
      },
      signInButton: {
        text: 'Sign in',
      },
    };
  },

  mounted() {
    const self = this;

    // Start Firebase invisible reCAPTCHA verifier
    window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('get-sign-in-code', {
      size: 'invisible',
      callback: (response) => {
        // reCAPTCHA solved, allow signInWithPhoneNumber.
        self.sendSMS();
      }
    });
  },

  methods: {
    /**
     * Sends the user an SMS-verification code using Firebase auth
     *
     * @see https://firebase.google.com/docs/auth/web/phone-auth
     */
    sendSMS() {
      const self = this;

      self.getSignInCodeButton = {
        showSpinner: true,
        text: 'Sending SMS..',
        disabled: true,
      };
    },


    /**
     * Authenticates the user with Firebase auth
     */
    signIn() {
      // Redirect the user to the authenticated page
    },
  },
};
</script>

You can observe that there are two forms within the template - one designed to collect the phone number, and another that prompts the user to enter the verification code. The visibility toggling for these forms has been programmed accordingly.

Upon mounting the component, the Firebase reCAPTCHA verifier is called by passing the ID of the submit button ("get-sign-in-code" in this case). However, upon clicking the button, no action occurs, and there is no evidence of the reCAPTCHA XHR in the network tab of the dev tools.

Could this be due to the fact that the button is dynamically inserted into the DOM, causing

firebase.auth.RecaptchaVerifier()
to overlook it when the component mounts? How can this issue be resolved? Is there a way to make the reCAPTCHA verifier function properly using $el or other Vue.js techniques? Thank you for your assistance.

UPDATE

To address the issue, I made adjustments to the mounted() event by adding the following lines:

window.recaptchaVerifier.render().then((widgetId) => {
  window.recaptchaWidgetId = widgetId;
});

Here is the modified mounted() method:

mounted() {
  const self = this;

  // Start Firebase invisible reCAPTCHA verifier
  window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('get-sign-in-code', {
    size: 'invisible',
    callback: () => {
      // reCAPTCHA solved, allow signInWithPhoneNumber.
      self.sendSMS();
    },
  });

  window.recaptchaVerifier.render().then((widgetId) => {
    window.recaptchaWidgetId = widgetId;
  });
},

This adjustment led to a new challenge - the script now introduces a randomly positioned "Protected by reCAPTCHA" badge that I wish to eliminate. Is there a way to resolve this while ensuring the script functions without displaying the badge?

Answer №1

It may be a bit overdue, but I noticed while reviewing your code that the sendSMS() function is called within the mounted() hook after a callback in the recaptchaVerifier. However, when the first button is submitted:

<button type="submit" id="get-sign-in-code" class="btn btn-block btn-lg success theme-accent">{{ getSignInCodeButton.text }}</button>

there seems to be no specified function to call upon submission or click. You may want to consider updating your button so that it reacts to clicks like this: the change is made in the form tag, which specifies which function to call upon form submission.

<div v-if="showNumberInput">
  <form v-on:submit.prevent="sendSMS">
    <div class="form-group">
      <input type="text" class="form-control form-control-lg" v-model="numberInputForm.number" placeholder="Phone number" required>
    </div>
    <div class="form-group">
      <button type="submit" id="get-sign-in-code" class="btn btn-block btn-lg success theme-accent">test</button>
    </div>
  </form>
</div>

Answer №2

If you are using nuxt.js (a vuejs framework), here are the steps you need to follow:

To your nuxt.config.js file, add the following lines:

import 'firebase/compat/auth'; //import this on the top and

 modules: [
    ...// other code
    [
      '@nuxtjs/firebase',
      {
        config: {
          apiKey: '<apiKey>',
          authDomain: '<authDomain>',
          projectId: '<projectId>',
          storageBucket: '<storageBucket>',
          messagingSenderId: '<messagingSenderId>',
          appId: '<appId>',
          measurementId: '<measurementId>'
        },
        services: {
          auth: true // Just as example. Can be any other service.
        }
      }
    ]
  ],

Then, in your file where you want to use phone authentication, first import firebase like this:

<template>
///
</template>

<script>
import firebase from 'firebase/compat/app';

methods : {
    // configure recaptcha
    
    configureRecaptcha() {
      window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier(
        "otp-verfiy-button",
        {
          size: "invisible",
          callback: (response) => {
            sendOtpForVerification();
          },
        }
      );
    },
    
    // handle otpsend
      sendOtpForVerification() {
      this.configureRecaptcha();
      const phoneNumber = "+91" + this.userMobile; //user phone number
      const appVerifier = window.recaptchaVerifier;
      firebase.auth().languageCode = "en";

      firebase.auth()
        .signInWithPhoneNumber(phoneNumber, appVerifier)
        .then((confirmationResult) => {
          // SMS sent. Prompt user to type the code from the message, then sign the
          // user in with confirmationResult.confirm(code).
          window.confirmationResult = confirmationResult;
          this.$toast.success("Otp sent successfully");
        })
        .catch((error) => {
          // Error; SMS not sent
          console.log("Error", error);
        });
    },
    
    
}
</script>

Make sure you have installed both firebase and @nuxtjs/firebase packages.

Hopefully, this guide will assist someone :)

Answer №3

Utilize the sign-in-button to display the message "Secured with reCAPTCHA"

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 the power of jQuery to efficiently bind events to elements through associative

I am attempting to link the same action to 3 checkboxes, with a different outcome for each: var checkboxes = { 'option1' : 'result1', 'option2' : 'result2', 'option3' : 'result3', }; ...

What is the solution for the error message "Unhandled Runtime Error" with the description "TypeError: videoRef.current.play is not a function"?

I am currently working on implementing custom controls for a video on a Nextjs website. When using a standard HTML <video> component, the code functions as expected and clicking the custom play button successfully plays the video. However, when I swi ...

Using a toolbar to insert a hyperlink for hypertext communication

My journey with Javascript and React began this week, so I'm still getting the hang of things, especially in the front end domain. In my project, there's a link button within a toolbar. The idea is to click on it, have a text box pop up where yo ...

Ways to use jQuery to disable row form elements in the second and third columns

I need a way to deactivate form elements in the second and third columns, starting from the second row until the nth row using a jQuery selector. <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/> ...

What are the solutions for fixing a JSONdecode issue in Django when using AJAX?

I am encountering a JSONDecodeError when attempting to send a POST request from AJAX to Django's views.py. The POST request sends an array of JSON data which will be used to create a model. I would greatly appreciate any helpful hints. Error: Except ...

calling this.$emit results in a TypeError being thrown

I have a specific requirement for my Vue3 component where I need to emit an event during a method call. The code structure is as follows: export default { emits: ['event'], methods: { myMethod () { this.$emit('event') // t ...

The button is converting my text to a string instead of the integer format that I require

Hello everyone, I've been grappling with this button conundrum for the last 45 minutes, and I can't seem to find a solution. I have created three different buttons in my code snippet below. (html) <div class="action"> ...

A curated collection saved in LocalStorage using React JS

I have implemented a LocalStorage feature to create a favorite list, but it only adds one item each time the page is reloaded. The items are retrieved from a JSON file. For a demonstration of how my code functions, check out this link: const [ storageIte ...

Retrieving the date from the final item in a JSON object using AngularJS

In my web application built with AngularJS, I am dynamically creating objects. The goal is to allow users to load new events by clicking a button. To achieve this functionality, I need to determine the date of the last event loaded so that I can fetch the ...

Can anyone tell me the location of the modalColor with the background set to 'greenYellow' in the popup window?

Take a look at the sample in jQuery.bPopup.js called Example 2b I am trying to design a popup window with customized text and background style, using the Example 2b, custom settings: Simple jQuery popup with custom settings (Jamaican popup, relax man) $ ...

Data merging in Firebase 9 and Vue 3 is not functioning properly

I am facing an issue with merging data in my firebase database. I consulted the documentation at https://firebase.google.com/docs/firestore/manage-data/add-data for guidance. After attempting to merge data using setDoc, I encountered an error (Uncaught Ty ...

Dealing with lag problems while feeding a massive dataset into the Autocomplete component of Material-UI

In my React project, I have integrated the Autocomplete component from Material-UI to enhance user experience. However, when attempting to pass a large dataset of 10,000 items to the Autocomplete component as a prop, there is a noticeable delay of approxim ...

Gather keyboard information continuously

Currently working with Angular 10 and attempting to capture window:keyup events over a specific period using RXJS. So far, I've been facing some challenges in achieving the desired outcome. Essentially, my goal is to input data and submit the request ...

Is there a more efficient method for invoking `emit` in Vue's Composition API from an external file?

Is there a more efficient way to access the emit function in a separate logic file? This is my current approach that is functioning well: foo.js export default (emit) => { const foo = () => { emit('bar') }; return { foo }; } When ...

Is there a way to modify a document without altering its original location?

I attempted to load an entire page using ajax, with the doctype and html tags removed. However, when I tried setting it with the following code: document.documentElement.innerHTML=xmlhttp.responseText; Google Chrome returned an error message: An invalid ...

Receive AJAX aid for PHP/MySQL dynamic dropdown menu implementation

My goal is to dynamically populate a second drop-down menu based on the selection made in the first drop-down. The first drop-down contains a list of tables from my database, and the second drop-down should display providers associated with the selected ta ...

What causes the element to load with a transparent appearance? And why is my JavaScript code overriding the hover effect on it?

My goal is to have an element fade out when clicked and then fade back in when its space is clicked again. I also want the opacity of the element to be 0.9 instead of 1 when visible. I've encountered two issues with my code. First, the hover selector ...

Error: Unable to access the 'resource' property as it is undefined

I am currently working on a project that involves fetching the latest 4 results from Craigslist using a query. Although I have successfully retrieved all the relevant information, I seem to be encountering an issue with loading the URL of the image from th ...

What is the best way to efficiently import multiple variables from a separate file in Vue.JS?

When working with a Vue.JS application and implementing the Vuex Store, I encountered an issue in my state.js file where I needed to import configurations from another custom file, specifically config.js. Upon running it, I received the following warning ...

What is the best way to load a database URL asynchronously and establish a database connection prior to the initialization of an Express

My express.js app is set up to run on AWS lambda, with the database URL stored and encrypted in Amazon KMS. To access the URL, decryption using the AWS KMS service is required. // imports import mongoose from 'mongoose'; import serverless from & ...