Open kyleabens opened 2 years ago
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.
@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'
Okay, i will have a look 👍
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
any update on adding this?
Hi, did you find a solution @kyleabens ? I also really need this.
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?
Looking to do the same, any updates?
I did a quick and dirty patch to enable MFA on iOS: https://github.com/capawesome-team/capacitor-firebase/commit/2fe9ba0c24db2131c46335c95d7e23d94abb3709
This is really ugly, it was just the minimum and the fastest way to get something working.
verifyPhoneNumberToEnrollSecondFactor
function that sends a SMS and return a verification ID.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.
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"?
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);
//....
}
}
How will this work when we need to enroll users in MFA? There's a different process that happens there (still relies on Recaptcha)
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.
Is this still the best approach for supporting phone number MFA on iOS @robingenz?
"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.
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.
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
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}`,
});
`
@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
No updates yet. I'm currently working on a few other issues. Feel free to submit a PR. I will take a look then.
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.