[android][auth] phoneAuth via EE implementation
This commit is contained in:
parent
a27abfdf92
commit
fd474d5adb
|
@ -568,11 +568,12 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
|
|||
/**
|
||||
* signInWithPhoneNumber
|
||||
*
|
||||
* @param appName
|
||||
* @param phoneNumber
|
||||
* @param promise
|
||||
* @param phoneAuthRequestKey
|
||||
*/
|
||||
@ReactMethod
|
||||
public void signInWithPhoneNumber(String appName, final String phoneNumber, final Promise promise) {
|
||||
public void signInWithPhoneNumber(final String appName, final String phoneNumber, final String phoneAuthRequestKey) {
|
||||
Log.d(TAG, "signInWithPhoneNumber");
|
||||
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
|
||||
final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
|
||||
|
@ -580,48 +581,64 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
|
|||
// Reset the verification Id
|
||||
mVerificationId = null;
|
||||
|
||||
PhoneAuthProvider.getInstance(firebaseAuth).verifyPhoneNumber(phoneNumber, 60, TimeUnit.SECONDS,
|
||||
getCurrentActivity(), new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
|
||||
PhoneAuthProvider.getInstance(firebaseAuth).verifyPhoneNumber(phoneNumber, 120, TimeUnit.SECONDS,
|
||||
mReactContext.getCurrentActivity(), new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
|
||||
@Override
|
||||
public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
|
||||
public void onVerificationCompleted(final PhoneAuthCredential phoneAuthCredential) {
|
||||
// User has been automatically verified, log them in
|
||||
firebaseAuth.signInWithCredential(phoneAuthCredential)
|
||||
.addOnSuccessListener(new OnSuccessListener<AuthResult>() {
|
||||
@Override
|
||||
public void onSuccess(AuthResult authResult) {
|
||||
// onAuthStateChanged will pick up the user change
|
||||
Log.d(TAG, "signInWithPhoneNumber:autoVerified:success");
|
||||
WritableMap event = Arguments.createMap();
|
||||
WritableMap user = firebaseUserToMap(authResult.getUser());
|
||||
event.putMap("user", user);
|
||||
event.putString("type", "user");
|
||||
event.putString("appName", appName);
|
||||
event.putString("appName", phoneAuthCredential.getSmsCode());
|
||||
event.putString("phoneAuthRequestKey", phoneAuthRequestKey);
|
||||
Utils.sendEvent(mReactContext, "phone_auth_event", event);
|
||||
}
|
||||
})
|
||||
.addOnFailureListener(new OnFailureListener() {
|
||||
@Override
|
||||
public void onFailure(@NonNull Exception exception) {
|
||||
Log.e(TAG, "signInWithPhoneNumber:autoVerified:failure", exception);
|
||||
// TODO: Will this ever error? How do we get it back to the JS side?
|
||||
// promiseRejectAuthException(promise, exception);
|
||||
WritableMap event = Arguments.createMap();
|
||||
WritableMap error = getJSError(exception);
|
||||
event.putMap("error", error);
|
||||
event.putString("type", "error");
|
||||
event.putString("appName", appName);
|
||||
event.putString("phoneAuthRequestKey", phoneAuthRequestKey);
|
||||
Utils.sendEvent(mReactContext, "phone_auth_event", event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVerificationFailed(FirebaseException e) {
|
||||
// TODO: Will this ever get sent after we've received an onCodeSent?
|
||||
// If so, then this will cause an exception as the promise has already been used
|
||||
promiseRejectAuthException(promise, e);
|
||||
WritableMap event = Arguments.createMap();
|
||||
WritableMap error = getJSError(e);
|
||||
event.putMap("error", error);
|
||||
event.putString("type", "error");
|
||||
event.putString("appName", appName);
|
||||
event.putString("phoneAuthRequestKey", phoneAuthRequestKey);
|
||||
Utils.sendEvent(mReactContext, "phone_auth_event", event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCodeSent(String verificationId, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
|
||||
// TODO: This isn't being saved anywhere if the activity gets restarted when going to the SMS app
|
||||
mVerificationId = verificationId;
|
||||
WritableMap verificationMap = Arguments.createMap();
|
||||
verificationMap.putString("appName", appName);
|
||||
verificationMap.putString("type", "confirm");
|
||||
verificationMap.putString("verificationId", verificationId);
|
||||
|
||||
promise.resolve(verificationMap);
|
||||
verificationMap.putString("phoneAuthRequestKey", phoneAuthRequestKey);
|
||||
Utils.sendEvent(mReactContext, "phone_auth_event", verificationMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCodeAutoRetrievalTimeOut (String verificationId) {
|
||||
public void onCodeAutoRetrievalTimeOut(String verificationId) {
|
||||
super.onCodeAutoRetrievalTimeOut(verificationId);
|
||||
// Purposefully not doing anything with this at the moment
|
||||
}
|
||||
|
@ -629,11 +646,11 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
|
|||
}
|
||||
|
||||
@ReactMethod
|
||||
public void _confirmVerificationCode(String appName, final String verificationCode, final Promise promise) {
|
||||
public void _confirmVerificationCode(final String appName, final String phoneAuthRequestKey, String verificationId, String verificationCode, final Promise promise) {
|
||||
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
|
||||
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
|
||||
|
||||
PhoneAuthCredential credential = PhoneAuthProvider.getCredential(mVerificationId, verificationCode);
|
||||
PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, verificationCode);
|
||||
|
||||
firebaseAuth.signInWithCredential(credential)
|
||||
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
|
||||
|
@ -641,10 +658,24 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
|
|||
public void onComplete(@NonNull Task<AuthResult> task) {
|
||||
if (task.isSuccessful()) {
|
||||
Log.d(TAG, "signInWithCredential:onComplete:success");
|
||||
WritableMap event = Arguments.createMap();
|
||||
WritableMap user = firebaseUserToMap(task.getResult().getUser());
|
||||
event.putMap("user", user);
|
||||
event.putString("type", "user");
|
||||
event.putString("appName", appName);
|
||||
event.putString("phoneAuthRequestKey", phoneAuthRequestKey);
|
||||
Utils.sendEvent(mReactContext, "phone_auth_event", event);
|
||||
promiseWithUser(task.getResult().getUser(), promise);
|
||||
} else {
|
||||
Exception exception = task.getException();
|
||||
Log.e(TAG, "signInWithCredential:onComplete:failure", exception);
|
||||
WritableMap event = Arguments.createMap();
|
||||
WritableMap error = getJSError(exception);
|
||||
event.putMap("error", error);
|
||||
event.putString("type", "error");
|
||||
event.putString("appName", appName);
|
||||
event.putString("phoneAuthRequestKey", phoneAuthRequestKey);
|
||||
Utils.sendEvent(mReactContext, "phone_auth_event", event);
|
||||
promiseRejectAuthException(promise, exception);
|
||||
}
|
||||
}
|
||||
|
@ -1017,6 +1048,17 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
|
|||
* @param exception
|
||||
*/
|
||||
private void promiseRejectAuthException(Promise promise, Exception exception) {
|
||||
WritableMap error = getJSError(exception);
|
||||
promise.reject(error.getString("code"), error.getString("message"), exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* getJSError
|
||||
*
|
||||
* @param exception
|
||||
*/
|
||||
private WritableMap getJSError(Exception exception) {
|
||||
WritableMap error = Arguments.createMap();
|
||||
String code = "UNKNOWN";
|
||||
String message = exception.getMessage();
|
||||
String invalidEmail = "The email address is badly formatted.";
|
||||
|
@ -1024,6 +1066,7 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
|
|||
try {
|
||||
FirebaseAuthException authException = (FirebaseAuthException) exception;
|
||||
code = authException.getErrorCode();
|
||||
error.putString("nativeErrorCode", code);
|
||||
message = authException.getMessage();
|
||||
} catch (Exception e) {
|
||||
Matcher matcher = Pattern.compile("\\[(.*):.*\\]").matcher(message);
|
||||
|
@ -1088,7 +1131,10 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule {
|
|||
}
|
||||
|
||||
code = "auth/" + code.toLowerCase().replace("error_", "").replace('_', '-');
|
||||
promise.reject(code, message, exception);
|
||||
error.putString("code", code);
|
||||
error.putString("message", message);
|
||||
error.putString("nativeErrorMessage", exception.getMessage());
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -544,7 +544,7 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
|||
* @return
|
||||
*/
|
||||
private static String getCodeWithService(String service, String code) {
|
||||
return service.toUpperCase() + "/" + code.toUpperCase();
|
||||
return service.toLowerCase() + "/" + code.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -56,6 +56,13 @@ export default {
|
|||
ERROR_INIT_OBJECT: 'Firebase.initializeApp(options <-- requires a valid configuration object.',
|
||||
ERROR_INIT_STRING_NAME: 'Firebase.initializeApp(options, name <-- requires a valid string value.',
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
ERROR_MISSING_CB(method) {
|
||||
return `Missing required callback for method ${method}().`;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
* @param namespace
|
||||
|
|
|
@ -4,19 +4,31 @@
|
|||
export default class ConfirmationResult {
|
||||
_auth: Object;
|
||||
_verificationId: string;
|
||||
_phoneAuthRequestKey: string;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param auth
|
||||
* @param verificationId The phone number authentication operation's verification ID.
|
||||
* @param phoneAuthRequestKey
|
||||
*/
|
||||
constructor(auth: Object, verificationId: string) {
|
||||
constructor(auth: Object, verificationId: string, phoneAuthRequestKey: string) {
|
||||
this._auth = auth;
|
||||
this._verificationId = verificationId;
|
||||
this._phoneAuthRequestKey = phoneAuthRequestKey;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param verificationCode
|
||||
* @return {*}
|
||||
*/
|
||||
confirm(verificationCode: string): Promise<Object> {
|
||||
// verificationId is stored server side in case the app is shut when opening the SMS app
|
||||
return this._auth._native.confirmVerificationCode(verificationCode);
|
||||
return this._auth._native._confirmVerificationCode(
|
||||
this._phoneAuthRequestKey,
|
||||
this._verificationId,
|
||||
verificationCode,
|
||||
);
|
||||
}
|
||||
|
||||
get verificationId(): String | null {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// @flow
|
||||
import User from './user';
|
||||
import INTERNALS from './../../internals';
|
||||
import ModuleBase from './../../utils/ModuleBase';
|
||||
import { nativeToJSError, generatePushID, isFunction } from './../../utils';
|
||||
import ConfirmationResult from './ConfirmationResult';
|
||||
|
||||
// providers
|
||||
|
@ -31,9 +33,48 @@ export default class Auth extends ModuleBase {
|
|||
this._onAuthStateChanged.bind(this),
|
||||
);
|
||||
|
||||
this.addListener(
|
||||
// sub to phone auth native event - this can
|
||||
// be either a code sent confirmation result
|
||||
// event, a successful signIn event or an
|
||||
// error event
|
||||
this._getAppEventName('phone_auth_event'),
|
||||
this._onPhoneAuthEvent.bind(this),
|
||||
);
|
||||
|
||||
this._native.addAuthStateListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an incoming phone auth event and emits to the internal once listeners.
|
||||
* @param event
|
||||
* @private
|
||||
*/
|
||||
_onPhoneAuthEvent(event) {
|
||||
const { type, phoneAuthRequestKey } = event;
|
||||
|
||||
if (type === 'confirm') {
|
||||
return this.emit(
|
||||
`phone:auth:${phoneAuthRequestKey}:confirm`,
|
||||
new ConfirmationResult(this, event.verificationId, phoneAuthRequestKey),
|
||||
);
|
||||
}
|
||||
|
||||
if (type === 'user') {
|
||||
return this.emit(
|
||||
`phone:auth:${phoneAuthRequestKey}:user`,
|
||||
this._onAuthStateChanged({ authenticated: true, user: event.user }, false),
|
||||
);
|
||||
}
|
||||
|
||||
if (type === 'error') {
|
||||
const { code, message } = event.error;
|
||||
return this.emit(`phone:auth:${phoneAuthRequestKey}:error`, nativeToJSError(code, message));
|
||||
}
|
||||
|
||||
throw new Error('Internal RNFirebase Error: Invalid phone auth event received.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal auth changed listener
|
||||
* @param auth
|
||||
|
@ -141,18 +182,58 @@ export default class Auth extends ModuleBase {
|
|||
* @return {Promise} A promise resolved upon completion
|
||||
*/
|
||||
signInWithCredential(credential: CredentialType): Promise<Object> {
|
||||
return this._interceptUserValue(this._native.signInWithCredential(credential.provider, credential.token, credential.secret));
|
||||
return this._interceptUserValue(
|
||||
this._native.signInWithCredential(
|
||||
credential.provider, credential.token, credential.secret,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param phoneNumber
|
||||
* @return {Promise.<TResult>}
|
||||
* @return Object
|
||||
*/
|
||||
signInWithPhoneNumber(phoneNumber: string): Promise<Object> {
|
||||
return this._native.signInWithPhoneNumber(phoneNumber).then((result) => {
|
||||
return new ConfirmationResult(this, result.verificationId);
|
||||
});
|
||||
const phoneAuthRequestKey = generatePushID();
|
||||
|
||||
return {
|
||||
onCodeSent: (cb) => {
|
||||
if (!isFunction(cb)) {
|
||||
throw new Error(INTERNALS.STRINGS.ERROR_MISSING_CB('onCodeSent'));
|
||||
}
|
||||
|
||||
this.once(`phone:auth:${phoneAuthRequestKey}:confirm`, cb);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// start auth flow
|
||||
this._native.signInWithPhoneNumber(
|
||||
phoneNumber,
|
||||
phoneAuthRequestKey,
|
||||
);
|
||||
|
||||
let hadEvent = false;
|
||||
const successEvent = `phone:auth:${phoneAuthRequestKey}:user`;
|
||||
const errorEvent = `phone:auth:${phoneAuthRequestKey}:error`;
|
||||
|
||||
this.once(successEvent, (user) => {
|
||||
if (!hadEvent) {
|
||||
hadEvent = true;
|
||||
this.removeListener(errorEvent, reject);
|
||||
resolve(user);
|
||||
}
|
||||
});
|
||||
|
||||
this.once(errorEvent, (error) => {
|
||||
if (!hadEvent) {
|
||||
hadEvent = true;
|
||||
this.removeListener(successEvent, resolve);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,6 +23,7 @@ const NATIVE_MODULE_EVENTS = {
|
|||
],
|
||||
Auth: [
|
||||
'onAuthStateChanged',
|
||||
'phone_auth_event',
|
||||
],
|
||||
Database: [
|
||||
// 'database_on_event',
|
||||
|
@ -133,6 +134,10 @@ export default class ModuleBase {
|
|||
return INTERNALS.SharedEventEmitter.addListener.bind(INTERNALS.SharedEventEmitter);
|
||||
}
|
||||
|
||||
get once() {
|
||||
return INTERNALS.SharedEventEmitter.once.bind(INTERNALS.SharedEventEmitter);
|
||||
}
|
||||
|
||||
get on() {
|
||||
return INTERNALS.SharedEventEmitter.addListener.bind(INTERNALS.SharedEventEmitter);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue