Skip to content

Commit

Permalink
Fix keychain access issues caused by prewarming (#9622)
Browse files Browse the repository at this point in the history
* Try listening to `UIApplicationProtectedDataDidBecomeAvailable`.

* Handle platform availability.

* More platform availability handling.

* Fix static analysis issue.

* Update changelog.
  • Loading branch information
rosalyntan authored Apr 19, 2022
1 parent a2b3758 commit dc45185
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 49 deletions.
1 change: 1 addition & 0 deletions FirebaseAuth/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# v9.0.0
- [fixed] **Breaking change:** Fixed an ObjC-to-Swift API conversion error where `getStoredUser(forAccessGroup:)` returned a non-optional type. This change is breaking for Swift users only (#8599).
- [fixed] Fixed an iOS 15 keychain access issue related to prewarming. (#8695)

# v8.14.0
- [added] Started to collect the Firebase user agent for Firebase Auth. (#9066)
Expand Down
145 changes: 96 additions & 49 deletions FirebaseAuth/Sources/Auth/FIRAuth.m
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,12 @@ @implementation FIRAuth {
UIApplicationDidEnterBackgroundNotification.
*/
id<NSObject> _applicationDidEnterBackgroundObserver;

/** @var _protectedDataDidBecomeAvailableObserver
@brief An opaque object to act as the observer for
UIApplicationProtectedDataDidBecomeAvailable.
*/
id<NSObject> _protectedDataDidBecomeAvailableObserver;
}

+ (void)load {
Expand Down Expand Up @@ -485,65 +491,107 @@ - (nullable instancetype)initWithAPIKey:(NSString *)APIKey
_firebaseAppName = [appName copy];
#if TARGET_OS_IOS
_settings = [[FIRAuthSettings alloc] init];

[GULAppDelegateSwizzler proxyOriginalDelegateIncludingAPNSMethods];
[GULSceneDelegateSwizzler proxyOriginalSceneDelegate];
#endif // TARGET_OS_IOS

#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_MACCATALYST
static Class applicationClass = nil;
// iOS App extensions should not call [UIApplication sharedApplication], even if UIApplication
// responds to it.
if (![GULAppEnvironmentUtil isAppExtension]) {
Class cls = NSClassFromString(@"UIApplication");
if (cls && [cls respondsToSelector:NSSelectorFromString(@"sharedApplication")]) {
if (cls && [cls respondsToSelector:@selector(sharedApplication)]) {
applicationClass = cls;
}
}
UIApplication *application = [applicationClass sharedApplication];

[GULAppDelegateSwizzler proxyOriginalDelegateIncludingAPNSMethods];
[GULSceneDelegateSwizzler proxyOriginalSceneDelegate];
#endif // TARGET_OS_IOS

// Continue with the rest of initialization in the work thread.
__weak FIRAuth *weakSelf = self;
dispatch_async(FIRAuthGlobalWorkQueue(), ^{
// Load current user from Keychain.
FIRAuth *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
NSString *keychainServiceName =
[FIRAuth keychainServiceNameForAppName:strongSelf->_firebaseAppName];
if (keychainServiceName) {
strongSelf->_keychainServices =
[[FIRAuthKeychainServices alloc] initWithService:keychainServiceName];
strongSelf.storedUserManager =
[[FIRAuthStoredUserManager alloc] initWithServiceName:keychainServiceName];
if ([application respondsToSelector:@selector(isProtectedDataAvailable)]) {
if ([application isProtectedDataAvailable]) {
[self protectedDataInitialization];
} else {
// Add listener for UIApplicationProtectedDataDidBecomeAvailable.
self->_protectedDataDidBecomeAvailableObserver = [[NSNotificationCenter defaultCenter]
addObserverForName:UIApplicationProtectedDataDidBecomeAvailable
object:nil
queue:nil
usingBlock:^(NSNotification *notification) {
[self protectedDataInitialization];
}];
}
} else {
[self protectedDataInitialization];
}
#else
[self protectedDataInitialization];
#endif // TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_MACCATALYST
}
return self;
}

NSError *error;
NSString *storedUserAccessGroup =
[strongSelf.storedUserManager getStoredUserAccessGroupWithError:&error];
if (!error) {
if (!storedUserAccessGroup) {
FIRUser *user;
if ([strongSelf getUser:&user error:&error]) {
strongSelf.tenantID = user.tenantID;
[strongSelf updateCurrentUser:user byForce:NO savingToDisk:NO error:&error];
self->_lastNotifiedUserToken = user.rawAccessToken;
} else {
FIRLogError(kFIRLoggerAuth, @"I-AUT000001",
@"Error loading saved user when starting up: %@", error);
}
- (void)protectedDataInitialization {
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_MACCATALYST
[[NSNotificationCenter defaultCenter] removeObserver:_protectedDataDidBecomeAvailableObserver
name:UIApplicationProtectedDataDidBecomeAvailable
object:nil];
#endif // TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_MACCATALYST
// Continue with the rest of initialization in the work thread.
__weak FIRAuth *weakSelf = self;
dispatch_async(FIRAuthGlobalWorkQueue(), ^{
// Load current user from Keychain.
FIRAuth *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
NSString *keychainServiceName =
[FIRAuth keychainServiceNameForAppName:strongSelf->_firebaseAppName];
if (keychainServiceName) {
strongSelf->_keychainServices =
[[FIRAuthKeychainServices alloc] initWithService:keychainServiceName];
strongSelf.storedUserManager =
[[FIRAuthStoredUserManager alloc] initWithServiceName:keychainServiceName];
}

NSError *error;
NSString *storedUserAccessGroup =
[strongSelf.storedUserManager getStoredUserAccessGroupWithError:&error];
if (!error) {
if (!storedUserAccessGroup) {
FIRUser *user;
if ([strongSelf getUser:&user error:&error]) {
strongSelf.tenantID = user.tenantID;
[strongSelf updateCurrentUser:user byForce:NO savingToDisk:NO error:&error];
self->_lastNotifiedUserToken = user.rawAccessToken;
} else {
[strongSelf internalUseUserAccessGroup:storedUserAccessGroup error:&error];
if (error) {
FIRLogError(kFIRLoggerAuth, @"I-AUT000001",
@"Error loading saved user when starting up: %@", error);
}
FIRLogError(kFIRLoggerAuth, @"I-AUT000001",
@"Error loading saved user when starting up: %@", error);
}
} else {
FIRLogError(kFIRLoggerAuth, @"I-AUT000001",
@"Error loading saved user when starting up: %@", error);
[strongSelf internalUseUserAccessGroup:storedUserAccessGroup error:&error];
if (error) {
FIRLogError(kFIRLoggerAuth, @"I-AUT000001",
@"Error loading saved user when starting up: %@", error);
}
}
} else {
FIRLogError(kFIRLoggerAuth, @"I-AUT000001", @"Error loading saved user when starting up: %@",
error);
}

#if TARGET_OS_IOS
static Class applicationClass = nil;
// iOS App extensions should not call [UIApplication sharedApplication], even if UIApplication
// responds to it.
if (![GULAppEnvironmentUtil isAppExtension]) {
Class cls = NSClassFromString(@"UIApplication");
if (cls && [cls respondsToSelector:@selector(sharedApplication)]) {
applicationClass = cls;
}
}
UIApplication *application = [applicationClass sharedApplication];

if (application) {
// Initialize for phone number auth.
strongSelf->_tokenManager = [[FIRAuthAPNSTokenManager alloc] initWithApplication:application];

Expand All @@ -553,17 +601,16 @@ - (nullable instancetype)initWithAPIKey:(NSString *)APIKey
strongSelf->_notificationManager = [[FIRAuthNotificationManager alloc]
initWithApplication:application
appCredentialManager:strongSelf->_appCredentialManager];
}

[GULAppDelegateSwizzler registerAppDelegateInterceptor:strongSelf];
[GULAppDelegateSwizzler registerAppDelegateInterceptor:strongSelf];
#if ((TARGET_OS_IOS || TARGET_OS_TV) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 130000))
if (@available(iOS 13, tvos 13, *)) {
[GULSceneDelegateSwizzler registerSceneDelegateInterceptor:strongSelf];
}
if (@available(iOS 13, tvos 13, *)) {
[GULSceneDelegateSwizzler registerSceneDelegateInterceptor:strongSelf];
}
#endif // ((TARGET_OS_IOS || TARGET_OS_TV) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 130000))
#endif // TARGET_OS_IOS
});
}
return self;
});
}

- (void)dealloc {
Expand Down

0 comments on commit dc45185

Please sign in to comment.