FIRAuthStoredUserManager.m 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. /*
  2. * Copyright 2019 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "FirebaseAuth/Sources/SystemService/FIRAuthStoredUserManager.h"
  17. #import "FirebaseAuth/Sources/User/FIRUser_Internal.h"
  18. /** @var kUserAccessGroupKey
  19. @brief Key of user access group stored in user defaults. Used for retrieve the user access
  20. group at launch.
  21. */
  22. static NSString *kStoredUserAccessGroupKey = @"firebase_auth_stored_user_access_group";
  23. /** @var kSharedKeychainAccountValue
  24. @brief Default value for kSecAttrAccount of shared keychain items.
  25. */
  26. static NSString *kSharedKeychainAccountValue = @"firebase_auth_firebase_user";
  27. /** @var kStoredUserCoderKey
  28. @brief The key to encode and decode the stored user.
  29. */
  30. static NSString *kStoredUserCoderKey = @"firebase_auth_stored_user_coder_key";
  31. @implementation FIRAuthStoredUserManager
  32. #pragma mark - Initializers
  33. - (instancetype)initWithServiceName:(NSString *)serviceName {
  34. self = [super init];
  35. if (self) {
  36. _keychainServices = [[FIRAuthKeychainServices alloc] initWithService:serviceName];
  37. _userDefaults = [[FIRAuthUserDefaults alloc] initWithService:serviceName];
  38. }
  39. return self;
  40. }
  41. #pragma mark - User Access Group
  42. - (NSString *_Nullable)getStoredUserAccessGroupWithError:(NSError *_Nullable *_Nullable)outError {
  43. NSData *data = [self.userDefaults dataForKey:kStoredUserAccessGroupKey error:outError];
  44. if (data) {
  45. NSString *userAccessGroup = [NSString stringWithUTF8String:data.bytes];
  46. return userAccessGroup;
  47. } else {
  48. return nil;
  49. }
  50. }
  51. - (BOOL)setStoredUserAccessGroup:(NSString *_Nullable)accessGroup
  52. error:(NSError *_Nullable *_Nullable)outError {
  53. NSData *data = [accessGroup dataUsingEncoding:NSUTF8StringEncoding];
  54. if (!data) {
  55. return [self.userDefaults removeDataForKey:kStoredUserAccessGroupKey error:outError];
  56. } else {
  57. return [self.userDefaults setData:data forKey:kStoredUserAccessGroupKey error:outError];
  58. }
  59. }
  60. #pragma mark - User for Access Group
  61. - (FIRUser *)getStoredUserForAccessGroup:(NSString *)accessGroup
  62. shareAuthStateAcrossDevices:(BOOL)shareAuthStateAcrossDevices
  63. projectIdentifier:(NSString *)projectIdentifier
  64. error:(NSError *_Nullable *_Nullable)outError {
  65. NSMutableDictionary *query = [self keychainQueryForAccessGroup:accessGroup
  66. shareAuthStateAcrossDevices:shareAuthStateAcrossDevices
  67. projectIdentifier:projectIdentifier];
  68. NSData *data = [self.keychainServices getItemWithQuery:query error:outError];
  69. // If there's an outError parameter and it's populated, or there's no data, return.
  70. if ((outError && *outError) || !data) {
  71. return nil;
  72. }
  73. #if TARGET_OS_WATCH
  74. NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:data
  75. error:outError];
  76. if (outError && *outError) {
  77. return nil;
  78. }
  79. #else
  80. // iOS 12 deprecation
  81. #pragma clang diagnostic push
  82. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  83. NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
  84. #pragma clang diagnostic pop
  85. #endif // TARGET_OS_WATCH
  86. FIRUser *user = [unarchiver decodeObjectOfClass:[FIRUser class] forKey:kStoredUserCoderKey];
  87. return user;
  88. }
  89. - (BOOL)setStoredUser:(FIRUser *)user
  90. forAccessGroup:(NSString *)accessGroup
  91. shareAuthStateAcrossDevices:(BOOL)shareAuthStateAcrossDevices
  92. projectIdentifier:(NSString *)projectIdentifier
  93. error:(NSError *_Nullable *_Nullable)outError {
  94. NSMutableDictionary *query = [self keychainQueryForAccessGroup:accessGroup
  95. shareAuthStateAcrossDevices:shareAuthStateAcrossDevices
  96. projectIdentifier:projectIdentifier];
  97. if (shareAuthStateAcrossDevices) {
  98. query[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlock;
  99. } else {
  100. query[(__bridge id)kSecAttrAccessible] =
  101. (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
  102. }
  103. #if TARGET_OS_WATCH
  104. NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:false];
  105. #else
  106. NSMutableData *data = [NSMutableData data];
  107. // iOS 12 deprecation
  108. #pragma clang diagnostic push
  109. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  110. NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
  111. #pragma clang diagnostic pop
  112. #endif // TARGET_OS_WATCH
  113. [archiver encodeObject:user forKey:kStoredUserCoderKey];
  114. [archiver finishEncoding];
  115. #if TARGET_OS_WATCH
  116. NSData *data = archiver.encodedData;
  117. #endif // TARGET_OS_WATCH
  118. return [self.keychainServices setItem:data withQuery:query error:outError];
  119. }
  120. - (BOOL)removeStoredUserForAccessGroup:(NSString *)accessGroup
  121. shareAuthStateAcrossDevices:(BOOL)shareAuthStateAcrossDevices
  122. projectIdentifier:(NSString *)projectIdentifier
  123. error:(NSError *_Nullable *_Nullable)outError {
  124. NSMutableDictionary *query = [self keychainQueryForAccessGroup:accessGroup
  125. shareAuthStateAcrossDevices:shareAuthStateAcrossDevices
  126. projectIdentifier:projectIdentifier];
  127. if (shareAuthStateAcrossDevices) {
  128. query[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlock;
  129. } else {
  130. query[(__bridge id)kSecAttrAccessible] =
  131. (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
  132. }
  133. return [self.keychainServices removeItemWithQuery:query error:outError];
  134. }
  135. #pragma mark - Internal Methods
  136. - (NSMutableDictionary *)keychainQueryForAccessGroup:(NSString *)accessGroup
  137. shareAuthStateAcrossDevices:(BOOL)shareAuthStateAcrossDevices
  138. projectIdentifier:(NSString *)projectIdentifier {
  139. NSMutableDictionary *query = [[NSMutableDictionary alloc] init];
  140. query[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword;
  141. query[(__bridge id)kSecAttrAccessGroup] = accessGroup;
  142. query[(__bridge id)kSecAttrService] = projectIdentifier;
  143. query[(__bridge id)kSecAttrAccount] = kSharedKeychainAccountValue;
  144. if (@available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 6.0, *)) {
  145. /*
  146. "The data protection key affects operations only in macOS.
  147. Other platforms automatically behave as if the key is set to true,
  148. and ignore the key in the query dictionary. You can safely use the key on all platforms."
  149. [kSecUseDataProtectionKeychain](https://developer.apple.com/documentation/security/ksecusedataprotectionkeychain)
  150. */
  151. query[(__bridge id)kSecUseDataProtectionKeychain] = (__bridge id)kCFBooleanTrue;
  152. }
  153. if (shareAuthStateAcrossDevices) {
  154. query[(__bridge id)kSecAttrSynchronizable] = (__bridge id)kCFBooleanTrue;
  155. }
  156. return query;
  157. }
  158. @end