Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 32 additions & 12 deletions Example/Auth/Sample/SettingsViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@
@"GoogleService-Info_multi"
};

/** @var kSharedKeychainAccessGroup
@brief The shared keychain access group for testing.
*/
static NSString *const kSharedKeychainAccessGroup = @"com.google.firebase.auth.keychainGroup1";

/** @var gAPIEndpoints
@brief List of API Hosts by request class name.
*/
Expand Down Expand Up @@ -155,6 +160,9 @@ - (void)setUpFirebaseAppOptions {
}

- (void)loadTableView {
NSString *appIdentifierPrefix = NSBundle.mainBundle.infoDictionary[@"AppIdentifierPrefix"];
NSString *fullKeychainAccessGroup = [appIdentifierPrefix stringByAppendingString:kSharedKeychainAccessGroup];

__weak typeof(self) weakSelf = self;
_tableViewManager.contents = [StaticContentTableViewContent contentWithSections:@[
[StaticContentTableViewSection sectionWithTitle:@"Versions" cells:@[
Expand Down Expand Up @@ -191,6 +199,23 @@ - (void)loadTableView {
[weakSelf toggleProjectForAppAtIndex:1];
}],
]],
[StaticContentTableViewSection sectionWithTitle:@"Keychain Access Groups" cells:@[
[StaticContentTableViewCell cellWithTitle:@"Current Access Group"
value:[AppManager auth].userAccessGroup ? [AppManager auth].userAccessGroup : @"[none]"
],
[StaticContentTableViewCell cellWithTitle:@"Default Group"
value:@"[none]"
action:^{
[[AppManager auth] useUserAccessGroup:nil error:nil];
[self loadTableView];
}],
[StaticContentTableViewCell cellWithTitle:@"Shared Group"
value:fullKeychainAccessGroup
action:^{
[[AppManager auth] useUserAccessGroup:fullKeychainAccessGroup error:nil];
[self loadTableView];
}],
]],
[StaticContentTableViewSection sectionWithTitle:@"Phone Auth" cells:@[
[StaticContentTableViewCell cellWithTitle:@"APNs Token"
value:[self APNSTokenString]
Expand Down Expand Up @@ -281,19 +306,14 @@ - (NSString *)projectIDForAppAtIndex:(int)index {
return @"[none]";
}

/** @fn googleAppIDForAppAtIndex:
@brief Returns the Google App ID for the Firebase app at the given index.
@param index The index for the app in the app manager.
@return The Google App ID of the project.
/** @fn projectIDForAppAtIndex:
@brief Returns the Firebase project ID for the Firebase app at the given index.
@param index The index for the app in the app manager.
@return The ID of the project.
*/
- (NSString *)googleAppIDForAppAtIndex:(int)index {
NSString *APIKey = [[AppManager sharedInstance] appAtIndex:index].options.APIKey;
for (FIROptions *options in gFirebaseAppOptions) {
if ([options.APIKey isEqualToString:APIKey]) {
return options.googleAppID;
}
}
return @"[none]";
- (NSString *)keychainAccessGroupAtIndex:(int)index {
NSArray *array = @[@"123", @"456"];
return array[index];
}

/** @fn toggleProjectForAppAtIndex:
Expand Down
4 changes: 0 additions & 4 deletions Example/Auth/Tests/FIRAuthUserDefaultsStorageTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@

#import "FIRAuthUserDefaultsStorage.h"

#if FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE

NS_ASSUME_NONNULL_BEGIN

/** @var kKey
Expand Down Expand Up @@ -151,5 +149,3 @@ - (void)testStandardUserDefaults {
@end

NS_ASSUME_NONNULL_END

#endif // FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE
174 changes: 141 additions & 33 deletions Firebase/Auth/Source/FIRAuth.m
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#import "FIRAuthKeychain.h"
#import "FIRAuthOperationType.h"
#import "FIRAuthSettings.h"
#import "FIRAuthStoredUserManager.h"
#import "FIRUser_Internal.h"
#import "FirebaseAuth.h"
#import "FIRAuthBackend.h"
Expand Down Expand Up @@ -245,6 +246,11 @@ @interface FIRAuth () <FIRLibrary, FIRComponentLifecycleMaintainer>
*/
@property(nonatomic, copy, nullable) NSString *additionalFrameworkMarker;

/** @property storedUserManager
@brief The stored user manager.
*/
@property(nonatomic, strong, nullable) FIRAuthStoredUserManager *storedUserManager;

/** @fn initWithApp:
@brief Creates a @c FIRAuth instance associated with the provided @c FIRApp instance.
@param app The application to associate the auth instance with.
Expand Down Expand Up @@ -390,11 +396,29 @@ - (nullable instancetype)initWithAPIKey:(NSString *)APIKey appName:(NSString *)a
if (keychainServiceName) {
strongSelf->_keychain = [[FIRAuthKeychain alloc] initWithService:keychainServiceName];
}
FIRUser *user;

strongSelf.storedUserManager =
[[FIRAuthStoredUserManager alloc] initWithServiceName:keychainServiceName];

NSError *error;
if ([strongSelf getUser:&user error:&error]) {
[strongSelf updateCurrentUser:user byForce:NO savingToDisk:NO error:&error];
self->_lastNotifiedUserToken = user.rawAccessToken;
NSString *storedUserAccessGroup = [strongSelf.storedUserManager getStoredUserAccessGroupWithError:&error];
if (!error) {
if (!storedUserAccessGroup) {
FIRUser *user;
if ([strongSelf getUser:&user error:&error]) {
[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);
}
} else {
[strongSelf useUserAccessGroup: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);
Expand Down Expand Up @@ -1855,26 +1879,40 @@ - (BOOL)updateCurrentUser:(nullable FIRUser *)user
/** @fn saveUser:error:
@brief Persists user.
@param user The user to save.
@param error Return value for any error which occurs.
@param outError Return value for any error which occurs.
@return @YES on success, @NO otherwise.
*/
- (BOOL)saveUser:(FIRUser *)user
error:(NSError *_Nullable *_Nullable)error {
error:(NSError *_Nullable *_Nullable)outError {
BOOL success;
NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName];

if (!user) {
success = [_keychain removeDataForKey:userKey error:error];
if (!self.userAccessGroup) {
NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName];
if (!user) {
success = [_keychain removeDataForKey:userKey error:outError];
} else {
// Encode the user object.
NSMutableData *archiveData = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:archiveData];
[archiver encodeObject:user forKey:userKey];
[archiver finishEncoding];

// Save the user object's encoded value.
success = [_keychain setData:archiveData forKey:userKey error:outError];
}
} else {
// Encode the user object.
NSMutableData *archiveData = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:archiveData];
[archiver encodeObject:user forKey:userKey];
[archiver finishEncoding];

// Save the user object's encoded value.
success = [_keychain setData:archiveData forKey:userKey error:error];
if (!user) {
success = [self.storedUserManager removeStoredUserForAccessGroup:self.userAccessGroup
projectIdentifier:self.app.options.APIKey
error:outError];
} else {
success = [self.storedUserManager setStoredUser:user
forAccessGroup:self.userAccessGroup
projectIdentifier:self.app.options.APIKey
error:outError];
}
}

return success;
}

Expand All @@ -1887,26 +1925,44 @@ - (BOOL)saveUser:(FIRUser *)user
*/
- (BOOL)getUser:(FIRUser *_Nullable *)outUser
error:(NSError *_Nullable *_Nullable)error {
NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName];
if (!self.userAccessGroup) {
NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName];

NSError *keychainError;
NSData *encodedUserData = [_keychain dataForKey:userKey error:&keychainError];
if (keychainError) {
if (error) {
*error = keychainError;
NSError *keychainError;
NSData *encodedUserData = [_keychain dataForKey:userKey error:&keychainError];
if (keychainError) {
if (error) {
*error = keychainError;
}
return NO;
}
return NO;
}
if (!encodedUserData) {
*outUser = nil;
if (!encodedUserData) {
*outUser = nil;
return YES;
}
NSKeyedUnarchiver *unarchiver =
[[NSKeyedUnarchiver alloc] initForReadingWithData:encodedUserData];
FIRUser *user = [unarchiver decodeObjectOfClass:[FIRUser class] forKey:userKey];
user.auth = self;
*outUser = user;

return YES;
} else {
FIRUser *user = [self.storedUserManager getStoredUserForAccessGroup:self.userAccessGroup
projectIdentifier:self.app.options.APIKey
error:error];
user.auth = self;
*outUser = user;
if (user) {
return YES;
} else {
if (error && *error) {
return NO;
} else {
return YES;
}
}
}
NSKeyedUnarchiver *unarchiver =
[[NSKeyedUnarchiver alloc] initForReadingWithData:encodedUserData];
FIRUser *user = [unarchiver decodeObjectOfClass:[FIRUser class] forKey:userKey];
user.auth = self;
*outUser = user;
return YES;
}

#pragma mark - Interoperability
Expand Down Expand Up @@ -2011,6 +2067,58 @@ - (nullable NSString *)getUserID {
return _currentUser.uid;
}

#pragma mark - Keychain sharing

- (BOOL)useUserAccessGroup:(NSString *_Nullable)accessGroup
error:(NSError *_Nullable *_Nullable)outError {
BOOL success;
success = [self.storedUserManager setStoredUserAccessGroup:accessGroup error:outError];
if (!success) {
return NO;
}

FIRUser *user = [self getStoredUserForAccessGroup:accessGroup error:outError];
if (!user && outError && *outError) {
return NO;
}
success = [self updateCurrentUser:user byForce:NO savingToDisk:NO error:outError];
if (!success) {
return NO;
}

if(_userAccessGroup == nil && accessGroup != nil) {
NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName];
[_keychain removeDataForKey:userKey error:outError];
}
_userAccessGroup = accessGroup;
self->_lastNotifiedUserToken = user.rawAccessToken;

return YES;
}

- (FIRUser *)getStoredUserForAccessGroup:(NSString *_Nullable)accessGroup
error:(NSError *_Nullable *_Nullable)outError {
FIRUser *user;
if (!accessGroup) {
NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName];
NSData *encodedUserData = [_keychain dataForKey:userKey error:outError];
if (!encodedUserData) {
return nil;
}

NSKeyedUnarchiver *unarchiver =
[[NSKeyedUnarchiver alloc] initForReadingWithData:encodedUserData];
user = [unarchiver decodeObjectOfClass:[FIRUser class] forKey:userKey];
user.auth = self;
} else {
user = [self.storedUserManager getStoredUserForAccessGroup:self.userAccessGroup
projectIdentifier:self.app.options.APIKey
error:outError];
}

return user;
}

@end

NS_ASSUME_NONNULL_END
27 changes: 27 additions & 0 deletions Firebase/Auth/Source/FIRAuthKeychain.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,33 @@ NS_ASSUME_NONNULL_BEGIN
@brief The utility class to manipulate data in iOS Keychain.
*/
@interface FIRAuthKeychain : NSObject <FIRAuthStorage>

/** @fn getItemWithQuery:error:
@brief Get the item from keychain by given query.
@param query The query to query the keychain.
@param outError The address to store any error that occurs during the process, if not nil.
@return The item of the given query. nil if not exsit.
*/
- (NSData *)getItemWithQuery:(NSDictionary *)query error:(NSError *_Nullable *_Nullable)outError;

/** @fn setItem:withQuery:error:
@brief Set the item into keychain with given query.
@param item The item to be added into keychain.
@param query The query to query the keychain.
@param outError The address to store any error that occurs during the process, if not nil.
@return Whether the operation succeed.
*/
- (BOOL)setItem:(NSData *)item withQuery:(NSDictionary *)query
error:(NSError *_Nullable *_Nullable)outError;

/** @fn getItemWithQuery:error:
@brief Remove the item with given queryfrom keychain.
@param query The query to query the keychain.
@param outError The address to store any error that occurs during the process, if not nil.
@return Whether the operation succeed.
*/
- (BOOL)removeItemWithQuery:(NSDictionary *)query error:(NSError *_Nullable *_Nullable)outError;

@end

NS_ASSUME_NONNULL_END
Loading