FIRAuthAppCredentialManager.m 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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 "FIRAuthAppCredentialManager.h"
  19. #import "FIRAuthAppCredential.h"
  20. #import "FIRAuthGlobalWorkQueue.h"
  21. #import "FIRAuthKeychainServices.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. NSKeyedUnarchiver *unarchiver =
  62. [[NSKeyedUnarchiver alloc] initForReadingWithData:encodedData];
  63. FIRAuthAppCredential *credential =
  64. [unarchiver decodeObjectOfClass:[FIRAuthAppCredential class] forKey:kFullCredentialKey];
  65. if ([credential isKindOfClass:[FIRAuthAppCredential class]]) {
  66. _credential = credential;
  67. }
  68. NSSet<Class> *allowedClasses =
  69. [NSSet<Class> setWithObjects:[NSArray class], [NSString class], nil];
  70. NSArray<NSString *> *pendingReceipts = [unarchiver decodeObjectOfClasses:allowedClasses
  71. forKey:kPendingReceiptsKey];
  72. if ([pendingReceipts isKindOfClass:[NSArray class]]) {
  73. _pendingReceipts = [pendingReceipts mutableCopy];
  74. }
  75. }
  76. if (!_pendingReceipts) {
  77. _pendingReceipts = [[NSMutableArray<NSString *> alloc] init];
  78. }
  79. _callbacksByReceipt =
  80. [[NSMutableDictionary<NSString *, FIRAuthAppCredentialCallback> alloc] init];
  81. }
  82. return self;
  83. }
  84. - (NSUInteger)maximumNumberOfPendingReceipts {
  85. return kMaximumNumberOfPendingReceipts;
  86. }
  87. - (void)didStartVerificationWithReceipt:(NSString *)receipt
  88. timeout:(NSTimeInterval)timeout
  89. callback:(FIRAuthAppCredentialCallback)callback {
  90. [_pendingReceipts removeObject:receipt];
  91. if (_pendingReceipts.count >= kMaximumNumberOfPendingReceipts) {
  92. [_pendingReceipts removeObjectAtIndex:0];
  93. }
  94. [_pendingReceipts addObject:receipt];
  95. _callbacksByReceipt[receipt] = callback;
  96. [self saveData];
  97. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)),
  98. FIRAuthGlobalWorkQueue(), ^{
  99. [self callBackWithReceipt:receipt];
  100. });
  101. }
  102. - (BOOL)canFinishVerificationWithReceipt:(NSString *)receipt secret:(NSString *)secret {
  103. if (![_pendingReceipts containsObject:receipt]) {
  104. return NO;
  105. }
  106. [_pendingReceipts removeObject:receipt];
  107. _credential = [[FIRAuthAppCredential alloc] initWithReceipt:receipt secret:secret];
  108. [self saveData];
  109. [self callBackWithReceipt:receipt];
  110. return YES;
  111. }
  112. - (void)clearCredential {
  113. _credential = nil;
  114. [self saveData];
  115. }
  116. #pragma mark - Internal methods
  117. /** @fn saveData
  118. @brief Save the data in memory to the keychain ignoring any errors.
  119. */
  120. - (void)saveData {
  121. NSMutableData *archiveData = [NSMutableData data];
  122. NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:archiveData];
  123. [archiver encodeObject:_credential forKey:kFullCredentialKey];
  124. [archiver encodeObject:_pendingReceipts forKey:kPendingReceiptsKey];
  125. [archiver finishEncoding];
  126. [_keychainServices setData:archiveData forKey:kKeychainDataKey error:NULL];
  127. }
  128. /** @fn callBackWithReceipt:
  129. @brief Calls the saved callback for the specifc receipt.
  130. @param receipt The receipt associated with the callback.
  131. */
  132. - (void)callBackWithReceipt:(NSString *)receipt {
  133. FIRAuthAppCredentialCallback callback = _callbacksByReceipt[receipt];
  134. if (!callback) {
  135. return;
  136. }
  137. [_callbacksByReceipt removeObjectForKey:receipt];
  138. if (_credential) {
  139. callback(_credential);
  140. } else {
  141. callback([[FIRAuthAppCredential alloc] initWithReceipt:receipt secret:nil]);
  142. }
  143. }
  144. @end
  145. NS_ASSUME_NONNULL_END
  146. #endif