diff options
author | Ryan Wilson <wilsonryan@google.com> | 2018-07-11 17:40:25 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-07-11 17:40:25 -0400 |
commit | 79f663d5eaaa4b7f9fe46d176c82660d38e4be8a (patch) | |
tree | 47b27cb93aa0372eb141668526552b3559ac1dbb /Firebase/Auth | |
parent | c6b4b03fffc3cea7c9525e5c79dce28f52900521 (diff) |
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.
Diffstat (limited to 'Firebase/Auth')
-rw-r--r-- | Firebase/Auth/Source/FIRAuth.m | 222 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuth_Internal.h | 4 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRUser.m | 2 |
3 files changed, 130 insertions, 98 deletions
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(<UIKit/UIKit.h>) +#import <UIKit/UIKit.h> +#endif + +#import <FirebaseAuthInterop/FIRAuthInterop.h> #import <FirebaseCore/FIRAppAssociationRegistration.h> #import <FirebaseCore/FIRAppInternal.h> +#import <FirebaseCore/FIRComponent.h> +#import <FirebaseCore/FIRComponentContainer.h> +#import <FirebaseCore/FIRComponentRegistrant.h> +#import <FirebaseCore/FIRCoreConfigurable.h> #import <FirebaseCore/FIRLogger.h> #import <FirebaseCore/FIROptions.h> #import <GoogleUtilities/GULAppEnvironmentUtil.h> @@ -63,7 +72,6 @@ #import "FIRVerifyPhoneNumberResponse.h" #if TARGET_OS_IOS -#import <UIKit/UIKit.h> #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 () <FIRAuthAppDelegateHandler> +@interface FIRAuth () <FIRAuthAppDelegateHandler, FIRAuthInterop, FIRComponentRegistrant, FIRCoreConfigurable, FIRComponentLifecycleMaintainer> #else -@interface FIRAuth () +@interface FIRAuth () <FIRAuthInterop, FIRComponentRegistrant, FIRCoreConfigurable, FIRComponentLifecycleMaintainer> #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<FIRAuthInterop> 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<NSString *, NSString *> *FIRAuthParseURL(NSString *urlString return YES; } -- (nullable NSString *)getUID { +#pragma mark - Interoperability + ++ (nonnull NSArray<FIRComponent *> *)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; |