Phone number authentication - howto

#firebase #recaptcha #integration #customelement #wixfirebase #signin #sms #OTP
Here is a write-up on how to integrate firebase authentication in wix

Firebase authentication provides solution for phone/email/social(fb,google,twitter etc.,) and more
Here I have documented the phone authentication mechanism.
Note: This solution is only for authenticating / validating a phone number via SMS using a one time password. This is not a free SMS solution.
Firebase phone authentication is free for up to 10k authentication per month.

Typical use case scenario:
user keys in phone number in your website → website initiates firebase authentication → an SMS is sent to the user’s phone number with an otp → user enters the otp in your website → website requests firebase to authenticate/validate → firebase returns validation result → website does it’s thing based on the result.

In the background firebase uses recaptcha, so at times, user may be required to solve a recaptcha challenge.

Pre-requisites:

  1. Setup a firebase project, details in the link below :

https://firebase.google.com/docs/auth/web/phone-auth

  • Once you have created project and enabled phone authentication, you can
  • setup test numbers
  • setup authorised domains
  • setup template messages
  1. Firebase/auth NPM module - add the firebase npm module or just the firebase/auth npm module based on your requirements.

Setup in wix

You could implement this whole solution as a custom element, or use a combination of velo + custom element. I have used the latter.

High level steps:

  1. custom element

     	- create a button, which is required for firebase as a recaptcha container 
     	(I have made this button invisible and programatically triggered the click event) 
     	- use the attribute changed callback to handle each of the following 
     		* initialise firebase and setup recaptcha 
     		* send sms and render the recaptcha verifier 
     		* validate code 
     2. Page code 
     	- entirely upto you on how you want to implement this, as the meat of the matter is in the custom element code 
     	- I have just created elements for invoking the attribute changed callback as mentioned above 
    

custom element code

import app from 'public/firebase_init.js'
import firebase from 'firebase/app';
import 'firebase/auth';

const createButton = () => {
 const buttonElement = document.createElement('Button');
 buttonElement.innerHTML = "button";
 buttonElement.id = 'send-sms-button';
 buttonElement.style.display = "none";
 return buttonElement;
}

const getView = () => {
 // the window object
 return document.defaultView;
}

const getDoc = () => {
 return document;
}

class MobileAuthenticatorCustomElement extends HTMLElement {

 constructor() {

 super();
 this.authInstance = app.auth();
 this.DOCUMENT = getDoc();
 this.DEFAULT_VIEW = getView();
 this.triggersmsbutton = createButton();
 this.appendChild(this.triggersmsbutton);
 this.phoneNumber = undefined;
 app.auth().useDeviceLanguage();

 }
 static get observedAttributes() {
 return ['setupcaptcha', 'sendauthsms', 'validatecode'];
 }

 connectedCallback() {

 }

 attributeChangedCallback(name, oldValue, newValue) { // is invoked when one of the custom element's attributes is added, removed, or changed
 console.log("Attribute changed call back name:", name, " old val:", oldValue, " new val:", newValue);

 if (name === "setupcaptcha") {

 if (this.DEFAULT_VIEW !== undefined) {
 if (!this.DEFAULT_VIEW.recaptchaVerifier) {
 this.DEFAULT_VIEW.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('send-sms-button', {
 'size': 'invisible',
 'callback': () => {

 this.onSendSmsButtonClick();

 }
 });
 }

 } else {
 this.DEFAULT_VIEW = getView();
 if (!this.DEFAULT_VIEW.recaptchaVerifier) {
 this.DEFAULT_VIEW.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('send-sms-button', {
 'size': 'invisible',
 'callback': () => {

 this.onSendSmsButtonClick();

 }
 });
 }

 }
 } else if (name === "sendauthsms") {
 this.phoneNumber = newValue;
 this.DEFAULT_VIEW.recaptchaVerifier.render()
 .then((widgetId) => {
 this.DEFAULT_VIEW.recaptchaWidgetId = widgetId;
 this.DOCUMENT.getElementById("send-sms-button").click()
 })
 .catch((err) => (console.log("captcha error ", err)));

 } else if (name === "validatecode") {
 const code = newValue;
 this.confirmationResult.confirm(code).then((result) => {
 console.log("Code authentication result ", result);

 this.dispatchEvent(new CustomEvent("authenticated", {}))
 this.confirmationResult = null;
 this.resetReCaptcha();

 }).catch((error) => {
 console.log("Code authentication error ", error);
 this.dispatchEvent(new CustomEvent("notauthenticated", {}))

 })

 }

 }

 onSendSmsButtonClick() {

 return new Promise((resolve, reject) => {
 let appVerifier = this.DEFAULT_VIEW.recaptchaVerifier;
 app.auth().signInWithPhoneNumber(this.phoneNumber, appVerifier).then((confirmationResult) => {
 console.log("Confirmation result ", confirmationResult);
 this.confirmationResult = confirmationResult;
 this.dispatchEvent(new CustomEvent("smssent", {}))
 resolve(confirmationResult);
 }).catch((confirmationErr) => {
 console.log("Confirmation err ", confirmationErr);
 this.dispatchEvent(new CustomEvent("smsnotsent", {}))
 reject(confirmationErr);
 })
 })

 }

 resetReCaptcha() {
 this.DEFAULT_VIEW.recaptchaVerifier.clear();

 if (typeof grecaptcha !== 'undefined' &&
 typeof this.DEFAULT_VIEW.recaptchaWidgetId !== 'undefined') {
 grecaptcha.reset(this.DEFAULT_VIEW.recaptchaWidgetId);

 }
 }

}
customElements.define('mobile-authenticate-ce', MobileAuthenticatorCustomElement);	

page code

$w.onReady(function () {
// TODO: write your page related code here…

$w(‘#mobileAuthCE’).on(‘smssent’, () => {
// do your actions to be performed once the SMS has been sent
});

$w(‘#mobileAuthCE’).on(‘smsnotsent’, () => {
// error handling in case SMS not sent
});

$w(‘#mobileAuthCE’).on(‘authenticated’, () => {
// actions to be performed on authentication success
});

$w(‘#mobileAuthCE’).on(‘notauthenticated’, () => {
// actions to be performed on authentication failure
});

});

export function setupCaptchaButton_click(event) {

$w(“#mobileAuthCE”).setAttribute(“setupcaptcha”, “null”);

}

export function sendSMSButton_click(event) {
// This function was added from the Properties & Events panel. To learn more, visit Velo: Working with the Properties & Events Panel | Help Center | Wix.com
// Add your code for this event here:
$w(“#mobileAuthCE”).setAttribute(“sendauthsms”, $

w(“#mobilenumberinput”).value);
}

export function checkCodeButton_click(event) {
$w(“#mobileAuthCE”).setAttribute(“validatecode”, $w(“#mobileverificationcodeinput35”).value);
}


screen shot of SMS received with otp.

8 Likes

Nice :slight_smile:

You are the best human on earth, you saved my life Thanks a lot Mr. prashanth bhaskaran

Can you tell me Where should I add the custom element code and how? @prashanthbhaskaran

do take a look at this :

I copied the codes as is but it didn’t work for me

of course with firebase configuration

need more details, where are you stuck? do you see any error messages

can you make a video tutorial of how to do this please, I’m so lost Mr.@ prashanth bhaskaran

Its give this error:
Uncaught TypeError: t.default is undefined
c wix-default-custom-element.js:2
wix-default-custom-element.js:109
wix-default-custom-element.js:112
wix-default-custom-element.js:112

This is what I did, I copied all your codes

Sorry, I’m not good with interpreting videos. When and where do you get the exception? I need more details to understand what’s going on.
I suggest you take it one step at a time.

  1. Make sure that the custom element is setup and you are able to confirm communication between your page and the element.

  2. Ensure the Firebase app initialisation is happening

  3. Then you can start checking the interactions among the components.
    Just a copy paste of my sample code will not work out of the box.
    Cheers.

Hi @prashanthbhaskaran ,
Can you please provide a video or tutorial blog, or maybe a example site which we can duplicate at our end. It will be very much helpful.
I have followed the above steps, but I am sure that something is missing that will link to that particular firebase app. I have used firebase on android before but no experience with web or JavaScript.
Waiting for some step by step guide.
Thanks

Hi @prashanthbhaskaran ,
Waiting for your response, I am also facing same issues as @support71783 faced.

@support71783 Did you still face the issue or it’s resolved.
I also want to implement mobile verification.

where to paste it??

@prashanthbhaskaran Question… would this same concept work if I wanted to limit the amount of logged in users to 1 device? I am trying to avoid someone purchasing my subscription, then sharing their login credentials with other people to access my exclusive content for paid members only. It would be nice if when someone goes to log in from a non-recognized device, that a SMS verification code is sent that the user must enter before logging in. Just trying to avoid “same login credentials being used on multiple devices”. Any thoughts would be highly appreciated. Thanks!

this solution is to confirm validity of a phone number, will not satisfy your use case. I am sure there will be solutions that meets your requirement. Will let you know if I come across one. Best of luck.

hey @prashanthbhaskaran acording to the firebase poicy " Developers should ensure they have appropriate end-user consent prior to using the Firebase Authentication phone number sign-in service."
how did you manage to do this?

  1. did you ask your user to accept the terms and condition check box .

  2. or is it ok to just ignore this
    please let me now what you did .

thank you in advance .

You should add it, this post is just an example, with the minimum elements and code so not to confuse with other stuff.

@prashanthbhaskaran this is the code i have written to sent the sms . but there is problen the sms is not going . And can we use the firebase auth ui for this ?

// API Reference: https://www.wix.com/velo/reference/api-overview/introduction
// “Hello, World!” Example: https://learn-code.wix.com/en/article/1-hello-world
import { initializeApp } from "firebase/app";
import { getAuth, signInWithPhoneNumber } from "firebase/auth";

$w.onReady(function () {
  // Import the functions you need from the SDKs you need

// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
  apiKey: "-D7cpDOVI2CCWrQ",
  authDomain: "-8300a.firebaseapp.com",
  projectId: "-8300a",
  storageBucket: "-8300a.appspot.com", // dont worry about this config 
  messagingSenderId: "",
  appId: "1:17717882880:",
  measurementId: "G-"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const phoneNumber = $w("#inputnumber").onChange(()=>{$w("#inputnumber").value});
// const appVerifier = window.recaptchaVerifier; I have commented this


const auth = getAuth();
// get otp btn on click
$w("#getotp").onClick(()=>{

signInWithPhoneNumber(auth, 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;   I have commented this

      // ...
    $w("#login").onClick(()=>{
const code = $w("#codeinput").value;
confirmationResult.confirm(code).then((result) => {
  // User signed in successfully.
  const user = result.user;
  console.log('sucessful');
  // ...
}).catch((error) => {
  // User couldn't sign in (bad verification code?)
  // ...
  console.log("bad verification code");
});
  })

    }).catch((error) => {
      // Error; SMS not sent
      // ...
    console.log('sms was not send');
    });
})
  
});

And also could you please help me find out the problem
and I cant figure out where to write the this part of the code

const code = $w("#codeinput").value;
confirmationResult.confirm(code).then((result) => {
  // User signed in successfully.
  const user = result.user;
  console.log('sucessful');
  // ...
}).catch((error) => {
  // User couldn't sign in (bad verification code?)
  // ...
  console.log("bad verification code");
});