FIRAuthStoredUserManager.m 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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)getStoredUserAccessGroup {
  43. NSData *data = [self.userDefaults dataForKey:kStoredUserAccessGroupKey error:NULL];
  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. NSData *data = [accessGroup dataUsingEncoding:NSUTF8StringEncoding];
  53. if (!data) {
  54. return [self.userDefaults removeDataForKey:kStoredUserAccessGroupKey error:NULL];
  55. } else {
  56. return [self.userDefaults setData:data forKey:kStoredUserAccessGroupKey error:NULL];
  57. }
  58. }
  59. #pragma mark - User for Access Group
  60. - (FIRUser *)getStoredUserForAccessGroup:(NSString *)accessGroup
  61. shareAuthStateAcrossDevices:(BOOL)shareAuthStateAcrossDevices
  62. projectIdentifier:(NSString *)projectIdentifier
  63. error:(NSError *_Nullable *_Nullable)outError {
  64. NSMutableDictionary *query = [self keychainQueryForAccessGroup:accessGroup
  65. shareAuthStateAcrossDevices:shareAuthStateAcrossDevices
  66. projectIdentifier:projectIdentifier];
  67. NSData *data = [self.keychainServices getItemWithQuery:query error:outError];
  68. // If there's an outError parameter and it's populated, or there's no data, return.
  69. if ((outError && *outError) || !data) {
  70. return nil;
  71. }
  72. #if TARGET_OS_WATCH
  73. NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:data
  74. error:outError];
  75. if (outError && *outError) {
  76. return nil;
  77. }
  78. #else
  79. // iOS 12 deprecation
  80. #pragma clang diagnostic push
  81. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  82. NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
  83. #pragma clang diagnostic pop
  84. #endif // TARGET_OS_WATCH
  85. FIRUser *user = [unarchiver decodeObjectOfClass:[FIRUser class] forKey:kStoredUserCoderKey];
  86. return user;
  87. }
  88. - (BOOL)setStoredUser:(FIRUser *)user
  89. forAccessGroup:(NSString *)accessGroup
  90. shareAuthStateAcrossDevices:(BOOL)shareAuthStateAcrossDevices
  91. projectIdentifier:(NSString *)projectIdentifier
  92. error:(NSError *_Nullable *_Nullable)outError {
  93. NSMutableDictionary *query = [self keychainQueryForAccessGroup:accessGroup
  94. shareAuthStateAcrossDevices:shareAuthStateAcrossDevices
  95. projectIdentifier:projectIdentifier];
  96. if (shareAuthStateAcrossDevices) {
  97. query[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlock;
  98. } else {
  99. query[(__bridge id)kSecAttrAccessible] =
  100. (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
  101. }
  102. #if TARGET_OS_WATCH
  103. NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:false];
  104. #else
  105. NSMutableData *data = [NSMutableData data];
  106. // iOS 12 deprecation
  107. #pragma clang diagnostic push
  108. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  109. NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
  110. #pragma clang diagnostic pop
  111. #endif // TARGET_OS_WATCH
  112. [archiver encodeObject:user forKey:kStoredUserCoderKey];
  113. [archiver finishEncoding];
  114. #if TARGET_OS_WATCH
  115. NSData *data = archiver.encodedData;
  116. #endif // TARGET_OS_WATCH
  117. return [self.keychainServices setItem:data withQuery:query error:outError];
  118. }
  119. - (BOOL)removeStoredUserForAccessGroup:(NSString *)accessGroup
  120. shareAuthStateAcrossDevices:(BOOL)shareAuthStateAcrossDevices
  121. projectIdentifier:(NSString *)projectIdentifier
  122. error:(NSError *_Nullable *_Nullable)outError {
  123. NSMutableDictionary *query = [self keychainQueryForAccessGroup:accessGroup
  124. shareAuthStateAcrossDevices:shareAuthStateAcrossDevices
  125. projectIdentifier:projectIdentifier];
  126. if (shareAuthStateAcrossDevices) {
  127. query[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlock;
  128. } else {
  129. query[(__bridge id)kSecAttrAccessible] =
  130. (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
  131. }
  132. return [self.keychainServices removeItemWithQuery:query error:outError];
  133. }
  134. #pragma mark - Internal Methods
  135. - (NSMutableDictionary *)keychainQueryForAccessGroup:(NSString *)accessGroup
  136. shareAuthStateAcrossDevices:(BOOL)shareAuthStateAcrossDevices
  137. projectIdentifier:(NSString *)projectIdentifier {
  138. NSMutableDictionary *query = [[NSMutableDictionary alloc] init];
  139. query[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword;
  140. query[(__bridge id)kSecAttrAccessGroup] = accessGroup;
  141. query[(__bridge id)kSecAttrService] = projectIdentifier;
  142. query[(__bridge id)kSecAttrAccount] = kSharedKeychainAccountValue;
  143. if (@available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 6.0, *)) {
  144. /*
  145. "The data protection key affects operations only in macOS.
  146. Other platforms automatically behave as if the key is set to true,
  147. and ignore the key in the query dictionary. You can safely use the key on all platforms."
  148. [kSecUseDataProtectionKeychain](https://developer.apple.com/documentation/security/ksecusedataprotectionkeychain)
  149. */
  150. query[(__bridge id)kSecUseDataProtectionKeychain] = (__bridge id)kCFBooleanTrue;
  151. }
  152. if (shareAuthStateAcrossDevices) {
  153. query[(__bridge id)kSecAttrSynchronizable] = (__bridge id)kCFBooleanTrue;
  154. }
  155. return query;
  156. }
  157. @end