diff --git a/android/src/main/java/io/invertase/firebase/auth/RNFirebaseAuth.java b/android/src/main/java/io/invertase/firebase/auth/RNFirebaseAuth.java index f0dcb371..8c0a39ee 100644 --- a/android/src/main/java/io/invertase/firebase/auth/RNFirebaseAuth.java +++ b/android/src/main/java/io/invertase/firebase/auth/RNFirebaseAuth.java @@ -54,6 +54,7 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule { private String mVerificationId; private ReactContext mReactContext; private HashMap mAuthListeners = new HashMap<>(); + private HashMap mIdTokenListeners = new HashMap<>(); RNFirebaseAuth(ReactApplicationContext reactContext) { @@ -88,11 +89,11 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule { msgMap.putBoolean("authenticated", true); msgMap.putString("appName", appName); // for js side distribution msgMap.putMap("user", firebaseUserToMap(user)); - Utils.sendEvent(mReactContext, "onAuthStateChanged", msgMap); + Utils.sendEvent(mReactContext, "auth_state_changed", msgMap); } else { msgMap.putString("appName", appName); // for js side distribution msgMap.putBoolean("authenticated", false); - Utils.sendEvent(mReactContext, "onAuthStateChanged", msgMap); + Utils.sendEvent(mReactContext, "auth_state_changed", msgMap); } } }; @@ -120,6 +121,58 @@ class RNFirebaseAuth extends ReactContextBaseJavaModule { } } + /** + * Add a new id token listener - if one doesn't exist already + */ + @ReactMethod + public void addIdTokenListener(final String appName) { + Log.d(TAG, "addIdTokenListener"); + + FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); + FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); + + if (!mIdTokenListeners.containsKey(appName)) { + FirebaseAuth.IdTokenListener newIdTokenListener = new FirebaseAuth.IdTokenListener() { + @Override + public void onIdTokenChanged(@NonNull FirebaseAuth firebaseAuth) { + FirebaseUser user = firebaseAuth.getCurrentUser(); + WritableMap msgMap = Arguments.createMap(); + if (user != null) { + msgMap.putBoolean("authenticated", true); + msgMap.putString("appName", appName); // for js side distribution + msgMap.putMap("user", firebaseUserToMap(user)); + Utils.sendEvent(mReactContext, "auth_id_token_changed", msgMap); + } else { + msgMap.putString("appName", appName); // for js side distribution + msgMap.putBoolean("authenticated", false); + Utils.sendEvent(mReactContext, "auth_id_token_changed", msgMap); + } + } + }; + + firebaseAuth.addIdTokenListener(newIdTokenListener); + mIdTokenListeners.put(appName, newIdTokenListener); + } + } + + /** + * Removes the current id token listener + */ + @ReactMethod + public void removeIdTokenListener(String appName) { + Log.d(TAG, "removeIdTokenListener"); + + FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); + FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); + + FirebaseAuth.IdTokenListener mIdTokenListener = mIdTokenListeners.get(appName); + + if (mIdTokenListener != null) { + firebaseAuth.removeIdTokenListener(mIdTokenListener); + mIdTokenListeners.remove(appName); + } + } + /** * signOut * diff --git a/ios/RNFirebase/RNFirebaseEvents.h b/ios/RNFirebase/RNFirebaseEvents.h index c7af4c05..bcdc186d 100644 --- a/ios/RNFirebase/RNFirebaseEvents.h +++ b/ios/RNFirebase/RNFirebaseEvents.h @@ -3,9 +3,8 @@ #import -static NSString *const AUTH_CHANGED_EVENT = @"onAuthStateChanged"; -static NSString *const AUTH_ERROR_EVENT = @"authError"; -static NSString *const AUTH_ANONYMOUS_ERROR_EVENT = @"authAnonymousError"; +static NSString *const AUTH_CHANGED_EVENT = @"auth_state_changed"; +static NSString *const AUTH_ID_TOKEN_CHANGED_EVENT = @"auth_id_token_changed"; // Database static NSString *const DATABASE_SYNC_EVENT = @"database_sync_event"; diff --git a/ios/RNFirebase/auth/RNFirebaseAuth.h b/ios/RNFirebase/auth/RNFirebaseAuth.h index 802e2a1a..05975067 100644 --- a/ios/RNFirebase/auth/RNFirebaseAuth.h +++ b/ios/RNFirebase/auth/RNFirebaseAuth.h @@ -9,6 +9,7 @@ @interface RNFirebaseAuth : RCTEventEmitter {}; @property NSMutableDictionary *authStateHandlers; +@property NSMutableDictionary *idTokenHandlers; @end diff --git a/ios/RNFirebase/auth/RNFirebaseAuth.m b/ios/RNFirebase/auth/RNFirebaseAuth.m index 18faac5c..66b40bfc 100644 --- a/ios/RNFirebase/auth/RNFirebaseAuth.m +++ b/ios/RNFirebase/auth/RNFirebaseAuth.m @@ -12,6 +12,7 @@ RCT_EXPORT_MODULE(); self = [super init]; if (self != nil) { _authStateHandlers = [[NSMutableDictionary alloc] init]; + _idTokenHandlers = [[NSMutableDictionary alloc] init]; } return self; } @@ -51,6 +52,41 @@ RCT_EXPORT_METHOD(removeAuthStateListener: } } +/** + addIdTokenListener + + */ +RCT_EXPORT_METHOD(addIdTokenListener: + (NSString *) appName) { + + if (![_idTokenHandlers valueForKey:appName]) { + FIRApp *firApp = [FIRApp appNamed:appName]; + FIRIDTokenDidChangeListenerHandle newListenerHandle = [[FIRAuth authWithApp:firApp] addIDTokenDidChangeListener:^(FIRAuth * _Nonnull auth, FIRUser * _Nullable user) { + if (user != nil) { + [self sendJSEventWithAppName:appName title:AUTH_ID_TOKEN_CHANGED_EVENT props:[@{@"authenticated": @(true), @"user": [self firebaseUserToDict:user]} mutableCopy]]; + } else { + [self sendJSEventWithAppName:appName title:AUTH_ID_TOKEN_CHANGED_EVENT props:[@{@"authenticated": @(false)} mutableCopy]]; + } + }]; + + _idTokenHandlers[appName] = [NSValue valueWithNonretainedObject:newListenerHandle]; + } +} + +/** + removeAuthStateListener + + */ +RCT_EXPORT_METHOD(removeIdTokenListener: + (NSString *) appName) { + if ([_idTokenHandlers valueForKey:appName]) { + FIRApp *firApp = [FIRApp appNamed:appName]; + [[FIRAuth authWithApp:firApp] removeIDTokenDidChangeListener:[_idTokenHandlers valueForKey:appName]]; + [_idTokenHandlers removeObjectForKey:appName]; + } +} + + /** signOut @@ -1073,7 +1109,7 @@ RCT_EXPORT_METHOD(fetchProvidersForEmail: } - (NSArray *)supportedEvents { - return @[AUTH_CHANGED_EVENT, AUTH_ANONYMOUS_ERROR_EVENT, AUTH_ERROR_EVENT]; + return @[AUTH_CHANGED_EVENT, AUTH_ID_TOKEN_CHANGED_EVENT]; } @end diff --git a/lib/modules/auth/index.js b/lib/modules/auth/index.js index 46988232..789a39e8 100644 --- a/lib/modules/auth/index.js +++ b/lib/modules/auth/index.js @@ -26,12 +26,19 @@ export default class Auth extends ModuleBase { this.authenticated = false; this.addListener( // sub to internal native event - this fans out to - // public event name: onAuthStateChangedPublic - this._getAppEventName('onAuthStateChanged'), + // public event name: onAuthStateChanged + this._getAppEventName('auth_state_changed'), this._onAuthStateChanged.bind(this), ); + this.addListener( + // sub to internal native event - this fans out to + // public event name: onIdTokenChanged + this._getAppEventName('auth_id_token_changed'), + this._onIdTokenChanged.bind(this), + ); this._native.addAuthStateListener(); + this._native.addIdTokenListener(); } /** @@ -46,18 +53,42 @@ export default class Auth extends ModuleBase { if (auth && auth.user && !this._user) this._user = new User(this, auth); else if ((!auth || !auth.user) && this._user) this._user = null; else if (this._user) this._user._updateValues(auth); - if (emit) this.emit(this._getAppEventName('onAuthStateChangedPublic'), this._user); + if (emit) this.emit(this._getAppEventName('onAuthStateChanged'), this._user); return auth ? this._user : null; } - /** * Remove auth change listener * @param listener */ _offAuthStateChanged(listener: Function) { this.log.info('Removing onAuthStateChanged listener'); - this.removeListener(this._getAppEventName('onAuthStateChangedPublic'), listener); + this.removeListener(this._getAppEventName('onAuthStateChanged'), listener); + } + + /** + * Internal auth changed listener + * @param auth + * @param emit + * @private + */ + _onIdTokenChanged(auth: AuthResultType, emit: boolean = true) { + this._authResult = auth; + this.authenticated = auth ? auth.authenticated || false : false; + if (auth && auth.user && !this._user) this._user = new User(this, auth); + else if ((!auth || !auth.user) && this._user) this._user = null; + else if (this._user) this._user._updateValues(auth); + if (emit) this.emit(this._getAppEventName('onIdTokenChanged'), this._user); + return auth ? this._user : null; + } + + /** + * Remove id token change listener + * @param listener + */ + _offIdTokenChanged(listener: Function) { + this.log.info('Removing onIdTokenChanged listener'); + this.removeListener(this._getAppEventName('onIdTokenChanged'), listener); } /** @@ -86,11 +117,22 @@ export default class Auth extends ModuleBase { */ onAuthStateChanged(listener: Function) { this.log.info('Creating onAuthStateChanged listener'); - this.on(this._getAppEventName('onAuthStateChangedPublic'), listener); + this.on(this._getAppEventName('onAuthStateChanged'), listener); if (this._authResult) listener(this._user || null); return this._offAuthStateChanged.bind(this, listener); } + /** + * Listen for id token changes. + * @param listener + */ + onIdTokenChanged(listener: Function) { + this.log.info('Creating onIdTokenChanged listener'); + this.on(this._getAppEventName('onIdTokenChanged'), listener); + if (this._authResult) listener(this._user || null); + return this._offIdTokenChanged.bind(this, listener); + } + /** * Sign the current user out * @return {Promise} diff --git a/tests/ios/Podfile.lock b/tests/ios/Podfile.lock index b785cf9d..ebb97db9 100644 --- a/tests/ios/Podfile.lock +++ b/tests/ios/Podfile.lock @@ -1,60 +1,61 @@ PODS: - - Firebase/AdMob (4.0.4): + - Firebase/AdMob (4.1.0): - Firebase/Core - - Google-Mobile-Ads-SDK (= 7.21.0) - - Firebase/Analytics (4.0.4): + - Google-Mobile-Ads-SDK (= 7.22.0) + - Firebase/Auth (4.1.0): - Firebase/Core - - Firebase/Auth (4.0.4): + - FirebaseAuth (= 4.1.0) + - Firebase/Core (4.1.0): + - FirebaseAnalytics (= 4.0.3) + - FirebaseCore (= 4.0.5) + - Firebase/Crash (4.1.0): - Firebase/Core - - FirebaseAuth (= 4.0.0) - - Firebase/Core (4.0.4): - - FirebaseAnalytics (= 4.0.2) - - FirebaseCore (= 4.0.4) - - Firebase/Crash (4.0.4): + - FirebaseCrash (= 2.0.1) + - Firebase/Database (4.1.0): - Firebase/Core - - FirebaseCrash (= 2.0.0) - - Firebase/Database (4.0.4): + - FirebaseDatabase (= 4.0.1) + - Firebase/DynamicLinks (4.1.0): - Firebase/Core - - FirebaseDatabase (= 4.0.0) - - Firebase/DynamicLinks (4.0.4): + - FirebaseDynamicLinks (= 2.1.0) + - Firebase/Messaging (4.1.0): - Firebase/Core - - FirebaseDynamicLinks (= 2.0.0) - - Firebase/Messaging (4.0.4): - - Firebase/Core - - FirebaseMessaging (= 2.0.0) - - Firebase/Performance (4.0.4): + - FirebaseMessaging (= 2.0.1) + - Firebase/Performance (4.1.0): - Firebase/Core - FirebasePerformance (= 1.0.3) - - Firebase/RemoteConfig (4.0.4): + - Firebase/RemoteConfig (4.1.0): - Firebase/Core - - FirebaseRemoteConfig (= 2.0.1) - - Firebase/Storage (4.0.4): + - FirebaseRemoteConfig (= 2.0.2) + - Firebase/Storage (4.1.0): - Firebase/Core - - FirebaseStorage (= 2.0.0) - - FirebaseAnalytics (4.0.2): + - FirebaseStorage (= 2.0.1) + - FirebaseAnalytics (4.0.3): - FirebaseCore (~> 4.0) - FirebaseInstanceID (~> 2.0) - GoogleToolboxForMac/NSData+zlib (~> 2.1) - - FirebaseAuth (4.0.0): + - nanopb (~> 0.3) + - FirebaseAuth (4.1.0): - FirebaseAnalytics (~> 4.0) - GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1) - GTMSessionFetcher/Core (~> 1.1) - - FirebaseCore (4.0.4): + - FirebaseCore (4.0.5): - GoogleToolboxForMac/NSData+zlib (~> 2.1) - nanopb (~> 0.3) - - FirebaseCrash (2.0.0): + - FirebaseCrash (2.0.1): - FirebaseAnalytics (~> 4.0) - FirebaseInstanceID (~> 2.0) - GoogleToolboxForMac/Logger (~> 2.1) - GoogleToolboxForMac/NSData+zlib (~> 2.1) - Protobuf (~> 3.1) - - FirebaseDatabase (4.0.0): + - FirebaseDatabase (4.0.1): - FirebaseAnalytics (~> 4.0) - - FirebaseDynamicLinks (2.0.0): - - FirebaseAnalytics (~> 4.0) - - FirebaseInstanceID (2.0.0): - FirebaseCore (~> 4.0) - - FirebaseMessaging (2.0.0): + - leveldb-library (~> 1.18) + - FirebaseDynamicLinks (2.1.0): + - FirebaseAnalytics (~> 4.0) + - FirebaseInstanceID (2.0.1): + - FirebaseCore (~> 4.0) + - FirebaseMessaging (2.0.1): - FirebaseAnalytics (~> 4.0) - FirebaseCore (~> 4.0) - FirebaseInstanceID (~> 2.0) @@ -67,16 +68,16 @@ PODS: - GoogleToolboxForMac/NSData+zlib (~> 2.1) - GTMSessionFetcher/Core (~> 1.1) - Protobuf (~> 3.1) - - FirebaseRemoteConfig (2.0.1): + - FirebaseRemoteConfig (2.0.2): - FirebaseAnalytics (~> 4.0) - FirebaseInstanceID (~> 2.0) - GoogleToolboxForMac/NSData+zlib (~> 2.1) - Protobuf (~> 3.1) - - FirebaseStorage (2.0.0): + - FirebaseStorage (2.0.1): - FirebaseAnalytics (~> 4.0) - FirebaseCore (~> 4.0) - GTMSessionFetcher/Core (~> 1.1) - - Google-Mobile-Ads-SDK (7.21.0) + - Google-Mobile-Ads-SDK (7.22.0) - GoogleToolboxForMac/DebugUtils (2.1.1): - GoogleToolboxForMac/Defines (= 2.1.1) - GoogleToolboxForMac/Defines (2.1.1) @@ -90,12 +91,13 @@ PODS: - GoogleToolboxForMac/NSString+URLArguments (= 2.1.1) - GoogleToolboxForMac/NSString+URLArguments (2.1.1) - GTMSessionFetcher/Core (1.1.11) + - leveldb-library (1.18.3) - nanopb (0.3.8): - nanopb/decode (= 0.3.8) - nanopb/encode (= 0.3.8) - nanopb/decode (0.3.8) - nanopb/encode (0.3.8) - - Protobuf (3.3.0) + - Protobuf (3.4.0) - React (0.44.3): - React/Core (= 0.44.3) - React/Core (0.44.3): @@ -104,13 +106,12 @@ PODS: - React/cxxreact (0.44.3): - React/jschelpers - React/jschelpers (0.44.3) - - RNFirebase (2.1.3): + - RNFirebase (3.0.0-alpha.5): - React - Yoga (0.44.3.React) DEPENDENCIES: - Firebase/AdMob - - Firebase/Analytics - Firebase/Auth - Firebase/Core - Firebase/Crash @@ -133,27 +134,28 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: - Firebase: 1492bf04e1b73a7353b4fb2cf5a20bac9692f341 - FirebaseAnalytics: ad41720e3e67fc63fbe3d2948d3e26932a8de311 - FirebaseAuth: ebb6abcbabae00fc47446d690c19ce68d8484fde - FirebaseCore: cfc042628ef9f20debe09c0eb683135fcd404cb4 - FirebaseCrash: 600ec2cbd8e10c3064a42de2c6ee7687bd02a076 - FirebaseDatabase: d829b3a8c3e2ac7a16773c5df226966b0805dfc2 - FirebaseDynamicLinks: ad3bef2e8addfa1490af9db02201429c5c9447dd - FirebaseInstanceID: 9fbf536668f4d3f0880e7438456dabd1376e294b - FirebaseMessaging: 227406c05b0dc9290702d2e9f18ab5528f0c2cf2 + Firebase: ebebf41db7f10e0c7668b6eaaa857fbe599aa478 + FirebaseAnalytics: 76f754d37ca5b04f36856729b6af3ca0152d1069 + FirebaseAuth: 8d1d2389cf82f891048d6d50d27d044f55ae09a6 + FirebaseCore: 7d876ea97a830cbe62ba7fbbe7670c833a324ba0 + FirebaseCrash: bce9fbfb7dd6cc850a41fe39740a2292e6b61f2e + FirebaseDatabase: 94c38c783d23dc6679441050772d42e801d06e9e + FirebaseDynamicLinks: ed4cb6c42705aaa5e841ed2d76e3a4bddbec10c1 + FirebaseInstanceID: 0500e3cb54a1a4e01a8cffcc09323b8bb8fc7e1e + FirebaseMessaging: b45ff9ef5d932600d3f78ff43168e7c5707aa7dc FirebasePerformance: 36bdb0500213b459ae991766801d5dc5399ff231 - FirebaseRemoteConfig: 328244399f64226cfdfa32efee596bb010ef5665 - FirebaseStorage: 8110a1ed2034c8fbfd83890d2acc9cdbbd99afec - Google-Mobile-Ads-SDK: 4a6f1b43c8cc0e1e95a3b59d9f52a01671c6f9f3 + FirebaseRemoteConfig: 5b3e3301ef2f237b1b588e8ef3211b5a22e9e15d + FirebaseStorage: 661fc1f8d4131891d256b62e82a45ace8b3f0c3b + Google-Mobile-Ads-SDK: 1bdf1a4244d0553b1840239874c209c01aef055f GoogleToolboxForMac: 8e329f1b599f2512c6b10676d45736bcc2cbbeb0 GTMSessionFetcher: 5ad62e8200fa00ed011fe5e08d27fef72c5b1429 + leveldb-library: 10fb39c39e243db4af1828441162405bbcec1404 nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3 - Protobuf: d582fecf68201eac3d79ed61369ef45734394b9c + Protobuf: 03eef2ee0b674770735cf79d9c4d3659cf6908e8 React: 6361345ebeb769a929e10a06baf0c868d6d03ad5 - RNFirebase: fbd064a85661d42410d4a509b46fbfe313b5eedf + RNFirebase: 60be8c01b94551a12e7be5431189e8ee8cefcdd3 Yoga: c90474ca3ec1edba44c97b6c381f03e222a9e287 -PODFILE CHECKSUM: 45666f734ebfc8b3b0f2be0a83bc2680caeb502f +PODFILE CHECKSUM: 46b6a553f3c9fd264b449806b373d33b4af518b5 COCOAPODS: 1.2.1