Is the new Firebase Phone number Auth on the roadmap?
Yeah it will be. Will look at seeing what's possible to do
@jasan-s @Ehesp - might make sense to wait until android support is out for this at the end of may: https://firebase.google.com/docs/auth/android/phone-auth
They will support this? Awesome ๐
Since I currently use Facebook Account Kit to verify phone numbers, I actually consider waiting with my first release until I can use it with firebase.
I believe its already available as they have open sourced their SDKs
@jeveloper it's only available in ios at the moment
ohh thats odd, i thought they were just reusing fabrics digits which works on anything
thanks man!
Maybe _now_ is too early June :)
Phone number sign-in is coming soon to Firebase on Android. Check this page in early June 2017 for details.
Yesterday it said late May! :)
+1 I am also really interested in this feature!
+1 We're anxiously awaiting this feature after Google finishes implementation for Android
https://firebase.google.com/support/release-notes/android#20170607
Now available on Android SDK!
Is anyone assigned or already working a PR to this issue? If not, I could try to do it myself.
@Sparragus no one specifically. I'll take a look at Android on Monday if theres no PR. IOS looks a bit too complicated for my object c skills
I've been looking into this today. I'll have to speak internally about how to actually implement this (more best practice). The aim of this lib is to try and replicate the web SDK, however I don't think it'll be possible here.
signInWithPhoneNumber
takes a phoneNumber & appVerifier (where appVerifier is redundant on the device).verifyPhoneNumber
. It can also do things like auto detection. The response of which, can be directly passed into signInWithCredential
.@chrisbianca @Salakar How do you suggest dealing with the following workflow:
After calling verifyPhoneNumber
, we return a promise which would resolve on onVerificationCompleted
(Instantly verified or auto detected). The issue is though, there's an optional onCodeSent
which would prompt the user to enter the code manually (as it's not been detected automagically). How would that fit into the API?
firebase.auth().verifyPhoneNumber('+441234567')
.then((credential) => {}) // onVerificationCompleted
.catch(() => {});
That may potentially never get called, as it's hit the onCodeSent
block. We could add a method like verifyPhoneCode
or something, but then how would the user know whether to use this or not.
Hello, I successfully got it working for iOS. Unfortunately I don't have the time for a PR but I will describe how I went on. First, the NativeModule (my module is called WHOFirebaseAuth
here)
//
// WHOFirebaseAuth.m
// WhoCaresAppNew
//
// Created by Enes Kaya on 29.05.17.
// Copyright ยฉ 2017 Facebook. All rights reserved.
//
#import "WHOFirebaseAuth.h"
#import <FirebaseAuth/FirebaseAuth.h>
@implementation WHOFirebaseAuth
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(verifyPhoneNumber:(NSString*) userInput
callback:(RCTResponseSenderBlock) callback)
{
[[FIRPhoneAuthProvider provider]
verifyPhoneNumber: userInput
completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
NSLog(@"WHOFirebaseAuth: %@", userInput);
if (error) {
callback(@[error.localizedDescription, @"ERROR"]);
NSLog(@"WHOFirebaseAuth: %@", error.localizedDescription);
} else {
// Sets the verficiationID in UserDefaults for later usage
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:verificationID forKey:@"authVerificationID"];
callback(@[[NSNull null], verificationID]);
}
}];
}
RCT_EXPORT_METHOD(getVerificationId:(RCTResponseSenderBlock)callback)
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *verificationID = [defaults stringForKey:@"authVerificationID"];
callback(@[[NSNull null], verificationID]);
}
@end
You call it like this in your RN project:
const WHOFirebaseAuth = NativeModules.WHOFirebaseAuth
...
static sendVerificationCodeTo(phoneNumber) {
return new Promise((resolve, reject) => {
WHOFirebaseAuth.verifyPhoneNumber(
phoneNumber,
(error, verificationID) => {
if (!error) {
resolve({
error: null,
verificationID: verificationID
})
} else {
reject({
error,
verificationID: null
})
}
}
)
})
}
...
After that, if it was successful, the user get's the code sent via SMS. Then you can log in like so:
/**
* Signs the user in with PhoneAuth credential.
*
* @param {[type]} verificationID The verificationID returned by this.sendVerificationCodeTo
* @param {[type]} code The code which was sent via SMS
* @return {[type]} firebase.Promise containing non-null firebase.User
*/
static signInWithPhoneAuth(verificationID, code) {
var credential = firebase.auth.PhoneAuthProvider.credential(
verificationID,
code
)
return firebase.auth().signInWithCredential(credential)
}
So, basically it's a mix between the native SDK and the JS SDK. Remember to install Firebase/Auth
via CocoaPods in version >4.0.0.
Hope that helps, for me it's working perfectly.
@eneskaya will it work on android with same syntax and all ?
@yash2code The JS part of the code yes, but obviously not the objective-c part. I will, later today implement the android side.
@eneskaya Any thought on my comment above regarding onCodeSent would be handy on Android.
@eneskaya Ty sir, I m surely waiting for that !! :)
@Ehesp not yet, but will look into that. I'll keep you posted
I was just about to post a version of myself.
What I did slightly different was I used promises rather than callbacks
RCT_EXPORT_METHOD(verifyPhone:(NSString *)userInput
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
[[FIRPhoneAuthProvider provider]
verifyPhoneNumber:userInput
completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
if (error) {
reject(@"Error on Phone Verification", @"Phone can't be verified", error);
}
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:verificationID forKey:@"authVerificationID"];
resolve(verificationID);
}];
}
So, it's a tad more difficult on Android than I thought ๐ Does anyone else have an Idea?
Need this for a project, so I can pick up the work if someone's not already on it - @eneskaya, is your work all on this comment thread or is there a branch I should work from?
Also @Ehesp: you have your android work somewhere I could peek? My ObjC is stronger than my Java so would love a starting point for the android side. Also happy to just test or chip in on something if you're aware of in-progress work.
Hi Guys, spent some time looking into this today, however we ran into a bit of an issue.
As described my myself above, there is the potential for onCodeSent
to be called, which needs handling. Also to keep this inline with the webSDK, we decided to call the method signInWithPhoneNumber
, which looks something like:
firebase.native.auth().signInWithPhoneNumber('+441234567', (confirm) => {
console.log('Show popup to enter code');
confirm('123456'); // code from text message
}).then((user) => {
console.log('Hide any manual popups for code');
console.log('User signed in: ', user);
})
.catch(console.error);
The issue is we're having trouble with the callback arg native side being null, see https://github.com/facebook/react-native/issues/14702. Not sure if this is intended or not, but if this is the case then we're going to have to hook into event emitters to handle this.
Really annoying, since the callback may be called, even if the device auto picks the code up, it still hits there first.
hi guys , any update on this? or any alternative until its ready ?
This is my (not production ready) solution for Android:
@ReactMethod
public void verifyPhoneNumber(String phoneNumber, final Callback successCallback) {
Log.d(TAG, "verifyPhoneNumber called with " + phoneNumber);
PhoneAuthProvider.getInstance().verifyPhoneNumber(
phoneNumber,
60,
TimeUnit.SECONDS,
this.reactApplicationContext.getCurrentActivity(),
new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
@Override
public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
Log.d(TAG, "onVerificationCompleted:" + phoneAuthCredential);
Log.d(TAG, phoneAuthCredential.toString());
// Don't know what to do here though :(
}
@Override
public void onVerificationFailed(FirebaseException e) {
Log.d(TAG, e.toString());
successCallback.invoke(e.toString(), null);
}
@Override
public void onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
successCallback.invoke(null, verificationId);
Log.d(TAG, "onCodeSent");
}
}
);
}
This could work if the onVerificationCompleted
callback would hand us the credentials to sign in, but its an Object which we cannot simply pass to our JS side... Will investigate next week. ๐ถ
Keep in mind, this solution cannot work 100% of the time! If the request can be verified by Google Services without sending an SMS code, the onCodeSent
method never gets called.
Sounds like current challenges are:
onVerificationCompleted
resultonCodeSent
may or may not be called, so the code or API must be resilient to this case.I'm not sure I completely understand facebook/react-native/issues/14702, but I'm sure the case will become obvious if I fiddle with the implementation.
I have some time this weekend and some evenings next week to chip away at this stuff if it's needed, and I'm following along here so if there's progress elsewhere I'd love to know!
@jesseditson went through the Firebase docs and no mentioning whatsoever of serializing the "PhoneAuthCredential" class. What you can get is the SMS code, but the verification ID is missing so, yeah.
Is this working with React-Native guys? Can i let the users sign in using phone numbers with firebase on React-Native App?
@eneskaya @jesseditson to keep it closer to the web SDK and not to worry about the PhoneAuthCredential
being passed to JS side, we decided to call (as per my example above) it signInWithPhoneNumber
which signs the user in if it's correct (much like signInWithEmailAndPassword
).
We got the SMS coming through fine, however this is the issue:
onCodeSent
is called directly before onVerificationCompleted
. Therefore you can't use a singular promise/callback to handle this. Our solution was to use both, so the callback was called for onCodeSent
with a validate
function as the arg. If the user validated the code and/or the phone auto detected it, it'd go to the promise. Issue is, it seems you can't mix the two on the native side.You may now be thinking "why not just use multiple callbacks" and shim it JS side. Well we could, but every other auth method is currently setup to handle a promise result JS side, so this would need handling.
One other solution we've thought about, but again isn't so simple, is to keep the setup with promises going, but instead of using callbacks native side, use the event emitter, which JS land picks up and passes through to the users callback.
We are working on it, not so easy when it needs to tie into the library though. Standalone it would be easy to do.
For those interested, here's where I currently got to, which doesn't work yet.
@ReactMethod
public void signInWithPhoneNumber(String phoneNumber, final Callback callback, final Promise promise) {
PhoneAuthProvider.OnVerificationStateChangedCallbacks callbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
@Override
public void onVerificationCompleted(PhoneAuthCredential credential) {
signInWithCredential(credential, promise);
}
@Override
public void onVerificationFailed(FirebaseException exception) {
promiseRejectAuthException(promise, exception);
}
@Override
public void onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingToken token) {
callback.invoke(verificationId);
}
};
PhoneAuthProvider.getInstance().verifyPhoneNumber(phoneNumber, 60, TimeUnit.SECONDS, mReactContext.getCurrentActivity(), callbacks);
}
JS implementation (bit stinky while we try and get it working)
signInWithPhoneNumber(phoneNumber: string, onCodeSent: () => {}): Promise<Object> {
return new Promise((resolve, reject) => {
let hasResolved = false;
const handleResolve = (user) => {
if (!hasResolved) {
hasResolved = true;
resolve(user);
}
};
const handleReject = (e) => {
if (!hasResolved) {
hasResolved = true;
reject(e);
}
};
FirebaseAuth
.signInWithPhoneNumber(phoneNumber, (err, verificationId) => {
console.log('CALLED!')
onCodeSent((code) => {
FirebaseAuth.signInWithCredential('phone', verificationId, code)
.then(handleResolve)
.catch(handleReject);
});
})
.then(handleResolve)
.catch(handleReject);
});
}
Hey @eneskaya , I have created the NativeModule and implemented your strategy into my application, however when building, I run into 'FirebaseAuth/FirebaseAuth.h' file not found
.
I'm able to reference Firebase.h via my AppDelegate, and all pods installed correctly. Your WHOFirebaseAuth.xcodeproj was added to the Libraries folder.
Any ideas?
Edit 1: I've spent a good portion of the day attempting to modify the header paths, linker flags and more. The header file is able to import <React/RCTBridgeModule.h>
but not matter what I put, it cannot find 'FirebaseAuth/FirebaseAuth.h'
.
Edit 2: Using react-native-create-library
, and carefully matching the .xcodeproj Build Settings from react-native-firebase
, I was able to solve my dependency issue. Since the Native Module exists outside of node_modules, I had to manually drag the .xcodeproj to Libraries in Xcode, add it on Link Binary With Libraries. On the Android side, I declared it in settings.gradle, compiled it in build.gradle, and referenced it in my MainApplication.java as per other modules. I also wrote the ios.js and android.js files to link the Native Module to javascript calls. I will do some testing to recreate eneskaya's results, and dabble in Android as well.
In case anyone is interested, I used the code provided by @eneskaya (thanks!) with exception to the last part; signInWithPhoneAuth(verificationID, code)
. I wanted to keep everything in the Native SDK and skip the JS SDK.
I modified two files in the library, making the two additions below:
ios/RNFirebase/auth/RNFirebaseAuth.m
~ Line 479
else if ([provider compare:@"phone" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
credential = [[FIRPhoneAuthProvider provider]
credentialWithVerificationID:authToken
verificationCode:authTokenSecret];
}
This adds phone
as a Provider so we can use firebase.auth().signInWithCredential(credential)
natively. You just pass the credential
as an object:
const credential = {
provider: 'phone',
token: verificationID,
secret: code
};
index.d.ts
~ Line 455
/**
* Link the user with a 3rd party credential provider.
*/
linkWithCredential(credential: Credential): Promise<User>
I wanted to be able to link Phone users to anonymous accounts already created. The Native SDK 2.0.0 already has linkWithCredential
in there, it's just missing from index.d.ts
. I'm sure it's already on the authors agenda.
I can now call firebase.auth().currentUser.linkWithCredential(credential);
from my code and link with the with current user, just pass the same credential object above.
I prefer not to make the quick hacks to the library, but the building blocks are there, just needed a quick fix for iOS until the library authors provide the native solution for Android & iOS.
Hope it helps someone else out in a similar situation. Happy to provide a PR for the above mentioned changes to the library.
Thanks, that insight helps - we'll experiment with it as well. Right now we use twillo... which I want to get rid off ๐
I got a response back on my issue. Seems it's like that for a reason. I'll have to use an event emitter in this case. Will start looking at that today hopefully.
@Ehesp Any updates on your progress? Any thing I can do to help with this?
@Ehesp any luck with this one? :)
Unfortunately I couldn't find a satisfying way yet for Android. @brianhiss What do you mean by 'keep everything in the Native SDK and skip the JS SDK'? Are you logged in on JS side automatically when you login through the Native SDKs? That would make the hassle of auto-verficiation for no trouble at all.
@eneskaya, I'm not using the JS SDK, just the iOS native code through this project, react-native-firebase. The only way I could get your great example to work, was to add the JS Web SDK to my project, but that dependency, at least for me, caused issues between my anonymous users. Basically creating two users, instead of one. (One for the Native SDK, one for the JS Web SDK)
Maybe there was a way to get them to work together better, but instead I just managed to make some tweaks to the react-native-firebase library to suit my needs, pushing everything through the Native SDK.
Wish I could be more help on Android, but it's not a requirement for right now and my Java is pretty non-existent.
For iOS, I've taken bits and pieces from what people have mentioned here and put together something working here: https://github.com/soulglo/react-native-firebase
It allows for verifying the phone number and then either signing in with it or linking it to an existing account.
Also added phoneNumber as a user property too.
@soulglo David, thanks for your effort!
Edit: Please add closing bracket in the createUserWithEmailAndPassword ;) There is small typo.
@awojtczyk Cheers.. any more clues on where the missing bracket is? My caffeine hasn't kicked in yet...
Thanks to the great people here, I have a working version in my fork: https://github.com/BuffMcBigHuge/react-native-firebase
Usage
onPhoneLogin()
,verifyPhoneNumber()
is called,verifyCode()
is called.onPhoneLogin() {
if (Platform.OS === 'ios')
// Open number pad, user enters number
else
// Tell user Android is coming soon
}
verifyPhoneNumber() {
Firebase.auth().verifyPhoneNumber("+1"+this.state.phoneNumber)
.then((verificationID) => {
this.setState({
verificationID
});
// Open number pad, user enters code
})
.catch((error) => {
// Handle Error
console.log(error);
});
}
md5-845eb3f246345ca323e7d63f613469a3
verifyCode() {
let credential = {
provider: 'phone',
token: this.state.verificationID,
secret: this.state.code
};
Firebase.auth().signInWithCredential(credential).then((currentUser) => {
if (currentUser === 'cancelled')
return Promise.resolve('cancelled');
// Do your post login things...
})
.catch((error) => {
// Handle Error
console.log(error);
});
}
@soulglo: lib/modules/auth/index.js line 130 :)
@BuffMcBigHuge - after switching to your fork, I'm getting this error:
@BuffMcBigHuge I'm not sure how that will work on Android though, as it needs to handle the possibility of the promise not being returned if it's auto resolved. Will check out your iOS implementation though.
Are there any updates on this?
I reverted my last commit and all should be well with the instructions I posted above. Tested and working. Only functional for iOS at the moment.
@BuffMcBigHuge Great job! Let me know if I can get you some beer or pizza! ๐ ๐บ
Anyone up to android version?
Hi guys, great discussion here! I'm also trying to do an alternative implementation on Android side, but without success for while :(
@awojtczyk Did you or anyone else encounter this issue? Just asking.
https://stackoverflow.com/questions/45111165/app-store-rejection-due-to-firebase-phone-auth
Anyways, bravo for making this works guys!
@awojtczyk Save your donation for the good people at invertase.
@manrashid Thanks for sharing. That is certainly a concern.
@BuffMcBigHuge you already sent the PR to invertase?
@martinvarelaaaa I think the issue here (as @Ehesp has mentioned) is that the API in @BuffMcBigHuge's example is incompatible with the android API, in that the android API requires two callbacks, which is unsupported by react (see this issue). I believe the path forward is to move to an EventEmitter, which will dispatch an optional event that only ever fires on android. However, this API is very different than the Promise-based one in @BuffMcBigHuge's implementation, which as I understand it is why that implementation was not PR'd already (as it is almost identical to the prior attempts by @eneskaya and later @soulglo).
Feel free to correct me, but my understanding is that for this to be cross-compatible, the API will need to be meaningfully different than the iOS-only solutions in this thread.
If I understand, but it would be good to have it at least for iOS.
For now...
Certainly! And I'll leave it to the maintainers to decide if they want a breaking API in the interim, but my feeling is that if it were an EventEmitter, it'd be fine to merge iOS-only - but we know based on the discussion in this thread that the API will change in a breaking way if the promise/callback-based approach were to land, or the iOS and android APIs would diverge, neither of which sound very friendly to consumers. Been trying to find the time to work up an EventEmitter solution, but have had little spare time recently. Hopefully myself or someone else will be able to file a PR with a future-compatible API soon!
You're exactly right @jesseditson, we have been speaking about this a bit internally. Right now work is happening on supporting multi-apps (see branch), which affects a few different modules and the way they're setup.
If you look at the Android native code, you can see that the promises which are sent over the bridge are being passed around on the native side, to handle things such as no user, with user in helpers. Part of the problem with event emitters is that we'd have to handle this part differently too.
Solutions here work as a standalone fine, however we need full compatibility with the rest of the auth module, for example triggering onAuthStateChanged and the like.
We do have plans, and hopefully if users are desperate they can add it to their own code base without too much knowledge of java/objc with the code snippets here.
Awesome, thanks for clearing it up, and good to know about the related challenges. I checked out the multi-apps branch, is there a roadmap or summary we could peek at to see what's going on there and how things might change? If I were to spend some time on this, would it be wise to fork off that branch or master?
Found a bit of time tonight to start to work up a potential future-compatible API. I haven't tested yet, but figured I'd put the early diff here to get feedback on the API:
I'll play around with this, but I believe this pattern will allow android to be a fairly direct port of the Objective-C changes in the above patch. I tried to preserve as much as possible of the prior art on this thread rather than building from the ground up, thanks everyone who helped out so far.
An easier way to read this may be just what would be added to the docs, which describes the proposed API:
signInWithPhoneNumber(phoneNumber: string): Promise
onAuthCodeSent(handler: (verificationID: string) => void): () => void
verifyCode(verificationID: string, verificationCode: string)
To sign a user in with their phone number, use the signInWithPhoneNumber()
function.
This method will send the user a verification code, which will either be automatically verified (some android devices), or will require the user to input the code that was sent to them.
As such, you must first subscribe to the onAuthCodeSent
event, which if called, must in turn call firebase.auth().verifyCode()
. once verified (or rejected), the original promise will be resolved with a user or an error:
const auth = firebase.auth();
// install handler
const unsubscribe = auth.onAuthCodeSent((verificationID) => {
// obtain code from user, probably by showing a text box or similar.
getCodeFromUser()
.then(code => auth.verifyCode(verificationID, code))
.then(unsubscribe)
.catch(e => {
console.error('Unknown error verifying code:', e.message || e);
});
});
// then invoke the sign in method
auth.signInWithPhoneNumber('+14156666666')
.then(user => {
console.log('User successfully logged in', user);
})
.catch(e => {
console.error('User signin error', e.message || e);
});
looking forward to this working! Thanks for all the work everyone
Thanks, this feature will be great
Any idea when the feature might be available?
@jesseditson thanks for the snippet. Although it totally goes away from the web and native SDK phone auth implementation, I guess it's probably the only way. I'm not here this weekend but I can have a go on Monday. Will probably start with Android as it's the trickiest.
@jesseditson and others, bumping this into https://github.com/invertase/react-native-firebase/releases/tag/v3.0.0 - see the change logs there and the v3 milestone to see what's upcoming change wise.
As @jesseditson correctly pointed out: the solutions that have been provided so far in this issue have been iOS only, androids firebase sdk implementation is vastly different, so the solutions provided wouldn't be compatible.
@martinvarelaaaa whilst we could have released an ios only implementation initially, this causes a lot of problems later on down the line:
1) Releasing code that's already doomed to break when android comes along causes more work for us and our user base, and is just poor versioning. I think we'd all rather have a well thought out and implemented api than one that's ultimately broken. ๐ฏ / ๐ฏ
2) Platform feature disparity is a documentation & developer nightmare. We're already behind on the docs as it is, the code base is huge and we rarely get contributions from the community. There's only 3 of us actively working on the library, in our personal time might I add, with lots of all nighters to push feature releases. We're not going to actively look at making more unnecessary work for ourselves :)
3) The main point of firebase is that it's easy for new developers to get starting building their ideas, and it's great, but from this comes a lot of beginners with no concept of native or why X works on android but not iOS, and lets face it - only a few people actually read the documents before making an issue. If I had gold coin for every issue i've responded to on here with a link to the docs then I'd be Scrooge McDuck. So, even if we documented the platform disparity we're guaranteed to get these kind of issues. Ultimately it's not your concern if we get more issues, but at the end of the day we end up spending less time improving the library because of it.
4) We're a platform neutral library, we're not going to suddenly start overly devoting time to only 1 side of the coin, it's both android and ios supported before we release any feature.
Having said that.. :) I am grateful for the civilised discussions around the implementation and with the proposed solutions / api. We will add support in for this in v3, it will be both platforms and it's not going to be a rushed out no-thought given implementation.
Thanks all for your patience ๐
FWIW, I've been updating that diff over the weekend and have that branch working fully on iOS. The API changes I made on that branch make it android-compatible (in that the middle auth callback is optional). I have skimmed the firebase android docs and I think the API I worked up there will be fairly drop-in for android.
Far be it for me to make more work for the dedicated group working on this fantastic lib though, so my plan is (and has been) to complete that branch with a working cross-compatible solution, and PR directly (with docs). I'm hoping to find some time this week or next to work up the android version, then continue the discussion on a PR thread.
Some early things worth bringing up in light of the above:
Definitely LMK if there are holes in my API proposal above, I'll be working against that and it'd be super helpful to know about any bad assumptions I made!
Ward.
@jesseditson I'll take a look at the Android side today if I can.
Unlike most (all?) of the other APIs, this one diverges from the web API (because we can't pass in two callbacks). This seems OK to me as long as it's documented, but I could see this being a blocker for the PR, so would love to get the temperature of the room on that early if possible.
-@jesseditson
Short answer: Yes that's fine. Long answer: Whilst we do aim to make the API's mirror the web sdk, there's circumstances where these either don't exist on web (analytics, crash, admob etc), would not work well or at all over the react native bridge (like storage putString on the web sdk - not good practice to send large amounts of data over the bridge like that) or the native sdk is completely different to the web. So this is one of those circumstances where it's fine to deviate, as long as it's documented.
Definitely LMK if there are holes in my API proposal above
-@jesseditson
Two that I can see, sorry ๐
1) Android can automatically verify the SMS code without verifyCode required, iOS can not. Docs: Auto-retrieval: on some devices, Google Play services can automatically detect the incoming verification SMS and perform verification without user action. (This capability might be unavailable with some carriers.)
2) Android can instant verify - without the need for it to send a SMS verification code. AFAIK the way this works is if the user has previously verified in the lifetime of the app and you go to verify again it will automatically verify without a code needed. Docs: In some cases the phone number can be instantly verified without needing to send or enter a verification code.
Those two threw a spanner in the works for us when we first tried to tackle the cross platform implementation. Android is certainly the complicated one of the two, would probably be best approaching android first then moulding the iOS one around it.
Awesome, thanks for the response!
I think maybe I can do a better job documenting, but I believe I've covered the case where android auto-verifies the code without needing a prompt by the user. Here's a quick example of how that looks with the API above (using async/await to demonstrate flow):
const auth = firebase.auth()
// install handler
auth.onAuthCodeSent((verificationID) => {
// this callback is completely optional and event-driven - so if this callback is never called,
// the below code still works (which is what would happen on android if it did not need a code verification step).
// in a react environment, you probably would do something like:
this.setState({ verificationID, showCodeInput: true });
})
// then invoke the sign in method - this will not continue until the user has completed the code input
// or android instant-verifies or intercepts the SMS.
const user = await auth.signInWithPhoneNumber('+14156666666')
// If android auto-verifies, this block will be executed directly after the call above
// (or whenever the native SDK decides to resolve the user).
console.log('User successfully logged in', user);
Note that since the iOS SDK does not support this flow, it will always expect the block passed to verifyPhoneNumber:completion:
be resolved. The JS on the other hand does not make this assumption, and so android can replicate the JS API (only send the onAuthCodeSent
event if we actually need a code), and I think everything works.
I realize that this implementation is less clear than a double callback, but the responses on facebook/react-native#14702 make it pretty clear that using an event is probably one of the few ways to handle the optional verification step. Let me know if that clears things up!
As the Firebase iOS SDK does not support auto-verification like Android, is it possible to start by implementing the iOS flow on both platforms? (with manual code submission)
@jesseditson Thanks for all this. @Salakar, @Ehesp and I spent some time discussing the best way to go about this yesterday. We're keen to try and stick to the web SDK as much as we can in the first instance and get the core signInWithPhoneNumber
working without needing to introduce the additional listeners and events being sent between native and front end.
I think I've worked out a way to do this and have put something together which I need to test. Hopefully I'll have some time tomorrow and Friday to do that. If you're interested in seeing the approach, it's currently here: https://github.com/invertase/react-native-firebase/commit/87a1166070aa186c17bcf2fd3e7e74fc0f277765
@zoontek this should do exactly what you've proposed, but still handle auto-verification for Android.
Hey guys, thanks for the code snippets! Based on them I also created an iOS and Android bridge that's cross platform. I used a mix of promise and event emitter on android. It's fully working!
PS: I don't use react-native-firebase
so I use this bridge with the web sdk, but you don't have to
[ANDROID] FirebasePhoneAuthModule.java
package com.xxx.modules.firebase;
import android.util.Log;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseException;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.PhoneAuthCredential;
import com.google.firebase.auth.PhoneAuthProvider;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class FirebasePhoneAuthModule extends ReactContextBaseJavaModule {
private static final String TAG = "FirebasePhoneAuthModule";
public static final String ON_VERIFICATION_COMPLETED = "onFirebasePhoneVerificationCompleted";
// public static final String ON_VERIFICATION_FAILED = "onFirebasePhoneVerificationFailed";
// public static final String ON_CODE_SENT = "onFirebasePhoneCodeSent";
public static final String ON_CODE_AUTO_RETRIEVAL_TIMEOUT = "onFirebasePhoneCodeAutoRetrievalTimeOut";
public FirebasePhoneAuthModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "FirebasePhoneAuth";
}
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put("ON_VERIFICATION_COMPLETED", ON_VERIFICATION_COMPLETED);
// constants.put("ON_VERIFICATION_FAILED", ON_VERIFICATION_FAILED);
// constants.put("ON_CODE_SENT", ON_CODE_SENT);
constants.put("ON_CODE_AUTO_RETRIEVAL_TIMEOUT", ON_CODE_AUTO_RETRIEVAL_TIMEOUT);
return constants;
}
private void sendEvent(String eventName, Object params) {
getReactApplicationContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
@ReactMethod
public void verifyPhoneNumber(final String phoneNumber, final Promise promise) {
Log.d(TAG, "verifyPhoneNumber");
FirebaseApp firebaseApp = FirebaseApp.getInstance();
final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
PhoneAuthProvider.getInstance(firebaseAuth).verifyPhoneNumber(phoneNumber, 60, TimeUnit.SECONDS,
getCurrentActivity(), new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
@Override
public void onVerificationCompleted(PhoneAuthCredential credential) {
Log.d(TAG, "onVerificationCompleted");
sendEvent(ON_VERIFICATION_COMPLETED, credential.getSmsCode());
}
@Override
public void onVerificationFailed(FirebaseException e) {
Log.e(TAG, "onVerificationFailed", e);
// sendEvent(ON_VERIFICATION_FAILED, e);
promise.reject("firebase_auth_phone_verification_failed", e.getMessage());
}
@Override
public void onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingToken token) {
Log.d(TAG, "onCodeSent");
// sendEvent(ON_CODE_SENT, verificationId);
promise.resolve(verificationId);
}
@Override
public void onCodeAutoRetrievalTimeOut (String verificationId) {
super.onCodeAutoRetrievalTimeOut(verificationId);
Log.d(TAG, "onCodeAutoRetrievalTimeOut");
sendEvent(ON_CODE_AUTO_RETRIEVAL_TIMEOUT, verificationId);
}
});
}
}
[ANDROID] FirebasePhoneAuthPackage.java
package com.xxx.modules.firebase;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class FirebasePhoneAuthPackage implements ReactPackage {
public FirebasePhoneAuthPackage(){
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Arrays.<NativeModule>asList(new FirebasePhoneAuthModule(reactContext));
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
[iOS] FirebasePhoneAuth.m
#import "FirebasePhoneAuth.h"
#import <FirebaseAuth/FirebaseAuth.h>
@implementation FirebasePhoneAuth
RCT_EXPORT_MODULE();
// PS: These events are for Android only. Its here just for convinience, to avoid crash on JS.
NSString *ON_VERIFICATION_COMPLETED = @"onFirebasePhoneVerificationCompleted";
NSString *ON_CODE_AUTO_RETRIEVAL_TIMEOUT = @"onFirebasePhoneCodeAutoRetrievalTimeOut";
- (NSDictionary *)constantsToExport
{
return @{
@"ON_VERIFICATION_COMPLETED": ON_VERIFICATION_COMPLETED,
@"ON_CODE_AUTO_RETRIEVAL_TIMEOUT": ON_CODE_AUTO_RETRIEVAL_TIMEOUT,
};
};
- (NSArray<NSString *> *)supportedEvents
{
return @[ON_VERIFICATION_COMPLETED, ON_CODE_AUTO_RETRIEVAL_TIMEOUT];
}
RCT_EXPORT_METHOD(verifyPhoneNumber:(NSString *)userInput
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
[[FIRPhoneAuthProvider provider]
verifyPhoneNumber:userInput
completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
if (error) {
reject(@"firebase_phone_verification_failed", [NSString stringWithFormat:@"Firebase Phone Verification Failed: %@", error.localizedDescription], error);
return;
}
resolve(verificationID);
}];
}
@end
[iOS] FirebasePhoneAuth.h
#ifndef FirebasePhoneAuth_h
#define FirebasePhoneAuth_h
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
@interface FirebasePhoneAuth : RCTEventEmitter <RCTBridgeModule>
@end
#endif
Javascript
import {
//...
NativeEventEmitter,
NativeModules,
//...
} from 'react-native'
const EventEmitter = new NativeEventEmitter(NativeModules.FirebasePhoneAuth)
const { FirebasePhoneAuth } = NativeModules
// ...
componentDidMount () {
const { phoneNumber } = this.props
EventEmitter.addListener(FirebasePhoneAuth.ON_VERIFICATION_COMPLETED, verificationCode => {
console.log('[PHONE_AUTH] Verification completed.', verificationCode)
this.setState({ isSending: false, verificationCode }, this.submit)
})
EventEmitter.addListener(FirebasePhoneAuth.ON_CODE_AUTO_RETRIEVAL_TIMEOUT, verificationId => {
console.log('[PHONE_AUTH] Auto verification timeout.', verificationId)
if (verificationId) this.setState({ verificationId })
})
console.log('[PHONE_AUTH] Sending verification code via sms...')
this.setState({ isSending: true })
FirebasePhoneAuth.verifyPhoneNumber(phoneNumber).then(verificationId => {
console.log('[PHONE_AUTH] Verification code sent.', verificationId)
this.setState({ isSending: false, verificationId })
}).catch(error => {
console.log('[PHONE_AUTH] Verification code failed.')
console.error(error)
this.setState({ isSending: false })
this.showError(
`Could not send confirmation code. Please try again.\n\nError: ${error}`,
true
)
})
}
componentWillUnmount () {
if (EventEmitter.removeAllListeners) EventEmitter.removeAllListeners(FirebasePhoneAuth.ON_VERIFICATION_COMPLETED)
if (EventEmitter.removeAllListeners) EventEmitter.removeAllListeners(FirebasePhoneAuth.ON_CODE_AUTO_RETRIEVAL_TIMEOUT)
}
@chrisbianca cool! Can you post the doc diff as well so I can understand what the resulting API will be? If I'm not mistaken, it's something like this:
const confirmationResultInstance = await signInWithPhoneNumber("+14156566666")
// show "confirm phone number" input
confirmationResultInstance.confirm("123456")
If the above is true, I think there are some UX and control flow issues with that approach, namely:
signInWith...
method that the return type is not Promise<User>
. That being said - this is your show, so I'm happy to follow whatever API y'all decide on. If it's not too much trouble, please post here when you stabilize an API so I can update the branch I'm working from to match your plans. Thanks for keeping this rolling!
_Edit for clarity:_
I've held off on actively developing the android side since the updates on this thread, in case that's not clear - please let me know if it would be helpful for me to finish the android implementation on that branch and PR, my assumption is no.
@jesseditson I've slightly changed the code Chris has pushed up, it's something like:
Believe this way keeps it consistent with the others where the final promise resolves the new user. Also supports auto verify, code sent may or may not get called, but the end result should still resolve the final promise.
Thoughts?
EDIT: not going with this approach anymore. See suggested implementation above from Chris.
@Salakar oh that's clean! Looks consistent and clear to me. Let me know if there's anything I can do to help!
@jesseditson could do with some v3 testers in the next day or 2, includes this as part of it ^^
Updated previous comment with a screenshot of code I had lying around, much better than my shoddy phone typed code ๐
But ye, it's 1am here, so I'm off to sleep. Am going to push to get an alpha out to npm tomorrow, some things may be broken as it's an alpha - wrote so much code for v3 ๐ฉ
Amazing. I'll integrate v3 when I get a chance (this weekend probably!) and test on iOS and Android - I'll be integrating some other Firebase services via this lib too, so while I won't be super helpful for regression testing, I will be able to tell you if things are working as expected!
EDIT: not going with this approach anymore. See suggested implementation above from Chris.
I believe android is working ๐ค though half way through testing I got blocked from google for doing too many auth requests:
Have waited almost 2 hours and am still blocked, so I guess that means sleep time ;p
There was a weird issue where SMS codes were immediately expired when trying to confirm - everything RNFirebase was correct, verificationId + sms code valid - firebase android sdk would error with com.google.firebase.auth.FirebaseAuthInvalidCredentialsException: The sms code has expired. Please re-send the verification code to try again.
- even if using the newly sent SMS code within 5 or so secs - guess this is a bug in their SDK ๐
My code from testing if you'd like to see how the flow looks / test it out from the WIP v3 branch:
/**
import React, { Component } from 'react';
import { View, Button, Text, TextInput, Image } from 'react-native';
import firebase from 'react-native-firebase';
const successImageUri = 'https://cdn.pixabay.com/photo/2015/06/09/16/12/icon-803718_1280.png';
export default class PhoneAuthTest extends Component {
constructor(props) {
super(props);
this.state = {
user: null,
message: '',
codeInput: '',
phoneNumber: '+44',
confirmResult: null,
};
}
signIn = () => {
const { phoneNumber } = this.state;
this.setState({ message: 'Sending code ...' });
firebase.auth()
.signInWithPhoneNumber(phoneNumber)
.onCodeSent(confirmResult => this.setState({ confirmResult, message: 'Code has been sent!' }))
.then(user => this.setState({ user: user.toJSON() }))
.catch(console.error);
};
confirmCode = () => {
const { codeInput, confirmResult } = this.state;
if (confirmResult && codeInput.length) {
confirmResult.confirm(codeInput)
.then(() => this.setState({ message: 'Code Confirmed!' }))
.catch(error => this.setState({ message: `Code Confirm Error: ${error.message}` }));
}
};
render() {
const { message, user, codeInput, confirmResult, phoneNumber } = this.state;
return (
<View style={{ flex: 1 }}>
{!user && !confirmResult ? (
<View style={{ padding: 25 }}>
<Text>Enter phone number:</Text>
<TextInput
autoFocus
style={{ height: 40, marginTop: 15, marginBottom: 15 }}
onChangeText={value => this.setState({ phoneNumber: value })}
placeholder={'Phone number ... '}
value={phoneNumber}
/>
<Button title="Sign In" color="green" onPress={this.signIn} />
</View>
) : null}
{message.length ? (
<Text style={{ padding: 5, backgroundColor: '#000', color: '#fff' }}>{message}</Text>) : null}
{!user && confirmResult ? (
<View style={{ marginTop: 25, padding: 25 }}>
<Text>Enter verification code below:</Text>
<TextInput
autoFocus
style={{ height: 40, marginTop: 15, marginBottom: 15 }}
onChangeText={value => this.setState({ codeInput: value })}
placeholder={'Code ... '}
value={codeInput}
/>
<Button title="Confirm Code" color="#841584" onPress={this.confirmCode} />
</View>
) : null}
{ user ? (
<View
style={{
padding: 15,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#77dd77',
flex: 1,
}}
>
<Image source={{ uri: successImageUri }} style={{ width: 100, height: 100, marginBottom: 25 }} />
<Text style={{ fontSize: 25 }}>Signed In!</Text>
<Text>{JSON.stringify(user)}</Text>
</View>
) : null}
</View>
);
}
}
*/
Hopefully won't be blocked by the time I wake up, can update ios to use the new EE logic - ios doesn't work at all currently.
If anyone does give it a go: https://firebase.google.com/docs/auth/android/phone-auth (especially step 4 from before you begin - otherwise all requests will fail)
Commit: https://github.com/invertase/react-native-firebase/commit/fd474d5adbb1928a2338407fd3b53cf74f42a2da
v3 change log: https://github.com/invertase/react-native-firebase/releases/tag/v3.0.0~~
@Salakar looks good. Will jump in and give it a test Monday!
(Also - you can bump the number of authorisation attempts per device in the Firebase console. Itโs hidden in the Authentication settings!)
@soulglo Aye I set it to 100 p/h earlier, also deleted the phone auth user off the console, it didn't reset my currently blocked status after all that - doh, I'll just go through a proxy later if i'm still blocked.
i fallow the installation instruction http://invertase.io/react-native-firebase/#/installation-android and i am trying to use auth function and get the error that
anyone know why i get it undefined ?
I would love to help (hey, it's Sunday ๐ ) since I do not have strong Obj-C/Java experience, how can I help test this? Is the implementation under 3.0? A few bullets would do
Hi All,
I can confirm I am experiencing the same issue.
I had some errors setting up the reCaptcha and I was using my main mobile number to sign in with during tests. turns out that I made a few too many requests and I've also been blocked with the same error message.
When it says 'device', I believe the error is referring to the phone number used to sign up. When trying to register using a different mobile telephone number (even on the same physical device), it still works. I Think the blocking of signup occurs somewhere in the backend of firebase which as far as I know, we don't seem to have any controls to change this.
Only thing I can suggest is to delete and recreate the project - although, that just seems like a lot of hassle - especially if you're in production
@manrashid
Did you or anyone else encounter this issue? Just asking.
https://stackoverflow.com/questions/45111165/app-store-rejection-due-to-firebase-phone-auth
Anyways, bravo for making this works guys!
I just wanted to note that our iOS app cleared review with no problems with phone auth implemented using the fork from @BuffMcBigHuge
@wbattel4607
I want to note that a rejection can currently only happen when phone auth is the only way to sign in, and that a sign in is required to access a substantial "advertised" part of the app. Since a disabled background refresh breaks phone auth in the Firebase native SDK. That, however, is getting addressed by the firebase team already.
@D1no ah, I see. Well that makes sense in our case because we also use Facebook auth.
@Salakar I've tried to jump in today and give this a test... however I'm getting the following error:
Any ideas what I might be doing wrong? Is there anything written about migrating to V3 that I've missed somewhere?
@soulglo ios db was still not fully functional in v3 - it is now so pull again from the branch.
Hold fire on testing phone auth for now, I think we're probably going to rollback to a web sdk only implementation.
If you'd like to help test v3 in general then I'd advise you only try test if you have some familiarity with native modules etc as no migration guides have been written yet - see the change logs though on the pre-release.
@Salakar Thanks Michael. Will jump in and give V3 a test in the next week or so.
Hi everybody, thanks for your patience on this. It's been tricky trying to adhere to our 'stick to the web SDK where possible' approach whilst supporting the differences between Android and iOS, but we think we've got a workable cross platform solution for you to try. Once we have your feedback, we can re-address any concerns. If you're interested in the code it's here: https://github.com/invertase/react-native-firebase/commit/81b631f111e1e94b6f4a8108b6fbc2d83c5d2f0f
This will be released as part of v3 very soon, but if you're feeling brave and want to give it a try in the meantime then you can install it in your project by running: npm install --save react-native-firebase@next
- there is a WIP migration guide here: https://github.com/invertase/react-native-firebase/releases/tag/v3.0.0
I've attached a complete example of how to work with phone auth below, but just to explain how we've approached it:
1) Trigger phone auth - as per the Web SDK (without the need for captcha):
firebase.auth().signInWithPhoneNumber(phoneNumber)
.then(confirmResult => // save confirm result to use with the manual verification code)
.catch(error => /error);
2) Confirm verification code - as per the Web SDK
confirmResult.confirm(verificationCode)
.then(user => // User is logged in){
.catch(error => // Error with verification code);
The above is all you'll need to support the manual verification flow in iOS, however, in order to support Android fully, including auto verification, you will also need to listen to the onAuthStateChanged
event (as is the recommended way to listen to user state changes in Firebase). This will get triggered if Google Play Services is able to authenticate the user automatically, either without sending an SMS or by processing the inbound SMS automatically.
3) Auto verification
firebase.auth().onAuthStateChanged((user) => {
if (user) // user is verified and logged in
});
I hope the above all makes sense, the full example below will hopefully bring everything together:
import React, { Component } from 'react';
import { View, Button, Text, TextInput, Image } from 'react-native';
import firebase from 'react-native-firebase';
const successImageUri = 'https://cdn.pixabay.com/photo/2015/06/09/16/12/icon-803718_1280.png';
export default class PhoneAuthTest extends Component {
constructor(props) {
super(props);
this.unsubscribe = null;
this.state = {
user: null,
message: '',
codeInput: '',
phoneNumber: '+44',
confirmResult: null,
};
}
componentDidMount() {
this.unsubscribe = firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.setState({ user: user.toJSON() });
} else {
// User has been signed out, reset the state
this.setState({
user: null,
message: '',
codeInput: '',
phoneNumber: '+44',
confirmResult: null,
});
}
});
}
componentWillUnmount() {
if (this.unsubscribe) this.unsubscribe();
}
signIn = () => {
const { phoneNumber } = this.state;
this.setState({ message: 'Sending code ...' });
firebase.auth().signInWithPhoneNumber(phoneNumber)
.then(confirmResult => this.setState({ confirmResult, message: 'Code has been sent!' }))
.catch(error => this.setState({ message: `Sign In With Phone Number Error: ${error.message}` }));
};
confirmCode = () => {
const { codeInput, confirmResult } = this.state;
if (confirmResult && codeInput.length) {
confirmResult.confirm(codeInput)
.then((user) => {
this.setState({ message: 'Code Confirmed!' });
})
.catch(error => this.setState({ message: `Code Confirm Error: ${error.message}` }));
}
};
signOut = () => {
firebase.auth().signOut();
}
render() {
const { message, user, codeInput, confirmResult, phoneNumber } = this.state;
return (
<View style={{ flex: 1 }}>
{!user && !confirmResult ? (
<View style={{ padding: 25 }}>
<Text>Enter phone number:</Text>
<TextInput
autoFocus
style={{ height: 40, marginTop: 15, marginBottom: 15 }}
onChangeText={value => this.setState({ phoneNumber: value })}
placeholder={'Phone number ... '}
value={phoneNumber}
/>
<Button title="Sign In" color="green" onPress={this.signIn} />
</View>
) : null}
{message.length ? (
<Text style={{ padding: 5, backgroundColor: '#000', color: '#fff' }}>{message}</Text>) : null}
{!user && confirmResult ? (
<View style={{ marginTop: 25, padding: 25 }}>
<Text>Enter verification code below:</Text>
<TextInput
autoFocus
style={{ height: 40, marginTop: 15, marginBottom: 15 }}
onChangeText={value => this.setState({ codeInput: value })}
placeholder={'Code ... '}
value={codeInput}
/>
<Button title="Confirm Code" color="#841584" onPress={this.confirmCode} />
</View>
) : null}
{ user ? (
<View
style={{
padding: 15,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#77dd77',
flex: 1,
}}
>
<Image source={{ uri: successImageUri }} style={{ width: 100, height: 100, marginBottom: 25 }} />
<Text style={{ fontSize: 25 }}>Signed In!</Text>
<Text>{JSON.stringify(user)}</Text>
<Button title="Sign Out" color="red" onPress={this.signOut} />
</View>
) : null}
</View>
);
}
}
@apple9220 This sounds like you're using the Firebase JS SDK rather than react-native-firebase
as that requires 2 arguments, not the one as we require. Can you confirm how you're importing Firebase?
hi, chrisbianca.
can you tell me your versions of 'react-native-firebase' and 'react-native'?
thank you.
The versions used in the example above were:
React Native: 0.47.1
React Native Firebase: 3.0.0-alpha.1
I am using version 2.1.3 react-native-firebase.
when did version 3.0.0 release?
I am using version 2.1.3 react-native-firebase.
when did version 3.0.0 release?
Please read the issue, it's not out yet.
If then, how can I use the code above now?
As explained above...
This will be released as part of v3 very soon, but if you're feeling brave and want to give it a try in the meantime then you can install it in your project by running: npm install --save react-native-firebase@next - there is a WIP migration guide here: https://github.com/invertase/react-native-firebase/releases/tag/v3.0.0
If you are not confident and comfortable with alpha code (it may contain bugs) then I would suggest you refrain from trying until v3 is released.
Hi, chrisbianca.
When does v3 release?
We don't have a specific date in mind - we are waiting on feedback on v3 to ensure there are no issues before releasing.
@chrisbianca loving this, have been testing it for a few days now and everything is working as expected!
One quick query though, is it possible to say have a user signup with Facebook and then at a later stage have the user link their phone number through this process?
[ANDROID] Did auto verification worked for you guys?
@GeorgeA93 Firebase supports auth provider account linking (i.e., phone number + Facebook) but, to my knowledge, this is not yet implemented in this library. Someone please correct me if I'm wrong- we'd love to do the same thing with our app.
@GeorgeA93 Glad things are going well!! In terms of linking an account, RNFirebase does have link credential functionality, however the Firebase docs are a bit low on information about how this is meant to work with phone auth...
@brunolemos When you say does it work, do you mean:
1) How do you get it to auto-verify? - I had to install the latest version of google play services on my phone to get it to work.
2) How do you detect an auto-verification? You need to use the onAuthStateChanged
listener.
@chrisbianca on my tests onVerificationCompleted
was never called... I'm using this code instead of your branch but I don't see why that wouldn't work. Maybe I missed some config step on AndroidManifest or other thing (local notifications, ...). Any clue?
@GeorgeA93 I have the same requirements as you, except with anonymous users, then link to a phone authenticated user.
@chrisbianca I had success adding two small lines to V2 code in order to link my iOS users anonymous accounts to phone authenticated accounts. Below are the changes I made successfully.
ios/RNFirebase/auth/RNFirebaseAuth.m
~ Line 479, in getCredentialForProvider(), after Github} else if ([provider compare:@"phone" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
credential = [[FIRPhoneAuthProvider provider] credentialWithVerificationID:authToken verificationCode:authTokenSecret];
index.d.ts
~ Line 455, after reauthenticate()/**
* Link the user with a 3rd party credential provider.
*/
linkWithCredential(credential: Credential): Promise<User>
I tried to test V3, but wasn't able to get @chrisbianca latest code sample working, or V3 running without errors. So I can't test my "hacks" on V3 yet, unsure how'd they integrate into Android as well, especially with auto verification.
Happy to help test and get this working, I'm sure like @GeorgeA93 and I, there are quite a few use cases out there that will need to leverage linkWithCredential() in their workflow.
I got something working last night, happy to put together a PR and get some opinions. Pretty much did the same thing as @brianhiss but in V3. Again unsure on Android.
@GeorgeA93 I was able to install V3 today with no issues, not sure why I was having issues yesterday.
Regardless, I made the same "hacks" noted above for v3, and my old code is working for sign-in and linkWithCredential.
Testing on Android now, haven't come across the SDK auto-verifying my phone, so should work as expected.
Would like a more permanent solution though. Hoping @chrisbianca can adopt one of our solutions and integrate into this great library.
Let me know how I can help either of you.
@brunolemos Sounds like you don't have the latest version of Google Play Services installed on your device (not as part of your build) - I had to opt in to the beta to trick it into downloading a later version. I believe you can install it manually too, though I'm not sure it's something I'd advise!
@brianhiss The best thing to do would be to create a PR with your changes. I'm actually about to fly out on my honeymoon so will be away for a couple of weeks, but I'm sure that @Salakar or @Ehesp will be able to review the PR and get something sorted.
This is why we wanted people trying out Phone auth before we used v3 to check that we'd covered all the use cases. Thanks to everybody that has tested so far!!
@chrisbianca Congratulations! Have a great trip.
@GeorgeA93 I did just complete my Android test and got everything working with my old code. For Android, I just added the one line below, much like I did for iOS in RNFirebaseAuth.m
.
case "phone":
return PhoneAuthProvider.getCredential(authToken, authSecret);
To run my three "hacks", I just call the function from @chrisbianca code above:
firebase.auth().signInWithPhoneNumber(phoneNumber)
.then(confirmResult => this.setState({ confirmResult, message: 'Code has been sent!' }))
.catch(error => this.setState({ message: `Sign In With Phone Number Error: ${error.message}` }));
Instead of then calling confirmResult.confirm(codeInput)
, I split my logic depending if I have a logged-in/anonymous user, or if no one is logged in.
If I have a logged-in/anonymous user, I will call firebase.auth().currentUser.linkWithCredential(credential);
If no one is logged in, firebase.auth().signInWithCredential(credential);
As for the credential
that I pass in, it's just an object:
let credential = {
provider: 'phone',
token: this.state.confirmResult._verificationId,
secret: codeInput
};
I will try to submit a PR for others, but not sure my way is the best way to implement this for Android. I'll let others decide if it works for them too, but happy to help anyone with my approach.
For android if it goes to the auto verification flow (firebase.auth().onAuthStateChanged()), the user is automatically signed in after authentication. In this case It seems it's impossible to link the Phone number credential with a current account. Any suggestion on this? Can we have a api similar to Android which returns a credential even in auto verify flow?
@brianhiss. Thanks everyone for putting in the work on this.
I'm trying to implement, but just get the error:
{
error_name: 'ERROR_MISSING_APP_TOKEN',
NSLocalizedDescription: 'There seems to be a problem with your project\'s Firebase phone number authentication set-up, please make sure to follow the instructions found at https://firebase.google.com/docs/auth/ios/phone-auth' },
domain: 'FIRAuthErrorDomain'
}
when I try signInWithPhoneNumber
. Can anyone point me in the right direction to get this working?
Any idea when this might make it into production?
For what it's worth, we recently got Firebase phone authentication working in a react-native
project without needing to depend on the functionality being built into react-native-firebase
.
FirebaseUI provides drop-in phone authentication interfaces for iOS and Android and is pretty easy to wrap to spawn it with a native-module call. It handles sending and confirming the SMS token. We then retrieve the authentication token to generate a custom token and eventually sign in to react-native-firebase
with signInWithCustomToken
, but I imagine if FirebaseUI
and react-native-firebase
share the same Firebase instance it would detect the authentication state change without any further integration.
Obviously FirebaseUI is not an option if your app needs a custom phone auth UI, but it's a good stop-gap until there's more robust support for the underlying APIs in react-native-firebase
. I imagine the phone auth code in FirebaseUI might be useful to review for implementers here as well.
@javamonn that sounds awesome. Can you provide some more info or maybe sample code on your integration?
thanks
Hello! Could someone help me please i have an error: "Remote notification and background fetching need to be set up for the app. If app delegate swizzling is disabled, the APNs device token received by UIApplicationDelegate needs to be forwarded to FIRAuth's APNSToken property.". Do i need to set up firebase cloud messaging for auth with phone for firebase (do i need to make swizzling disabled or not?)? It is pointed that cloud messaging is optional in installation docs. Thanks
also could you tell me please what is "auto-verify" in this context?
Yes, you need to activate Firebase Cloud Messaging!
Am 14.09.2017 um 10:24 schrieb Kirill notifications@github.com:
Hello! Could someone help me please i have an error: "Remote notification and background fetching need to be set up for the app. If app delegate swizzling is disabled, the APNs device token received by UIApplicationDelegate needs to be forwarded to FIRAuth's APNSToken property.". Do i need to set up firebase cloud messaging for auth with phone for firebase? It is pointed that cloud messaging is optional in installation docs. Thanks
also could you tell me please what is "auto-verify" in this context?โ
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
@eneskaya Thanks for fast answer, also could you tell me please is it possible to test it and debug this auth without generating APNs Auth Key (i do not have payed dev account now. So the question is can i test this method of auth without payed account? (how?)
You need to have FCM enabled and correctly set up according to Firebase documentation (https://firebase.google.com/docs/cloud-messaging/ios/client). It's a security measure, to prevent scam. So, unfortunately you need a paid developer account for iOS developers program to even be able to test it.
Am 14.09.2017 um 10:51 schrieb Kirill notifications@github.com:
@eneskaya Thanks for fast answer, also could you tell me please is it possible to test it and debug this auth without generating APNs Auth Key (i do not have payed dev account now. So the question is can i test this method of auth without payed account? (how?)
โ
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
@eneskaya Thank you very mush! You helped me a lot. I kindly suggest firebase developers to add some information about phone auth in the doc. I know that it is only alpha, but anyway, just some words will be good.
Need to be patient with that ;) but glad I could help.
Am 14.09.2017 um 11:54 schrieb Kirill notifications@github.com:
@eneskaya Thank you very mush! You helped me a lot. I kindly suggest firebase developers to add some information about phone auth in the doc. I know that it is only alpha, but anyway, just some words will be good.
โ
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
@javamonn could you share the code you guys use to bridge and share firebaseUI? Offloading auth and all its checks sounds like viable idea.
Ok I'm trying to setup react-native-firebase@next to try Phone authentication and followed the instructions here https://invertase.io/react-native-firebase/#/installation-android for v3.x.x
Everything compiles, but when I start the app, before seeing anything I'm getting this error
C++ exception in 'NativeModules':
java.lang.NoClassDefFoundError: com.google.firebase.auth.AuthCredential
Any hints what can be happening?
Thanks, cheers
Miguel
I'm back in the country now, so should be able to pick back up on this over the next few days. I'll try and pick up on the questions above that haven't already been answered:
@subugwriter Thanks for raising the linking issue. Auto-verify on Android has been a right pain to deal with in a cross platform manner. What I'm going to try and do is the following:
1) Update the current signInWithPhoneNumber
to link the phone credential automatically if the user is already signed in.
2) Expose a new verifyPhoneNumber
endpoint to the API which will expose the underlying API more explicitly, e.g. onCodeSent
, onAutoVerified
, etc, so that the user can do what they want should they need anything more custom.
@javamonn Thanks for mentioning FirebaseUI. This is certainly an option, however by using FirebaseUI there are some limitations - namely, that it will import all the underlying Firebase modules regardless of whether they're being used in the rest of the app or not. Also, React Native makes it so easy to build UIs, that it shouldn't really be necessary. Hopefully when the above changes I've proposed are made, this will render FirebaseUI unnecessary. I'm also not sure how well the new multi app support would translate across between react-native-firebase
and FirebaseUI
@maraujop Sounds like you've not got the required Android libraries being imported correctly. I'd need the rest of the error message to be able to help any further than that.
@faahmad I'd suggest you debug your verifyCode
method and see whether you're correctly setting confirmResult
in your reducer.
@bintoll Documentation will be firmed up once we've agreed on the final implementation. We wanted to find out how everybody was using the library and highlight the edge cases, e.g. linking the credential, before we spent too much time there.
Thanks @chrisbianca for your answer.
It took me a while, but I finally found a cross dependency problem, another library was loading an older version of Firebase. It is now working good, as I've excluded dependencies from libraries and manually forced dependencies. This article was of great help.
Beware that I've had to do a little adjustment to your javascript example, to handle auto verification, when there is no need to request to the user for an SMS code.
Now, I'm trying to compile it correctly in iOS, but I'm facing this compile error:
Argument list too long: recursive header expansion failed at /Users/map/repos/paytween/node_modules/react-native-fcm/ios/../../../ios/Pods/React/node_modules/utf-8-validate/node_modules/nan/tools.
Maybe react-native-firebase is not compatible with react-native-fcm? I have been using that project for FCM, and I wouldn't like to change it all now, as I'm in a hurry to have this finished.
My guess is that this also has to do with inter dependecy problems, or maybe that react-native-fcm is not set up using Pods. Any hint would be greatly appreciated.
Thanks. By the way your project hierarchy and code quality is amazing, great work!
Cheers,
Miguel
@maraujop Not sure how compatible we are with react-native-fcm
nowadays as I've not tested recently. In the past we have been, though obviously in the long term it would make sense to port over to the react-native-firebase
which does offer messaging support and is something we will be focusing more attention on once phone auth is out of the way ;)
In terms of your error, I'd suggest clearing down your project, DerivedData, etc and restarting XCode. See if that helps...
All,
I've just pushed up the verifyPhoneNumber
implementation (as @chrisbianca mentioned above). Source code.
This implementation gives you full control of the phone number verification flow on either platform as well as a flexible api to suite any UI flow and should be familiar to use if you've used things like storage upload tasks
/ database on
listeners.
This code snippet covers all scenarios for both android and iOS, how you implement this is entirely up to you. A simpler iOS only usage example can be found below this example.
firebase.auth()
.verifyPhoneNumber('+441234567890')
.on('state_changed', (phoneAuthSnapshot) => {
// How you handle these state events is entirely up to your ui flow and whether
// you need to support both ios and android. In short: not all of them need to
// be handled - it's entirely up to you, your ui and supported platforms.
// E.g you could handle android specific events only here, and let the rest fall back
// to the optionalErrorCb or optionalCompleteCb functions
switch (phoneAuthSnapshot.state) {
// ------------------------
// IOS AND ANDROID EVENTS
// ------------------------
case firebase.auth.PhoneAuthState.CODE_SENT: // or 'sent'
console.log('code sent');
// on ios this is the final phone auth state event you'd receive
// so you'd then ask for user input of the code and build a credential from it
break;
case firebase.auth.PhoneAuthState.ERROR: // or 'error'
console.log('verification error');
console.log(phoneAuthSnapshot.error);
break;
// ---------------------
// ANDROID ONLY EVENTS
// ---------------------
case firebase.auth.PhoneAuthState.AUTO_VERIFY_TIMEOUT: // or 'timeout'
console.log('auto verify on android timed out');
// proceed with your manual code input flow, same as you would do in
// CODE_SENT if you were on IOS
break;
case firebase.auth.PhoneAuthState.AUTO_VERIFIED: // or 'verified'
// auto verified means the code has also been automatically confirmed as correct/received
// phoneAuthSnapshot.code will contain the auto verified sms code - no need to ask the user for input.
console.log('auto verified on android');
console.log(phoneAuthSnapshot);
// Example usage if handling here and not in optionalCompleteCb:
// const { verificationId, code } = phoneAuthSnapshot;
// const credential = firebase.auth.PhoneAuthProvider.credential(verificationId, code);
// Do something with your new credential, e.g.:
// firebase.auth().signInWithCredential(credential);
// firebase.auth().linkWithCredential(credential);
// etc ...
break;
}
}, (error) => {
// optionalErrorCb would be same logic as the ERROR case above, if you've already handed
// the ERROR case in the above observer then there's no need to handle it here
console.log(error);
// verificationId is attached to error if required
console.log(error.verificationId);
}, (phoneAuthSnapshot) => {
// optionalCompleteCb would be same logic as the AUTO_VERIFIED/CODE_SENT switch cases above
// depending on the platform. If you've already handled those cases in the observer then
// there's absolutely no need to handle it here.
// Platform specific logic:
// - if this is on IOS then phoneAuthSnapshot.code will always be null
// - if ANDROID auto verified the sms code then phoneAuthSnapshot.code will contain the verified sms code
// and there'd be no need to ask for user input of the code - proceed to credential creating logic
// - if ANDROID auto verify timed out then phoneAuthSnapshot.code would be null, just like ios, you'd
// continue with user input logic.
console.log(phoneAuthSnapshot);
});
// optionally also supports .then & .catch instead of optionalErrorCb &
// optionalCompleteCb (with the same resulting args)
The api is flexible enough to not force those of you that only need to support one platform (in this case iOS) into a wall of code.
Whilst this example makes use of the optional resulting promise, you can still however use the observer api instead and handle CODE_SENT
and ERROR
events individually like in the above detailed example - again it's entirely up to you.
firebase.auth()
.verifyPhoneNumber('+441234567890')
.then((phoneAuthSnapshot) => {
console.log(phoneAuthSnapshot);
// wait / get users code input then:
// const { verificationId } = phoneAuthSnapshot;
// const { codeInput } = this.state; // tied to your input box, for example
// const credential = firebase.auth.PhoneAuthProvider.credential(verificationId, codeInput);
// Do something with your new credential, e.g.:
// firebase.auth().signInWithCredential(credential);
// firebase.auth().linkWithCredential(credential);
})
.catch((error) => {
console.log(error);
console.log(error.verificationId);
});
As verifyPhoneNumber
covers full support for the firebase phone auth api I think we close to closing this issue ๐, just some further testing required by us internally and also any feedback you guys may have?
As for signInWithPhoneNumber
I think we'll leave this in it's current state (without auto linking) - even though verifyPhoneNumber
negates the need for it - for those of you who'd prefer to use it (unless you tell us otherwise?).
Thanks again all for helping us work through the implementation.
Thanks @chrisbianca I finally managed to get it working. It was a bad iOS + cocoapods setup. Although it's true that search paths in react-native-fcm are too open and can cause trouble. Anyway, I finally got it working in both platforms. In the future, I will probably move into using one single app.
@Salakar i was trying to change my code to use this new verifyPhoneNumber
flow. I believe that you forgot to push some native Android code.
Currently PhoneAuthListener
calls native verifyPhoneNumber
, which I haven't found anywhere.
So, it ends up in this error:
@maraujop I'm using fcm as well. Do you have any tips to share for getting it working?
thanks
@maraujop and others, I probably should of said this above but please wait until we give the go ahead before trying to use verifyPhoneNumber
:)
As you correctly said the native parts are not there yet as they require a few more tweaks / testing.
My post was a heads up of what the new API is :)
Thanks @Salakar , do you have an ETA?
@Salakar Oh, I totally misunderstood your post, sorry, as you said you had merged the implementation I understood it was fully there.
@jcharbo Make sure your LIBRARY_SEARCH_PATHS
looks like this:
LIBRARY_SEARCH_PATHS = (
"\"$(BUILD_DIR)/Release$(EFFECTIVE_PLATFORM_NAME)\"",
"$(inherited)",
);
Also make sure that you do pod update
in your ios
folder. If you have any problem, let me know what it is.
@maraujop this has been pushed up for android, you should be good to go now for android - will sort ios shortly.
My UI skills aren't amazing, but here's a gif of android working with auto verify included, it's pretty quick:
Hey all, this is now part of the full v3 release (with Cloud Firestore ๐ ) that we pushed out to npm yesterday. I'll be closing this issue now. I'm still working on the docs but for now please use this thread (https://github.com/invertase/react-native-firebase/issues/119#issuecomment-331716624 specifically) for implementation guides.
Please raise any new issues in separate github issues going forward. If you have queries / or want to continue discussing this further you can join us on discord - this thread takes far too long to load now ๐
Thanks
Hi everyone, I appreciate your work on this but does anyone have an estimate on when this will all be ready for production?
@jcharbo
awesome @Salakar , thanks!
Most helpful comment
All,
I've just pushed up the
verifyPhoneNumber
implementation (as @chrisbianca mentioned above). Source code.This implementation gives you full control of the phone number verification flow on either platform as well as a flexible api to suite any UI flow and should be familiar to use if you've used things like storage
upload tasks
/ databaseon
listeners.Cross Platform Example
This code snippet covers all scenarios for both android and iOS, how you implement this is entirely up to you. A simpler iOS only usage example can be found below this example.
Basic Example
The api is flexible enough to not force those of you that only need to support one platform (in this case iOS) into a wall of code.
Whilst this example makes use of the optional resulting promise, you can still however use the observer api instead and handle
CODE_SENT
andERROR
events individually like in the above detailed example - again it's entirely up to you.As
verifyPhoneNumber
covers full support for the firebase phone auth api I think we close to closing this issue ๐, just some further testing required by us internally and also any feedback you guys may have?As for
signInWithPhoneNumber
I think we'll leave this in it's current state (without auto linking) - even thoughverifyPhoneNumber
negates the need for it - for those of you who'd prefer to use it (unless you tell us otherwise?).Thanks again all for helping us work through the implementation.