9
votes

I am building a new Vue.js app based on the Webpack template. I have a /sign-in route that loads a component called SignIn. I am trying to use Firebase Phone Number authentication using the Firebase SDK to authenticate my users.

I've installed Firebase with npm install firebase and initialized it in my main.js file like this:

/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 },
});

My credentials are redacted for security in the above example.

When the user is on /sign-in, this is the component that they see:

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

As you can see, I have two forms in the template - one to capture the phone number and another to allow the user to enter the verification code. I am toggling the visibility of these forms programmatically.

When the component mounts, I am calling the Firebase reCAPTCHA verifier and passing the ID of the submit button ("get-sign-in-code" in this case). However, when I click the button, nothing happens. I don't see the reCAPTCHA XHR in my dev tools network tab.

Could this be because the button is being inserted into the DOM dynamically and firebase.auth.RecaptchaVerifier() cannot detect it when I pass the ID when the component mounts? How do I resolve this? Could I use $el or some other Vue.js method to get the reCAPTCHA verifier to work? Thanks.

UPDATE

I was able to get this script to work by adding the following lines to the mounted() event:

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

This is how my mounted() method looks like now:

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 however cause a new issue - the script now adds a randomly positioned "Protected by reCAPTCHA" badge that I'd like to get rid off. Is there any way to get this script to work without showing the badge?

2

2 Answers

1
votes

I guess it is late to answer. But I see in your code when mounted() hook appeared you call sendSMS() function in recaptchaVerifier after callback. But when on subbitting first button which is:

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

no function showed to call on submit or on click. I guess you would update your button to react clicking on it like below: change is in form tag that showing which function to call on submit form.

<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>
-1
votes

Use sign-in-button do "Protected by reCAPTCHA"