From 79f663d5eaaa4b7f9fe46d176c82660d38e4be8a Mon Sep 17 00:00:00 2001 From: Ryan Wilson Date: Wed, 11 Jul 2018 17:40:25 -0400 Subject: Add Auth interoperability library. (#1501) * Add Auth interoperability library. This allows other SDKs to retrieve the user ID and fetch a new Auth token in a type-safe way through the Core container. * Remove unnecessary import. * Add missing copyright. * Resolve comments. - Remove unnecessary umbrella header. - Move to version 1.0. - Add tvOS support for app lifecycle changes. * Move public interop headers to private. * Add public_header_files too. * Moved headers back into public. * Shorten Interoperability and Interoperable to Interop. * Add AuthInterop to travis linting. * Fixed tag format in AuthInterop.podspec. * Add AuthInterop path to Firestore Podfile. --- Firebase/Auth/Source/FIRAuth.m | 222 ++++++++++++++++++-------------- Firebase/Auth/Source/FIRAuth_Internal.h | 4 +- Firebase/Auth/Source/FIRUser.m | 2 +- 3 files changed, 130 insertions(+), 98 deletions(-) (limited to 'Firebase/Auth') diff --git a/Firebase/Auth/Source/FIRAuth.m b/Firebase/Auth/Source/FIRAuth.m index 691c6ce..b353ebb 100644 --- a/Firebase/Auth/Source/FIRAuth.m +++ b/Firebase/Auth/Source/FIRAuth.m @@ -18,8 +18,17 @@ #import "FIRAuth_Internal.h" +#if __has_include() +#import +#endif + +#import #import #import +#import +#import +#import +#import #import #import #import @@ -63,7 +72,6 @@ #import "FIRVerifyPhoneNumberResponse.h" #if TARGET_OS_IOS -#import #import "FIRAuthAPNSToken.h" #import "FIRAuthAPNSTokenManager.h" #import "FIRAuthAppCredentialManager.h" @@ -218,9 +226,9 @@ static NSMutableDictionary *gKeychainServiceNameForAppName; #pragma mark - FIRAuth #if TARGET_OS_IOS -@interface FIRAuth () +@interface FIRAuth () #else -@interface FIRAuth () +@interface FIRAuth () #endif /** @property firebaseAppId @@ -300,42 +308,12 @@ static NSMutableDictionary *gKeychainServiceNameForAppName; } + (void)load { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - gKeychainServiceNameForAppName = [[NSMutableDictionary alloc] init]; - - NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter]; + [FIRComponentContainer registerAsComponentRegistrant:self]; + [FIRApp registerAsConfigurable:self]; +} - // Ensures the @c FIRAuth instance for a given app gets loaded as soon as the app is ready. - [defaultCenter addObserverForName:kFIRAppReadyToConfigureSDKNotification - object:[FIRApp class] - queue:nil - usingBlock:^(NSNotification *notification) { - [FIRAuth authWithApp:[FIRApp appNamed:notification.userInfo[kFIRAppNameKey]]]; - }]; - // Ensures the saved user is cleared when the app is deleted. - [defaultCenter addObserverForName:kFIRAppDeleteNotification - object:[FIRApp class] - queue:nil - usingBlock:^(NSNotification *notification) { - dispatch_async(FIRAuthGlobalWorkQueue(), ^{ - // This doesn't stop any request already issued, see b/27704535 . - NSString *appName = notification.userInfo[kFIRAppNameKey]; - NSString *keychainServiceName = [FIRAuth keychainServiceNameForAppName:appName]; - if (keychainServiceName) { - [self deleteKeychainServiceNameForAppName:appName]; - FIRAuthKeychain *keychain = [[FIRAuthKeychain alloc] initWithService:keychainServiceName]; - NSString *userKey = [NSString stringWithFormat:kUserKey, appName]; - [keychain removeDataForKey:userKey error:NULL]; - } - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] - postNotificationName:FIRAuthStateDidChangeNotification - object:nil]; - }); - }); - }]; - }); ++ (void)initialize { + gKeychainServiceNameForAppName = [[NSMutableDictionary alloc] init]; } + (FIRAuth *)auth { @@ -352,11 +330,10 @@ static NSMutableDictionary *gKeychainServiceNameForAppName; } + (FIRAuth *)authWithApp:(FIRApp *)app { - return [FIRAppAssociationRegistration registeredObjectWithHost:app - key:NSStringFromClass(self) - creationBlock:^FIRAuth *_Nullable() { - return [[FIRAuth alloc] initWithApp:app]; - }]; + // Get the instance of Auth from the container, which will create or return the cached instance + // associated with this app. + id auth = FIR_COMPONENT(FIRAuthInterop, app.container); + return (FIRAuth *)auth; } - (instancetype)initWithApp:(FIRApp *)app { @@ -365,62 +342,19 @@ static NSMutableDictionary *gKeychainServiceNameForAppName; if (self) { _app = app; __weak FIRAuth *weakSelf = self; + + // TODO: Remove this block once Firestore, Database, and Storage move to the new interop API. app.getTokenImplementation = ^(BOOL forceRefresh, FIRTokenCallback callback) { - dispatch_async(FIRAuthGlobalWorkQueue(), ^{ - FIRAuth *strongSelf = weakSelf; - // Enable token auto-refresh if not aleady enabled. - if (strongSelf && !strongSelf->_autoRefreshTokens) { - FIRLogInfo(kFIRLoggerAuth, @"I-AUT000002", @"Token auto-refresh enabled."); - strongSelf->_autoRefreshTokens = YES; - [strongSelf scheduleAutoTokenRefresh]; - - #if TARGET_OS_IOS // TODO: Is a similar mechanism needed on macOS? - strongSelf->_applicationDidBecomeActiveObserver = [[NSNotificationCenter defaultCenter] - addObserverForName:UIApplicationDidBecomeActiveNotification - object:nil - queue:nil - usingBlock:^(NSNotification *notification) { - FIRAuth *strongSelf = weakSelf; - if (strongSelf) { - strongSelf->_isAppInBackground = NO; - if (!strongSelf->_autoRefreshScheduled) { - [weakSelf scheduleAutoTokenRefresh]; - } - } - }]; - strongSelf->_applicationDidEnterBackgroundObserver = [[NSNotificationCenter defaultCenter] - addObserverForName:UIApplicationDidEnterBackgroundNotification - object:nil - queue:nil - usingBlock:^(NSNotification *notification) { - FIRAuth *strongSelf = weakSelf; - if (strongSelf) { - strongSelf->_isAppInBackground = YES; - } - }]; - #endif - } - // Call back with 'nil' if there is no current user. - if (!strongSelf || !strongSelf->_currentUser) { - dispatch_async(dispatch_get_main_queue(), ^{ - callback(nil, nil); - }); - return; - } - // Call back with current user token. - [strongSelf->_currentUser internalGetTokenForcingRefresh:forceRefresh - callback:^(NSString *_Nullable token, - NSError *_Nullable error) { - dispatch_async(dispatch_get_main_queue(), ^{ - callback(token, error); - }); - }]; - }); + // In the meantime, redirect call to the interop method that provides this functionality. + __weak FIRAuth *weakSelf = self; + [weakSelf getTokenForcingRefresh:forceRefresh withCallback:callback]; }; + + // TODO: Remove this block once Firestore, Database, and Storage move to the new interop API. app.getUIDImplementation = ^NSString *_Nullable() { __block NSString *uid; dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ - uid = [weakSelf getUID]; + uid = [weakSelf getUserID]; }); return uid; }; @@ -1878,7 +1812,105 @@ static NSDictionary *FIRAuthParseURL(NSString *urlString return YES; } -- (nullable NSString *)getUID { +#pragma mark - Interoperability + ++ (nonnull NSArray *)componentsToRegister { + FIRComponentCreationBlock authCreationBlock = + ^id _Nullable(FIRComponentContainer *_Nonnull container, BOOL *_Nonnull isCacheable) { + *isCacheable = YES; + return [[FIRAuth alloc] initWithApp:container.app]; + }; + FIRComponent *authInterop = [FIRComponent componentWithProtocol:@protocol(FIRAuthInterop) + creationBlock:authCreationBlock]; + return @[authInterop]; +} + +#pragma mark - FIRCoreConfigurable + ++ (void)configureWithApp:(nonnull FIRApp *)app { + // TODO: Evaluate what actually needs to be configured here instead of initializing a full + // instance. + // Ensures the @c FIRAuth instance for a given app gets loaded as soon as the app is ready. + [FIRAuth authWithApp:app]; +} + +#pragma mark - FIRComponentLifecycleMaintainer + +- (void)appWillBeDeleted:(nonnull FIRApp *)app { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + // This doesn't stop any request already issued, see b/27704535 . + NSString *keychainServiceName = [FIRAuth keychainServiceNameForAppName:app.name]; + if (keychainServiceName) { + [[self class] deleteKeychainServiceNameForAppName:app.name]; + FIRAuthKeychain *keychain = [[FIRAuthKeychain alloc] initWithService:keychainServiceName]; + NSString *userKey = [NSString stringWithFormat:kUserKey, app.name]; + [keychain removeDataForKey:userKey error:NULL]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + // TODO: Move over to fire an event instead, once ready. + [[NSNotificationCenter defaultCenter] postNotificationName:FIRAuthStateDidChangeNotification + object:nil]; + }); + }); +} + +#pragma mark - FIRAuthInterop + +- (void)getTokenForcingRefresh:(BOOL)forceRefresh withCallback:(FIRTokenCallback)callback { + __weak FIRAuth *weakSelf = self; + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + FIRAuth *strongSelf = weakSelf; + // Enable token auto-refresh if not aleady enabled. + if (strongSelf && !strongSelf->_autoRefreshTokens) { + FIRLogInfo(kFIRLoggerAuth, @"I-AUT000002", @"Token auto-refresh enabled."); + strongSelf->_autoRefreshTokens = YES; + [strongSelf scheduleAutoTokenRefresh]; + +#if TARGET_OS_IOS || TARGET_OS_TV // TODO: Is a similar mechanism needed on macOS? + strongSelf->_applicationDidBecomeActiveObserver = [[NSNotificationCenter defaultCenter] + addObserverForName:UIApplicationDidBecomeActiveNotification + object:nil + queue:nil + usingBlock:^(NSNotification *notification) { + FIRAuth *strongSelf = weakSelf; + if (strongSelf) { + strongSelf->_isAppInBackground = NO; + if (!strongSelf->_autoRefreshScheduled) { + [weakSelf scheduleAutoTokenRefresh]; + } + } + }]; + strongSelf->_applicationDidEnterBackgroundObserver = [[NSNotificationCenter defaultCenter] + addObserverForName:UIApplicationDidEnterBackgroundNotification + object:nil + queue:nil + usingBlock:^(NSNotification *notification) { + FIRAuth *strongSelf = weakSelf; + if (strongSelf) { + strongSelf->_isAppInBackground = YES; + } + }]; +#endif + } + // Call back with 'nil' if there is no current user. + if (!strongSelf || !strongSelf->_currentUser) { + dispatch_async(dispatch_get_main_queue(), ^{ + callback(nil, nil); + }); + return; + } + // Call back with current user token. + [strongSelf->_currentUser internalGetTokenForcingRefresh:forceRefresh + callback:^(NSString *_Nullable token, + NSError *_Nullable error) { + dispatch_async(dispatch_get_main_queue(), ^{ + callback(token, error); + }); + }]; + }); +} + +- (nullable NSString *)getUserID { return _currentUser.uid; } diff --git a/Firebase/Auth/Source/FIRAuth_Internal.h b/Firebase/Auth/Source/FIRAuth_Internal.h index 4d87a08..519ece3 100644 --- a/Firebase/Auth/Source/FIRAuth_Internal.h +++ b/Firebase/Auth/Source/FIRAuth_Internal.h @@ -69,11 +69,11 @@ NS_ASSUME_NONNULL_BEGIN - (nullable instancetype)initWithAPIKey:(NSString *)APIKey appName:(NSString *)appName NS_DESIGNATED_INITIALIZER; -/** @fn getUID +/** @fn getUserID @brief Gets the identifier of the current user, if any. @return The identifier of the current user, or nil if there is no current user. */ -- (nullable NSString *)getUID; +- (nullable NSString *)getUserID; /** @fn updateKeychainWithUser:error: @brief Updates the keychain for the given user. diff --git a/Firebase/Auth/Source/FIRUser.m b/Firebase/Auth/Source/FIRUser.m index ad0b1d4..f3832a7 100644 --- a/Firebase/Auth/Source/FIRUser.m +++ b/Firebase/Auth/Source/FIRUser.m @@ -766,7 +766,7 @@ static void callInMainThreadWithAuthDataResultAndError( callInMainThreadWithAuthDataResultAndError(completion, authResult, error); return; } - if (![authResult.user.uid isEqual:[self->_auth getUID]]) { + if (![authResult.user.uid isEqual:[self->_auth getUserID]]) { callInMainThreadWithAuthDataResultAndError(completion, authResult, [FIRAuthErrorUtils userMismatchError]); return; -- cgit v1.2.3