Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add verifyPhoneNumber #38

Open
kyleabens opened this issue Mar 24, 2022 · 20 comments
Open

feat: add verifyPhoneNumber #38

kyleabens opened this issue Mar 24, 2022 · 20 comments

Comments

@kyleabens
Copy link

Is your feature request related to a problem? Please describe:
I use two-factor authentication in my app and would love it if you could implement verifyPhoneNumber. I'm not sure if signInWithPhoneNumber would work in this case (I haven't tried it yet);

Describe the solution you'd like:

To be able to generate a verificationId with verifyPhoneNumber.

Describe alternatives you've considered:

I've tried using the firebase sdk and it works on web and Android but iOS won't load the recaptcha needed to verify the phone number and generate the verificationId.

@robingenz robingenz self-assigned this Mar 24, 2022
@robingenz
Copy link
Member

Hi @kyleabens,
this should already work (see https://github.com/robingenz/capacitor-firebase/tree/main/packages/authentication#signinwithphonenumber).

import { FirebaseAuthentication } from '@capacitor-firebase/authentication';

const signInWithPhoneNumber = async () => {
  const { verificationId } = await FirebaseAuthentication.signInWithPhoneNumber(
    {
      phoneNumber: '123456789',
    },
  );
  const verificationCode = window.prompt(
    'Please enter the verification code that was sent to your mobile device.',
  );
  await FirebaseAuthentication.signInWithPhoneNumber({
    verificationId,
    verificationCode,
  });
};

Let me know if this is what you are looking for.

@kyleabens
Copy link
Author

kyleabens commented Mar 24, 2022

@robingenz verifyPhoneNumber for multi factor authentication is a little different since it takes a session instead of a phone number. Would love to see this added as an option. Should still return a verification ID. I’ve been trying to piece it together by modifying the plugin but I’m struggling with swift and type casting. I keep getting an error when I try to use cap.getObject(“session”) because it’s then not the right type when passing to verifyPhoneNumber as MultiFactorSession type. I’m new to a lot of this so any advice would be great.

This is the exact error I get in xcode when I try to pass the session that I obtain in my ionic app

Cannot convert value of type 'JSObject?' (aka 'Optional<Dictionary<String, JSValue>>') to expected argument type 'MultiFactorSession'

@robingenz
Copy link
Member

robingenz commented Mar 24, 2022

Okay, i will have a look 👍

@trancee
Copy link
Contributor

trancee commented Aug 3, 2022

Just as a side note, this would probably come in handy using the Multi-factor Auth:
https://firebase.google.com/docs/auth/android/multi-factor

@kyleabens
Copy link
Author

any update on adding this?

@VictorienTardif
Copy link

Hi, did you find a solution @kyleabens ? I also really need this.

@etischenko
Copy link

Also interested in this feature. I'm going to try creating a custom capacitor plugin just for the MFA feature on iOS but I would really love to just use this plugin instead.

Any updates?

@typefox09
Copy link

Looking to do the same, any updates?

@VictorienTardif
Copy link

I did a quick and dirty patch to enable MFA on iOS: 2fe9ba0

This is really ugly, it was just the minimum and the fastest way to get something working.

  1. I added a verifyPhoneNumberToEnrollSecondFactor function that sends a SMS and return a verification ID.
  2. This is the really ugly part, on signInWithEmailAndPassword (but it should be on every sign in method), I intercept the auth/multi-factor-auth-required error, I send an SMS and... brrr... I reject the call with the verification ID as error message.

Not ideal at all but at least I was able to send the SMS to enroll second factors and on sign in when MFA is enabled.

@typefox09
Copy link

I've noticed the problem with this it will only sign the user in on the native layer with MFA, however if we want to also sign them in on the JS layer (essential to use Firestore), it's not possible?

So for example, we sign the user in using your patch (on the native layer), we then take the credential returned and use this on the JS layer with signInWithCredential(auth, credential), would this 2nd call to login on the JS layer now fail with the error "auth/multi-factor-auth-required"?

@VictorienTardif
Copy link

VictorienTardif commented Mar 27, 2023

I login the user both on the native layer and the JS layer in parallel and I use the verificationId from the native layer in the JS layer (and luckily it works).

This is what I do in JS:

// Sign in
  async signInWithEmail(email: string, password: string) {
    let verificationId: string | undefined;

    if (this.isIOS) {
      // To be able to handle second factor enrollment and verification on iOS
      // we have to be signed in on native layer
      try {
        await CapacitorFirebaseAuth.signInWithEmailAndPassword({
          email,
          password,
        });
      } catch (e) {
        // If a second factor is already enrolled,
        // an SMS is automatically send and the verificationId is returned as errorMessage
        if (
          e.code === 'auth/multi-factor-auth-required'
        ) {
          verificationId = e.errorMessage;
        }
      }
    }

    try {
      const result = await signInWithEmailAndPassword(
        this.auth,
        email,
        password
      );
      return Promise.resolve(result);
    } catch (e) {
      // On iOS, if a second factor auth is required,
      if (
        this.isIOS &&
        e instanceof FirebaseError &&
        e.code === 'auth/multi-factor-auth-required'
      ) {
      // we get the resolver from the JS error
        const resolver = getMultiFactorResolver(
          this.auth,
          e as MultiFactorError
        );
      }
      
        // We ask the user for the verificationCode
       // and we can use the verificationID we got from the native signin error
      
    
      
      const cred = PhoneAuthProvider.credential(verificationId, verificationCode);
      const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);
   
      // resolve mfa signin 
      // Attention, the resolver is never resolved on the native layer. We only sign in the user on the JS layer
      resolver.resolveSignIn(multiFactorAssertion);
      
      //....
    }
  }

@typefox09
Copy link

How will this work when we need to enroll users in MFA? There's a different process that happens there (still relies on Recaptcha)

@VictorienTardif
Copy link

VictorienTardif commented Mar 27, 2023

That's why I added the verifyPhoneNumberToEnrollSecondFactor :)

In JS:

verifyPhoneNumberToEnrollSecondFactor(
    phoneNumber: string,
    recaptchaVerifier: RecaptchaVerifier
  ) {
    if (this.isIOS) {
      return CapacitorFirebaseAuth.verifyPhoneNumberToEnrollSecondFactor({
        phoneNumber,
      }).then(({ verificationId }) => verificationId);
    }

    return multiFactor(this.auth.currentUser)
      .getSession()
      .then((session) => {
        const phoneAuthProvider = new PhoneAuthProvider(this.auth);
        return phoneAuthProvider
          .verifyPhoneNumber({ phoneNumber, session }, recaptchaVerifier)
          .then((verificationId) => verificationId);
      });
  }

This way the SMS is sent on the native layer without using the racpatcha.

@olanb7
Copy link

olanb7 commented Feb 15, 2024

Is this still the best approach for supporting phone number MFA on iOS @robingenz?

@VictorienTardif
Copy link

"best" can't be used here, let's say the "less bad" lol. This is really not a good and reliable solution, besides I have a lot of problems with reCaptcha verification on iOS when silent notifications don't work (https://firebase.google.com/docs/auth/ios/phone-auth#set-up-recaptcha-verification), I don't understand how to deal with it.

@VictorienTardif
Copy link

Well, I finally added

if url.absoluteString.contains("://firebaseauth/link?deep_link_id=") {
  return true
}

in

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {

of AppDelegate.swift to prevent app to open the firebase dynamic link when it fallbacks to reCaptcha and it works :)

Again, a quick and dirty way to make things work.

@kumarmanishc
Copy link

With same package and feature I'm getting this error,

Module not found: Error: Can't resolve 'firebase/auth' in '/ionic-app/node_modules/@capacitor-firebase/authentication/dist/esm

@kumarmanishc
Copy link

kumarmanishc commented Apr 22, 2024

Hey @kyleabens @robingenz
I have fixed above issue and now sms is also getting sent without using reCaptcha.

However when we try to send OTP It's opening chrome and verifying recaptcha automatically..
My experience while using cordova was different.. App was verifying the request via sha256 and sending OTP without opening browser for recaptcha verification.

How can I avoid that?

`

await FirebaseAuthentication.addListener('phoneCodeSent', (event: any) => {
   this.verificationId = event.verificationId;
});

  await FirebaseAuthentication.signInWithPhoneNumber({
    phoneNumber: `${this.loginForm.value.phoneNumber}`,
  });

`

@kumarmanishc
Copy link

@robingenz

Any update on above ?

Google has changed their ways of verifying request using App Check.

But We don't have option to verify app-check with this sdk... I think that's the reason

@robingenz
Copy link
Member

No updates yet. I'm currently working on a few other issues. Feel free to submit a PR. I will take a look then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants