FIRAuthAppCredentialManager.m 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /*
  2. * Copyright 2017 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. #include <TargetConditionals.h>
  17. #if !TARGET_OS_OSX
  18. #import "FirebaseAuth/Sources/Auth/FIRAuthGlobalWorkQueue.h"
  19. #import "FirebaseAuth/Sources/Storage/FIRAuthKeychainServices.h"
  20. #import "FirebaseAuth/Sources/SystemService/FIRAuthAppCredential.h"
  21. #import "FirebaseAuth/Sources/SystemService/FIRAuthAppCredentialManager.h"
  22. NS_ASSUME_NONNULL_BEGIN
  23. /** @var kKeychainDataKey
  24. @brief The keychain key for the data.
  25. */
  26. static NSString *const kKeychainDataKey = @"app_credentials";
  27. /** @var kFullCredentialKey
  28. @brief The data key for the full app credential.
  29. */
  30. static NSString *const kFullCredentialKey = @"full_credential";
  31. /** @var kPendingReceiptsKey
  32. @brief The data key for the array of pending receipts.
  33. */
  34. static NSString *const kPendingReceiptsKey = @"pending_receipts";
  35. /** @var kMaximumNumberOfPendingReceipts
  36. @brief The maximum number of partial credentials kept by this class.
  37. */
  38. static const NSUInteger kMaximumNumberOfPendingReceipts = 32;
  39. @implementation FIRAuthAppCredentialManager {
  40. /** @var _keychainServices
  41. @brief The keychain for app credentials to load from and to save to.
  42. */
  43. FIRAuthKeychainServices *_keychainServices;
  44. /** @var _pendingReceipts
  45. @brief A list of pending receipts sorted in the order they were recorded.
  46. */
  47. NSMutableArray<NSString *> *_pendingReceipts;
  48. /** @var _callbacksByReceipt
  49. @brief A map from pending receipts to callbacks.
  50. */
  51. NSMutableDictionary<NSString *, FIRAuthAppCredentialCallback> *_callbacksByReceipt;
  52. }
  53. - (instancetype)initWithKeychain:(FIRAuthKeychainServices *)keychain {
  54. self = [super init];
  55. if (self) {
  56. _keychainServices = keychain;
  57. // Load the credentials from keychain if possible.
  58. NSError *error;
  59. NSData *encodedData = [_keychainServices dataForKey:kKeychainDataKey error:&error];
  60. if (!error && encodedData) {
  61. // iOS 12 deprecation
  62. #pragma clang diagnostic push
  63. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  64. NSKeyedUnarchiver *unarchiver =
  65. [[NSKeyedUnarchiver alloc] initForReadingWithData:encodedData];
  66. #pragma clang diagnostic pop
  67. FIRAuthAppCredential *credential =
  68. [unarchiver decodeObjectOfClass:[FIRAuthAppCredential class] forKey:kFullCredentialKey];
  69. if ([credential isKindOfClass:[FIRAuthAppCredential class]]) {
  70. _credential = credential;
  71. }
  72. NSSet<Class> *allowedClasses =
  73. [NSSet<Class> setWithObjects:[NSArray class], [NSString class], nil];
  74. NSArray<NSString *> *pendingReceipts = [unarchiver decodeObjectOfClasses:allowedClasses
  75. forKey:kPendingReceiptsKey];
  76. if ([pendingReceipts isKindOfClass:[NSArray class]]) {
  77. _pendingReceipts = [pendingReceipts mutableCopy];
  78. }
  79. }
  80. if (!_pendingReceipts) {
  81. _pendingReceipts = [[NSMutableArray<NSString *> alloc] init];
  82. }
  83. _callbacksByReceipt =
  84. [[NSMutableDictionary<NSString *, FIRAuthAppCredentialCallback> alloc] init];
  85. }
  86. return self;
  87. }
  88. - (NSUInteger)maximumNumberOfPendingReceipts {
  89. return kMaximumNumberOfPendingReceipts;
  90. }
  91. - (void)didStartVerificationWithReceipt:(NSString *)receipt
  92. timeout:(NSTimeInterval)timeout
  93. callback:(FIRAuthAppCredentialCallback)callback {
  94. [_pendingReceipts removeObject:receipt];
  95. if (_pendingReceipts.count >= kMaximumNumberOfPendingReceipts) {
  96. [_pendingReceipts removeObjectAtIndex:0];
  97. }
  98. [_pendingReceipts addObject:receipt];
  99. _callbacksByReceipt[receipt] = callback;
  100. [self saveData];
  101. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)),
  102. FIRAuthGlobalWorkQueue(), ^{
  103. [self callBackWithReceipt:receipt];
  104. });
  105. }
  106. - (BOOL)canFinishVerificationWithReceipt:(NSString *)receipt secret:(NSString *)secret {
  107. if (![_pendingReceipts containsObject:receipt]) {
  108. return NO;
  109. }
  110. [_pendingReceipts removeObject:receipt];
  111. _credential = [[FIRAuthAppCredential alloc] initWithReceipt:receipt secret:secret];
  112. [self saveData];
  113. [self callBackWithReceipt:receipt];
  114. return YES;
  115. }
  116. - (void)clearCredential {
  117. _credential = nil;
  118. [self saveData];
  119. }
  120. #pragma mark - Internal methods
  121. /** @fn saveData
  122. @brief Save the data in memory to the keychain ignoring any errors.
  123. */
  124. - (void)saveData {
  125. NSMutableData *archiveData = [NSMutableData data];
  126. // iOS 12 deprecation
  127. #pragma clang diagnostic push
  128. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  129. NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:archiveData];
  130. #pragma clang diagnostic pop
  131. [archiver encodeObject:_credential forKey:kFullCredentialKey];
  132. [archiver encodeObject:_pendingReceipts forKey:kPendingReceiptsKey];
  133. [archiver finishEncoding];
  134. [_keychainServices setData:archiveData forKey:kKeychainDataKey error:NULL];
  135. }
  136. /** @fn callBackWithReceipt:
  137. @brief Calls the saved callback for the specifc receipt.
  138. @param receipt The receipt associated with the callback.
  139. */
  140. - (void)callBackWithReceipt:(NSString *)receipt {
  141. FIRAuthAppCredentialCallback callback = _callbacksByReceipt[receipt];
  142. if (!callback) {
  143. return;
  144. }
  145. [_callbacksByReceipt removeObjectForKey:receipt];
  146. if (_credential) {
  147. callback(_credential);
  148. } else {
  149. callback([[FIRAuthAppCredential alloc] initWithReceipt:receipt secret:nil]);
  150. }
  151. }
  152. @end
  153. NS_ASSUME_NONNULL_END
  154. #endif