UNPKG

@react-native-firebase/auth

Version:

React Native Firebase - The authentication module provides an easy-to-use API to integrate an authentication workflow into new and existing applications. React Native Firebase provides access to all Firebase authentication methods and identity providers.

241 lines (201 loc) 6.51 kB
/* * Copyright (c) 2016-present Invertase Limited & Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this library except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ import { isAndroid, isFunction, isIOS, promiseDefer } from '@react-native-firebase/app/lib/common'; import NativeFirebaseError from '@react-native-firebase/app/lib/internal/NativeFirebaseError'; let REQUEST_ID = 0; export default class PhoneAuthListener { constructor(auth, phoneNumber, timeout, forceResend) { this._auth = auth; this._reject = null; this._resolve = null; this._promise = null; this._jsStack = new Error().stack; this._timeout = timeout || 20; this._phoneAuthRequestId = REQUEST_ID++; this._forceResending = forceResend || false; // internal events this._internalEvents = { codeSent: `phone:auth:${this._phoneAuthRequestId}:onCodeSent`, verificationFailed: `phone:auth:${this._phoneAuthRequestId}:onVerificationFailed`, verificationComplete: `phone:auth:${this._phoneAuthRequestId}:onVerificationComplete`, codeAutoRetrievalTimeout: `phone:auth:${this._phoneAuthRequestId}:onCodeAutoRetrievalTimeout`, }; // user observer events this._publicEvents = { error: `phone:auth:${this._phoneAuthRequestId}:error`, event: `phone:auth:${this._phoneAuthRequestId}:event`, success: `phone:auth:${this._phoneAuthRequestId}:success`, }; this._subscribeToEvents(); if (isAndroid) { this._auth.native.verifyPhoneNumber( phoneNumber, this._phoneAuthRequestId + '', this._timeout, this._forceResending, ); } if (isIOS) { this._auth.native.verifyPhoneNumber(phoneNumber, this._phoneAuthRequestId + ''); } } _subscribeToEvents() { const events = Object.keys(this._internalEvents); for (let i = 0, len = events.length; i < len; i++) { const type = events[i]; const subscription = this._auth.emitter.addListener(this._internalEvents[type], event => { this[`_${type}Handler`](event); subscription.remove(); }); } } _addUserObserver(observer) { this._auth.emitter.addListener(this._publicEvents.event, observer); } _emitToObservers(snapshot) { this._auth.emitter.emit(this._publicEvents.event, snapshot); } _emitToErrorCb(snapshot) { const { error } = snapshot; if (this._reject) { this._reject(error); } this._auth.emitter.emit(this._publicEvents.error, error); } _emitToSuccessCb(snapshot) { if (this._resolve) { this._resolve(snapshot); } this._auth.emitter.emit(this._publicEvents.success, snapshot); } _removeAllListeners() { setTimeout(() => { // move to next event loop - not sure if needed // internal listeners Object.values(this._internalEvents).forEach(event => { this._auth.emitter.removeAllListeners(event); }); // user observer listeners Object.values(this._publicEvents).forEach(publicEvent => { this._auth.emitter.removeAllListeners(publicEvent); }); }, 0); } _promiseDeferred() { if (!this._promise) { const { promise, resolve, reject } = promiseDefer(); this._promise = promise; this._resolve = resolve; this._reject = reject; } } /* -------------------------- --- INTERNAL EVENT HANDLERS ---------------------------- */ _codeSentHandler(credential) { const snapshot = { verificationId: credential.verificationId, code: null, error: null, state: 'sent', }; this._emitToObservers(snapshot); if (isIOS) { this._emitToSuccessCb(snapshot); } if (isAndroid) { // android can auto retrieve so we don't emit to successCb immediately, // if auto retrieve times out then that will emit to successCb } } _codeAutoRetrievalTimeoutHandler(credential) { const snapshot = { verificationId: credential.verificationId, code: null, error: null, state: 'timeout', }; this._emitToObservers(snapshot); this._emitToSuccessCb(snapshot); } _verificationCompleteHandler(credential) { const snapshot = { verificationId: credential.verificationId, code: credential.code || null, error: null, state: 'verified', }; this._emitToObservers(snapshot); this._emitToSuccessCb(snapshot); this._removeAllListeners(); } _verificationFailedHandler(state) { const snapshot = { verificationId: state.verificationId, code: null, error: null, state: 'error', }; snapshot.error = new NativeFirebaseError({ userInfo: state.error }, this._jsStack, 'auth'); this._emitToObservers(snapshot); this._emitToErrorCb(snapshot); this._removeAllListeners(); } /* ------------- -- PUBLIC API --------------*/ on(event, observer, errorCb, successCb) { if (event !== 'state_changed') { throw new Error( "firebase.auth.PhoneAuthListener.on(*, _, _, _) 'event' must equal 'state_changed'.", ); } if (!isFunction(observer)) { throw new Error( "firebase.auth.PhoneAuthListener.on(_, *, _, _) 'observer' must be a function.", ); } this._addUserObserver(observer); if (isFunction(errorCb)) { const subscription = this._auth.emitter.addListener(this._publicEvents.error, event => { subscription.remove(); errorCb(event); }); } if (isFunction(successCb)) { const subscription = this._auth.emitter.addListener(this._publicEvents.success, event => { subscription.remove(); successCb(event); }); } return this; } then(fn) { this._promiseDeferred(); if (this._promise) { return this._promise.then.bind(this._promise)(fn); } return undefined; } catch(fn) { this._promiseDeferred(); if (this._promise) { return this._promise.catch.bind(this._promise)(fn); } return undefined; } }