EddyVerbruggen / nativescript-plugin-firebase

:fire: NativeScript plugin for Firebase
https://firebase.google.com
MIT License
1.01k stars 444 forks source link

How to handle the prompt for Phone Authentication Verification Code? #751

Open faiyaz26 opened 6 years ago

faiyaz26 commented 6 years ago

I am trying to use phone number based authentication. It seems this library auto-generates a prompt for the verification code input. Is it possible to handle this prompt from the application side ( like I want to show a form )?

Another thing, when the prompt is being shown, if I hit the cancel button, my application gets stuck, what type of exception handling should I use for that?

tmldsousa commented 6 years ago

Please keep in mind that, even though this solution works, this is undocumented behavior, and it can stop working in any update.

This is how I fixed this on my project

import * as firebase from "nativescript-plugin-firebase";

// change requestPhoneAuthVerificationCode, which is the internal method that opens the prompt. this only needs to be done once
firebase["requestPhoneAuthVerificationCode"] = onRequestPhoneAuthVerificationCode;

// call firebase login
const loginPromise = firebase.login({
  type: firebase.LoginType.PHONE,
  phoneOptions: { phoneNumber, verificationPrompt: promptMessage }
})

// this will be called when the plugin asks for the sms code
function onRequestPhoneAuthVerificationCode(
  onUserResponse: (phoneAuthVerificationCode: string) => void,
  verificationPrompt: string) {

    // ... this is where you can write your code to open your prompt, or do whatever you like

    // this is the original code, which can be found here: https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/master/src/firebase-common.ts#L131
    prompt(verificationPrompt || "Verification code").then(promptResult => {
      if (!promptResult.result) {
        return;
      }
      onUserResponse(promptResult.text);
    });
}

Also consider that the plugin behavior is different between Android and iOS:

In my opinion, this should and can easily be supported by the plugin. Maybe @EddyVerbruggen could weigh in on how this could be added to the plugin so that someone can open a PR.

asimpleidea commented 6 years ago

@tmldsousa thanks man, you're awesome. I was looking for a way to override the popup since there is not even a way to change the title text and buttons text! And for a non-english application that's not good to see. Hope this gets implemented in a better way.

arxpoetica commented 5 years ago

@EddyVerbruggen bump on this. I would like to open a PR on this so that I can do something other than an prompt to enter a code—I'd like to build out my own page that's a better UI for this sort of thing. Thoughts?

isawk commented 5 years ago

@EddyVerbruggen it appears once you remove prompt from phone verification login the entire process stops. How can one present their own UI. Similar to Whatapp verification process?

isawk commented 5 years ago

Eventual resolved custom log in by return a promise in firebase["requestPhoneAuthVerificationCode"], once firebase returns user, returned promise's resolve method is called to complete verification/login flow.

Code snippets below


  // reference to nativescript-plugin-firebase onUserResponse function needed to complete authentication verification code capturing
  private _funcUserResponse: (results: string) => {};

  //global variables that reference promise resolve and reject to be used later on

  verificationPromiseResolver = null;
  verificationPromiseRejector = null;

  verificationPromise = new Promise((resolve, reject) => {
    this.verificationPromiseResolver = resolve;
    this.verificationPromiseRejector = reject;
  });

    firebase["requestPhoneAuthVerificationCode"] = (onUserResponse, verificationPrompt) => {
      this._funcUserResponse = onUserResponse;
      return this.verificationPromise;
    }
virtualbjorn commented 5 years ago

@tmldsousa great find! I managed to do this to my own custom screen and used observables to emit events

Here's how I did this in a way that it works well on Android but having haven't tested it on iOS which gives me a Custom URL Scheme error which I apparently have to add to Info.plist

Tried adding the given custom URL to Info.plist but still no luck, and I think the way I added it to Info.plist is not right.

EDIT: Finally made it work in iOS with these lines added to my Info.plist

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>com.googleusercontent.apps.{{CLIENT_ID}}</string>
        </array>
    </dict>
</array>

export class VerificationObservableModel extends Observable { private _verificationCode: number; private _verificationResponse: any; private _verificationError: any;

get verificationCode(): number {
    return this._verificationCode;
}
set verificationCode(value: number) {
    this._verificationCode = value;
}
get verificationResponse(): any {
    return this._verificationResponse;
}
set verificationResponse(value: any) {
    this._verificationResponse = value;
}
get verificationError(): any {
    return this._verificationError;
}
set verificationError(value: any) {
    this._verificationError = value;
}

}

- **login.component.ts**
```TS
import { FirebaseService } from "~/app/services/firebase/firebase.service";
import * as firebase from 'nativescript-plugin-firebase';
firebase["requestPhoneAuthVerificationCode"] = onRequestPhoneAuthVerificationCode;
import { VerificationObservableModel } from "~/app/models/verification-custom-observable";

var verificationObservable: VerificationObservableModel = new VerificationObservableModel();

function onRequestPhoneAuthVerificationCode(onUserResponse: (phoneAuthVerificationCode: string) => void, verificationPrompt: string) {
    verificationObservable.on("onverify", (data) => {
        onUserResponse(data.object.get("verificationCode"));
    });
}

@Component({
    selector: "ns-login",
    moduleId: module.id,
    templateUrl: "./login.component.html",
    styleUrls: ["./login.component.scss"]
})
export class LoginComponent {
    onCodeVerificationSend() {
        this.isProcessing = true;
        firebase.logout();
        this._firebase.onSendSMSVerification(this.phoneNumber);
        setTimeout(() => {
            this.isProcessing = false;
            this.isCodeSent = true;
        }, 3000);
    }

    onCodeVerify() {
        this.isProcessing = true;
        verificationObservable.set("verificationCode", this.verificationCode);
        let eventData: EventData = {
            eventName: "onverify",
            object: verificationObservable
        };
        verificationObservable.notify(eventData);
        this._firebase.verificationObservable.on("onverificationsuccess", (data) => {
            console.dir(data.object.get("verificationResponse"));
            this.isProcessing = false;
            this.reset();
            this._navigationService.navigateToHomeScreen(true);
        });
        this._firebase.verificationObservable.on("onverificationerror", (data) => {
            this.isProcessing = false;
            console.dir(data.object.get("verificationError"));
            alert("Verification Error");
            this.onBack();
        });
    }
}

@Injectable() export class FirebaseService { verificationObservable: VerificationObservableModel = new VerificationObservableModel();

constructor() {
    firebase.init({
        persist: true,
    }).then(() => {
        console.log("Firebase Initiliazed");
    }).catch((err) => {
        console.log(err);
    });
}

onSendSMSVerification(phoneNumber: string, promptMessage: string = '') {
    firebase.login({
        type: firebase.LoginType.PHONE,
        phoneOptions: { phoneNumber, verificationPrompt: promptMessage }
    }).then((response) => {
        this.verificationObservable.set("verificationResponse", response);
        let eventData: EventData = {
            eventName: "onverificationsuccess",
            object: this.verificationObservable
        }
        this.verificationObservable.notify(eventData);
    }).catch((err) => {
        this.verificationObservable.set("verificationError", err);
        let eventData: EventData = {
            eventName: "onverificationerror",
            object: this.verificationObservable
        }
        this.verificationObservable.notify(eventData);
    });
}

}

Nayeb-karimi commented 5 years ago

Hi I don't know how to do it with vue