FIRAuth.m 97 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252
  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. #import <Foundation/Foundation.h>
  17. #import "FirebaseAuth/Sources/Auth/FIRAuth_Internal.h"
  18. #if __has_include(<UIKit/UIKit.h>)
  19. #import <UIKit/UIKit.h>
  20. #endif
  21. #import <FirebaseAuth/FirebaseAuth.h>
  22. #import <FirebaseCore/FIRAppInternal.h>
  23. #import <FirebaseCore/FIRComponent.h>
  24. #import <FirebaseCore/FIRComponentContainer.h>
  25. #import <FirebaseCore/FIRLibrary.h>
  26. #import <FirebaseCore/FIRLogger.h>
  27. #import <FirebaseCore/FIROptions.h>
  28. #import <GoogleUtilities/GULAppDelegateSwizzler.h>
  29. #import <GoogleUtilities/GULAppEnvironmentUtil.h>
  30. #import <GoogleUtilities/GULSceneDelegateSwizzler.h>
  31. #import "FirebaseAuth/Sources/Auth/FIRAuthDataResult_Internal.h"
  32. #import "FirebaseAuth/Sources/Auth/FIRAuthDispatcher.h"
  33. #import "FirebaseAuth/Sources/Auth/FIRAuthGlobalWorkQueue.h"
  34. #import "FirebaseAuth/Sources/Auth/FIRAuthOperationType.h"
  35. #import "FirebaseAuth/Sources/AuthProvider/Email/FIREmailPasswordAuthCredential.h"
  36. #import "FirebaseAuth/Sources/AuthProvider/FIRAuthCredential_Internal.h"
  37. #import "FirebaseAuth/Sources/AuthProvider/GameCenter/FIRGameCenterAuthCredential.h"
  38. #import "FirebaseAuth/Sources/AuthProvider/OAuth/FIROAuthCredential_Internal.h"
  39. #import "FirebaseAuth/Sources/Backend/FIRAuthBackend.h"
  40. #import "FirebaseAuth/Sources/Backend/FIRAuthRequestConfiguration.h"
  41. #import "FirebaseAuth/Sources/Backend/RPC/FIRCreateAuthURIRequest.h"
  42. #import "FirebaseAuth/Sources/Backend/RPC/FIRCreateAuthURIResponse.h"
  43. #import "FirebaseAuth/Sources/Backend/RPC/FIREmailLinkSignInRequest.h"
  44. #import "FirebaseAuth/Sources/Backend/RPC/FIREmailLinkSignInResponse.h"
  45. #import "FirebaseAuth/Sources/Backend/RPC/FIRGetOOBConfirmationCodeRequest.h"
  46. #import "FirebaseAuth/Sources/Backend/RPC/FIRGetOOBConfirmationCodeResponse.h"
  47. #import "FirebaseAuth/Sources/Backend/RPC/FIRResetPasswordRequest.h"
  48. #import "FirebaseAuth/Sources/Backend/RPC/FIRResetPasswordResponse.h"
  49. #import "FirebaseAuth/Sources/Backend/RPC/FIRSendVerificationCodeRequest.h"
  50. #import "FirebaseAuth/Sources/Backend/RPC/FIRSendVerificationCodeResponse.h"
  51. #import "FirebaseAuth/Sources/Backend/RPC/FIRSetAccountInfoRequest.h"
  52. #import "FirebaseAuth/Sources/Backend/RPC/FIRSetAccountInfoResponse.h"
  53. #import "FirebaseAuth/Sources/Backend/RPC/FIRSignInWithGameCenterRequest.h"
  54. #import "FirebaseAuth/Sources/Backend/RPC/FIRSignInWithGameCenterResponse.h"
  55. #import "FirebaseAuth/Sources/Backend/RPC/FIRSignUpNewUserRequest.h"
  56. #import "FirebaseAuth/Sources/Backend/RPC/FIRSignUpNewUserResponse.h"
  57. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyAssertionRequest.h"
  58. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyAssertionResponse.h"
  59. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyCustomTokenRequest.h"
  60. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyCustomTokenResponse.h"
  61. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPasswordRequest.h"
  62. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPasswordResponse.h"
  63. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPhoneNumberRequest.h"
  64. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPhoneNumberResponse.h"
  65. #import "FirebaseAuth/Sources/Storage/FIRAuthKeychainServices.h"
  66. #import "FirebaseAuth/Sources/SystemService/FIRAuthStoredUserManager.h"
  67. #import "FirebaseAuth/Sources/User/FIRAdditionalUserInfo_Internal.h"
  68. #import "FirebaseAuth/Sources/User/FIRUser_Internal.h"
  69. #import "FirebaseAuth/Sources/Utilities/FIRAuthErrorUtils.h"
  70. #import "FirebaseAuth/Sources/Utilities/FIRAuthExceptionUtils.h"
  71. #import "FirebaseAuth/Sources/Utilities/FIRAuthWebUtils.h"
  72. #if TARGET_OS_IOS
  73. #import "FirebaseAuth/Sources/AuthProvider/Phone/FIRPhoneAuthCredential_Internal.h"
  74. #import "FirebaseAuth/Sources/SystemService/FIRAuthAPNSToken.h"
  75. #import "FirebaseAuth/Sources/SystemService/FIRAuthAPNSTokenManager.h"
  76. #import "FirebaseAuth/Sources/SystemService/FIRAuthAppCredentialManager.h"
  77. #import "FirebaseAuth/Sources/SystemService/FIRAuthNotificationManager.h"
  78. #import "FirebaseAuth/Sources/Utilities/FIRAuthURLPresenter.h"
  79. #endif
  80. NS_ASSUME_NONNULL_BEGIN
  81. #pragma mark - Constants
  82. #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
  83. const NSNotificationName FIRAuthStateDidChangeNotification = @"FIRAuthStateDidChangeNotification";
  84. #else
  85. NSString *const FIRAuthStateDidChangeNotification = @"FIRAuthStateDidChangeNotification";
  86. #endif // defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
  87. /** @var kMaxWaitTimeForBackoff
  88. @brief The maximum wait time before attempting to retry auto refreshing tokens after a failed
  89. attempt.
  90. @remarks This is the upper limit (in seconds) of the exponential backoff used for retrying
  91. token refresh.
  92. */
  93. static NSTimeInterval kMaxWaitTimeForBackoff = 16 * 60;
  94. /** @var kTokenRefreshHeadStart
  95. @brief The amount of time before the token expires that proactive refresh should be attempted.
  96. */
  97. NSTimeInterval kTokenRefreshHeadStart = 5 * 60;
  98. /** @var kUserKey
  99. @brief Key of user stored in the keychain. Prefixed with a Firebase app name.
  100. */
  101. static NSString *const kUserKey = @"%@_firebase_user";
  102. /** @var kMissingEmailInvalidParameterExceptionReason
  103. @brief The reason for @c invalidParameterException when the email used to initiate password
  104. reset is nil.
  105. */
  106. static NSString *const kMissingEmailInvalidParameterExceptionReason =
  107. @"The email used to initiate password reset cannot be nil.";
  108. /** @var kHandleCodeInAppFalseExceptionReason
  109. @brief The reason for @c invalidParameterException when the handleCodeInApp parameter is false
  110. on the ActionCodeSettings object used to send the link for Email-link Authentication.
  111. */
  112. static NSString *const kHandleCodeInAppFalseExceptionReason =
  113. @"You must set handleCodeInApp in your ActionCodeSettings to true for Email-link "
  114. "Authentication.";
  115. static NSString *const kInvalidEmailSignInLinkExceptionMessage =
  116. @"The link provided is not valid for email/link sign-in. Please check the link by calling "
  117. "isSignInWithEmailLink:link: on Auth before attempting to use it for email/link sign-in.";
  118. /** @var kPasswordResetRequestType
  119. @brief The action code type value for resetting password in the check action code response.
  120. */
  121. static NSString *const kPasswordResetRequestType = @"PASSWORD_RESET";
  122. /** @var kVerifyEmailRequestType
  123. @brief The action code type value for verifying email in the check action code response.
  124. */
  125. static NSString *const kVerifyEmailRequestType = @"VERIFY_EMAIL";
  126. /** @var kRecoverEmailRequestType
  127. @brief The action code type value for recovering email in the check action code response.
  128. */
  129. static NSString *const kRecoverEmailRequestType = @"RECOVER_EMAIL";
  130. /** @var kEmailLinkSignInRequestType
  131. @brief The action code type value for an email sign-in link in the check action code response.
  132. */
  133. static NSString *const kEmailLinkSignInRequestType = @"EMAIL_SIGNIN";
  134. /** @var kVerifyAndChangeEmailRequestType
  135. @brief The action code type value for verifing and changing email in the check action code
  136. response.
  137. */
  138. static NSString *const kVerifyAndChangeEmailRequestType = @"VERIFY_AND_CHANGE_EMAIL";
  139. /** @var kRevertSecondFactorAdditionRequestType
  140. @brief The action code type value for reverting second factor addition in the check action code
  141. response.
  142. */
  143. static NSString *const kRevertSecondFactorAdditionRequestType = @"REVERT_SECOND_FACTOR_ADDITION";
  144. /** @var kMissingPasswordReason
  145. @brief The reason why the @c FIRAuthErrorCodeWeakPassword error is thrown.
  146. @remarks This error message will be localized in the future.
  147. */
  148. static NSString *const kMissingPasswordReason = @"Missing Password";
  149. /** @var gKeychainServiceNameForAppName
  150. @brief A map from Firebase app name to keychain service names.
  151. @remarks This map is needed for looking up the keychain service name after the FIRApp instance
  152. is deleted, to remove the associated keychain item. Accessing should occur within a
  153. @syncronized([FIRAuth class]) context.
  154. */
  155. static NSMutableDictionary *gKeychainServiceNameForAppName;
  156. #pragma mark - FIRActionCodeInfo
  157. @interface FIRActionCodeInfo ()
  158. /**
  159. @brief The operation being performed.
  160. */
  161. @property(nonatomic, readwrite) FIRActionCodeOperation operation;
  162. /** @property email
  163. @brief The email address to which the code was sent. The new email address in the case of
  164. FIRActionCodeOperationRecoverEmail.
  165. */
  166. @property(nonatomic, nullable, readwrite, copy) NSString *email;
  167. /** @property previousEmail
  168. @brief The current email address in the case of FIRActionCodeOperationRecoverEmail.
  169. */
  170. @property(nonatomic, nullable, readwrite, copy) NSString *previousEmail;
  171. #if TARGET_OS_IOS
  172. /** @property multiFactorInfo
  173. @brief The MultiFactorInfo object of the second factor to be reverted in case of
  174. FIRActionCodeMultiFactorInfoKey.
  175. */
  176. @property(nonatomic, nullable, readwrite) FIRMultiFactorInfo *multiFactorInfo;
  177. #endif
  178. @end
  179. @implementation FIRActionCodeInfo
  180. - (NSString *)dataForKey:(FIRActionDataKey)key {
  181. switch (key) {
  182. case FIRActionCodeEmailKey:
  183. return self.email;
  184. case FIRActionCodeFromEmailKey:
  185. return self.previousEmail;
  186. }
  187. }
  188. - (instancetype)initWithOperation:(FIRActionCodeOperation)operation
  189. email:(NSString *)email
  190. newEmail:(nullable NSString *)newEmail {
  191. self = [super init];
  192. if (self) {
  193. _operation = operation;
  194. if (newEmail) {
  195. _email = [newEmail copy];
  196. _previousEmail = [email copy];
  197. } else {
  198. _email = [email copy];
  199. }
  200. }
  201. return self;
  202. }
  203. /** @fn actionCodeOperationForRequestType:
  204. @brief Returns the corresponding operation type per provided request type string.
  205. @param requestType Request type returned in in the server response.
  206. @return The corresponding FIRActionCodeOperation for the supplied request type.
  207. */
  208. + (FIRActionCodeOperation)actionCodeOperationForRequestType:(NSString *)requestType {
  209. if ([requestType isEqualToString:kPasswordResetRequestType]) {
  210. return FIRActionCodeOperationPasswordReset;
  211. }
  212. if ([requestType isEqualToString:kVerifyEmailRequestType]) {
  213. return FIRActionCodeOperationVerifyEmail;
  214. }
  215. if ([requestType isEqualToString:kRecoverEmailRequestType]) {
  216. return FIRActionCodeOperationRecoverEmail;
  217. }
  218. if ([requestType isEqualToString:kEmailLinkSignInRequestType]) {
  219. return FIRActionCodeOperationEmailLink;
  220. }
  221. if ([requestType isEqualToString:kVerifyAndChangeEmailRequestType]) {
  222. return FIRActionCodeOperationVerifyAndChangeEmail;
  223. }
  224. if ([requestType isEqualToString:kRevertSecondFactorAdditionRequestType]) {
  225. return FIRActionCodeOperationRevertSecondFactorAddition;
  226. }
  227. return FIRActionCodeOperationUnknown;
  228. }
  229. @end
  230. #pragma mark - FIRActionCodeURL
  231. @implementation FIRActionCodeURL
  232. /** @fn FIRAuthParseURL:NSString
  233. @brief Parses an incoming URL into all available query items.
  234. @param urlString The url to be parsed.
  235. @return A dictionary of available query items in the target URL.
  236. */
  237. + (NSDictionary<NSString *, NSString *> *)parseURL:(NSString *)urlString {
  238. NSString *linkURL = [NSURLComponents componentsWithString:urlString].query;
  239. if (!linkURL) {
  240. return @{};
  241. }
  242. NSArray<NSString *> *URLComponents = [linkURL componentsSeparatedByString:@"&"];
  243. NSMutableDictionary<NSString *, NSString *> *queryItems =
  244. [[NSMutableDictionary alloc] initWithCapacity:URLComponents.count];
  245. for (NSString *component in URLComponents) {
  246. NSRange equalRange = [component rangeOfString:@"="];
  247. if (equalRange.location != NSNotFound) {
  248. NSString *queryItemKey =
  249. [[component substringToIndex:equalRange.location] stringByRemovingPercentEncoding];
  250. NSString *queryItemValue =
  251. [[component substringFromIndex:equalRange.location + 1] stringByRemovingPercentEncoding];
  252. if (queryItemKey && queryItemValue) {
  253. queryItems[queryItemKey] = queryItemValue;
  254. }
  255. }
  256. }
  257. return queryItems;
  258. }
  259. + (nullable instancetype)actionCodeURLWithLink:(NSString *)link {
  260. NSDictionary<NSString *, NSString *> *queryItems = [FIRActionCodeURL parseURL:link];
  261. if (!queryItems.count) {
  262. NSURLComponents *urlComponents = [NSURLComponents componentsWithString:link];
  263. queryItems = [FIRActionCodeURL parseURL:urlComponents.query];
  264. }
  265. if (!queryItems.count) {
  266. return nil;
  267. }
  268. NSString *APIKey = queryItems[@"apiKey"];
  269. NSString *actionCode = queryItems[@"oobCode"];
  270. NSString *continueURLString = queryItems[@"continueUrl"];
  271. NSString *languageCode = queryItems[@"languageCode"];
  272. NSString *mode = queryItems[@"mode"];
  273. NSString *tenantID = queryItems[@"tenantID"];
  274. return [[FIRActionCodeURL alloc] initWithAPIKey:APIKey
  275. actionCode:actionCode
  276. continueURLString:continueURLString
  277. languageCode:languageCode
  278. mode:mode
  279. tenantID:tenantID];
  280. }
  281. - (nullable instancetype)initWithAPIKey:(NSString *)APIKey
  282. actionCode:(NSString *)actionCode
  283. continueURLString:(NSString *)continueURLString
  284. languageCode:(NSString *)languageCode
  285. mode:(NSString *)mode
  286. tenantID:(NSString *)tenantID {
  287. self = [super init];
  288. if (self) {
  289. _APIKey = APIKey;
  290. _operation = [FIRActionCodeInfo actionCodeOperationForRequestType:mode];
  291. _code = actionCode;
  292. _continueURL = [NSURL URLWithString:continueURLString];
  293. _languageCode = languageCode;
  294. }
  295. return self;
  296. }
  297. @end
  298. #pragma mark - FIRAuth
  299. #if TARGET_OS_IOS
  300. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
  301. @interface FIRAuth () <UIApplicationDelegate,
  302. UISceneDelegate,
  303. FIRLibrary,
  304. FIRComponentLifecycleMaintainer>
  305. #else
  306. @interface FIRAuth () <UIApplicationDelegate, FIRLibrary, FIRComponentLifecycleMaintainer>
  307. #endif
  308. #else
  309. @interface FIRAuth () <FIRLibrary, FIRComponentLifecycleMaintainer>
  310. #endif
  311. /** @property firebaseAppId
  312. @brief The Firebase app ID.
  313. */
  314. @property(nonatomic, copy, readonly) NSString *firebaseAppId;
  315. /** @property additionalFrameworkMarker
  316. @brief Additional framework marker that will be added as part of the header of every request.
  317. */
  318. @property(nonatomic, copy, nullable) NSString *additionalFrameworkMarker;
  319. /** @property storedUserManager
  320. @brief The stored user manager.
  321. */
  322. @property(nonatomic, strong, nullable) FIRAuthStoredUserManager *storedUserManager;
  323. /** @fn initWithApp:
  324. @brief Creates a @c FIRAuth instance associated with the provided @c FIRApp instance.
  325. @param app The application to associate the auth instance with.
  326. */
  327. - (instancetype)initWithApp:(FIRApp *)app;
  328. @end
  329. @implementation FIRAuth {
  330. /** @var _currentUser
  331. @brief The current user.
  332. */
  333. FIRUser *_currentUser;
  334. /** @var _firebaseAppName
  335. @brief The Firebase app name.
  336. */
  337. NSString *_firebaseAppName;
  338. /** @var _listenerHandles
  339. @brief Handles returned from @c NSNotificationCenter for blocks which are "auth state did
  340. change" notification listeners.
  341. @remarks Mutations should occur within a @syncronized(self) context.
  342. */
  343. NSMutableArray<FIRAuthStateDidChangeListenerHandle> *_listenerHandles;
  344. /** @var _keychainServices
  345. @brief The keychain service.
  346. */
  347. FIRAuthKeychainServices *_keychainServices;
  348. /** @var _lastNotifiedUserToken
  349. @brief The user access (ID) token used last time for posting auth state changed notification.
  350. */
  351. NSString *_lastNotifiedUserToken;
  352. /** @var _autoRefreshTokens
  353. @brief This flag denotes whether or not tokens should be automatically refreshed.
  354. @remarks Will only be set to @YES if the another Firebase service is included (additionally to
  355. Firebase Auth).
  356. */
  357. BOOL _autoRefreshTokens;
  358. /** @var _autoRefreshScheduled
  359. @brief Whether or not token auto-refresh is currently scheduled.
  360. */
  361. BOOL _autoRefreshScheduled;
  362. /** @var _isAppInBackground
  363. @brief A flag that is set to YES if the app is put in the background and no when the app is
  364. returned to the foreground.
  365. */
  366. BOOL _isAppInBackground;
  367. /** @var _applicationDidBecomeActiveObserver
  368. @brief An opaque object to act as the observer for UIApplicationDidBecomeActiveNotification.
  369. */
  370. id<NSObject> _applicationDidBecomeActiveObserver;
  371. /** @var _applicationDidBecomeActiveObserver
  372. @brief An opaque object to act as the observer for
  373. UIApplicationDidEnterBackgroundNotification.
  374. */
  375. id<NSObject> _applicationDidEnterBackgroundObserver;
  376. }
  377. + (void)load {
  378. [FIRApp registerInternalLibrary:(Class<FIRLibrary>)self
  379. withName:@"fire-auth"
  380. withVersion:[NSString stringWithUTF8String:FirebaseAuthVersionStr]];
  381. }
  382. + (void)initialize {
  383. gKeychainServiceNameForAppName = [[NSMutableDictionary alloc] init];
  384. }
  385. + (FIRAuth *)auth {
  386. FIRApp *defaultApp = [FIRApp defaultApp];
  387. if (!defaultApp) {
  388. [NSException raise:NSInternalInconsistencyException
  389. format:@"The default FIRApp instance must be configured before the default FIRAuth"
  390. @"instance can be initialized. One way to ensure that is to call "
  391. @"`[FIRApp configure];` (`FirebaseApp.configure()` in Swift) in the App "
  392. @"Delegate's `application:didFinishLaunchingWithOptions:` "
  393. @"(`application(_:didFinishLaunchingWithOptions:)` in Swift)."];
  394. }
  395. return [self authWithApp:defaultApp];
  396. }
  397. + (FIRAuth *)authWithApp:(FIRApp *)app {
  398. // Get the instance of Auth from the container, which will create or return the cached instance
  399. // associated with this app.
  400. id<FIRAuthInterop> auth = FIR_COMPONENT(FIRAuthInterop, app.container);
  401. return (FIRAuth *)auth;
  402. }
  403. - (instancetype)initWithApp:(FIRApp *)app {
  404. [FIRAuth setKeychainServiceNameForApp:app];
  405. self = [self initWithAPIKey:app.options.APIKey appName:app.name];
  406. if (self) {
  407. _app = app;
  408. #if TARGET_OS_IOS
  409. _authURLPresenter = [[FIRAuthURLPresenter alloc] init];
  410. #endif
  411. }
  412. return self;
  413. }
  414. - (nullable instancetype)initWithAPIKey:(NSString *)APIKey appName:(NSString *)appName {
  415. self = [super init];
  416. if (self) {
  417. _listenerHandles = [NSMutableArray array];
  418. _requestConfiguration = [[FIRAuthRequestConfiguration alloc] initWithAPIKey:APIKey];
  419. _firebaseAppName = [appName copy];
  420. #if TARGET_OS_IOS
  421. _settings = [[FIRAuthSettings alloc] init];
  422. static Class applicationClass = nil;
  423. // iOS App extensions should not call [UIApplication sharedApplication], even if UIApplication
  424. // responds to it.
  425. if (![GULAppEnvironmentUtil isAppExtension]) {
  426. Class cls = NSClassFromString(@"UIApplication");
  427. if (cls && [cls respondsToSelector:NSSelectorFromString(@"sharedApplication")]) {
  428. applicationClass = cls;
  429. }
  430. }
  431. UIApplication *application = [applicationClass sharedApplication];
  432. [GULAppDelegateSwizzler proxyOriginalDelegateIncludingAPNSMethods];
  433. [GULSceneDelegateSwizzler proxyOriginalSceneDelegate];
  434. #endif // TARGET_OS_IOS
  435. // Continue with the rest of initialization in the work thread.
  436. __weak FIRAuth *weakSelf = self;
  437. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  438. // Load current user from Keychain.
  439. FIRAuth *strongSelf = weakSelf;
  440. if (!strongSelf) {
  441. return;
  442. }
  443. NSString *keychainServiceName =
  444. [FIRAuth keychainServiceNameForAppName:strongSelf->_firebaseAppName];
  445. if (keychainServiceName) {
  446. strongSelf->_keychainServices =
  447. [[FIRAuthKeychainServices alloc] initWithService:keychainServiceName];
  448. strongSelf.storedUserManager =
  449. [[FIRAuthStoredUserManager alloc] initWithServiceName:keychainServiceName];
  450. }
  451. NSError *error;
  452. NSString *storedUserAccessGroup =
  453. [strongSelf.storedUserManager getStoredUserAccessGroupWithError:&error];
  454. if (!error) {
  455. if (!storedUserAccessGroup) {
  456. FIRUser *user;
  457. if ([strongSelf getUser:&user error:&error]) {
  458. [strongSelf updateCurrentUser:user byForce:NO savingToDisk:NO error:&error];
  459. self->_lastNotifiedUserToken = user.rawAccessToken;
  460. } else {
  461. FIRLogError(kFIRLoggerAuth, @"I-AUT000001",
  462. @"Error loading saved user when starting up: %@", error);
  463. }
  464. } else {
  465. [strongSelf internalUseUserAccessGroup:storedUserAccessGroup error:&error];
  466. if (error) {
  467. FIRLogError(kFIRLoggerAuth, @"I-AUT000001",
  468. @"Error loading saved user when starting up: %@", error);
  469. }
  470. }
  471. } else {
  472. FIRLogError(kFIRLoggerAuth, @"I-AUT000001",
  473. @"Error loading saved user when starting up: %@", error);
  474. }
  475. #if TARGET_OS_IOS
  476. // Initialize for phone number auth.
  477. strongSelf->_tokenManager = [[FIRAuthAPNSTokenManager alloc] initWithApplication:application];
  478. strongSelf->_appCredentialManager =
  479. [[FIRAuthAppCredentialManager alloc] initWithKeychain:strongSelf->_keychainServices];
  480. strongSelf->_notificationManager = [[FIRAuthNotificationManager alloc]
  481. initWithApplication:application
  482. appCredentialManager:strongSelf->_appCredentialManager];
  483. [GULAppDelegateSwizzler registerAppDelegateInterceptor:strongSelf];
  484. #if ((TARGET_OS_IOS || TARGET_OS_TV) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 130000))
  485. if (@available(iOS 13, tvos 13, *)) {
  486. [GULSceneDelegateSwizzler registerSceneDelegateInterceptor:strongSelf];
  487. }
  488. #endif // ((TARGET_OS_IOS || TARGET_OS_TV) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 130000))
  489. #endif // TARGET_OS_IOS
  490. });
  491. }
  492. return self;
  493. }
  494. - (void)dealloc {
  495. @synchronized(self) {
  496. NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
  497. while (_listenerHandles.count != 0) {
  498. FIRAuthStateDidChangeListenerHandle handleToRemove = _listenerHandles.lastObject;
  499. [defaultCenter removeObserver:handleToRemove];
  500. [_listenerHandles removeLastObject];
  501. }
  502. #if TARGET_OS_IOS
  503. [defaultCenter removeObserver:_applicationDidBecomeActiveObserver
  504. name:UIApplicationDidBecomeActiveNotification
  505. object:nil];
  506. [defaultCenter removeObserver:_applicationDidEnterBackgroundObserver
  507. name:UIApplicationDidEnterBackgroundNotification
  508. object:nil];
  509. #endif
  510. }
  511. }
  512. #pragma mark - Public API
  513. - (nullable FIRUser *)currentUser {
  514. __block FIRUser *result;
  515. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  516. result = self->_currentUser;
  517. });
  518. return result;
  519. }
  520. - (void)fetchProvidersForEmail:(NSString *)email
  521. completion:(nullable FIRProviderQueryCallback)completion {
  522. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  523. FIRCreateAuthURIRequest *request =
  524. [[FIRCreateAuthURIRequest alloc] initWithIdentifier:email
  525. continueURI:@"http://www.google.com/"
  526. requestConfiguration:self->_requestConfiguration];
  527. [FIRAuthBackend
  528. createAuthURI:request
  529. callback:^(FIRCreateAuthURIResponse *_Nullable response, NSError *_Nullable error) {
  530. if (completion) {
  531. dispatch_async(dispatch_get_main_queue(), ^{
  532. completion(response.allProviders, error);
  533. });
  534. }
  535. }];
  536. });
  537. }
  538. - (void)signInWithProvider:(id<FIRFederatedAuthProvider>)provider
  539. UIDelegate:(nullable id<FIRAuthUIDelegate>)UIDelegate
  540. completion:(nullable FIRAuthDataResultCallback)completion {
  541. #if TARGET_OS_IOS
  542. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  543. FIRAuthDataResultCallback decoratedCallback =
  544. [self signInFlowAuthDataResultCallbackByDecoratingCallback:completion];
  545. [provider getCredentialWithUIDelegate:UIDelegate
  546. completion:^(FIRAuthCredential *_Nullable credential,
  547. NSError *_Nullable error) {
  548. if (error) {
  549. decoratedCallback(nil, error);
  550. return;
  551. }
  552. [self
  553. internalSignInAndRetrieveDataWithCredential:credential
  554. isReauthentication:NO
  555. callback:decoratedCallback];
  556. }];
  557. });
  558. #endif // TARGET_OS_IOS
  559. }
  560. - (void)fetchSignInMethodsForEmail:(nonnull NSString *)email
  561. completion:(nullable FIRSignInMethodQueryCallback)completion {
  562. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  563. FIRCreateAuthURIRequest *request =
  564. [[FIRCreateAuthURIRequest alloc] initWithIdentifier:email
  565. continueURI:@"http://www.google.com/"
  566. requestConfiguration:self->_requestConfiguration];
  567. [FIRAuthBackend
  568. createAuthURI:request
  569. callback:^(FIRCreateAuthURIResponse *_Nullable response, NSError *_Nullable error) {
  570. if (completion) {
  571. dispatch_async(dispatch_get_main_queue(), ^{
  572. completion(response.signinMethods, error);
  573. });
  574. }
  575. }];
  576. });
  577. }
  578. - (void)signInWithEmail:(NSString *)email
  579. password:(NSString *)password
  580. completion:(nullable FIRAuthDataResultCallback)completion {
  581. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  582. FIRAuthDataResultCallback decoratedCallback =
  583. [self signInFlowAuthDataResultCallbackByDecoratingCallback:completion];
  584. [self internalSignInAndRetrieveDataWithEmail:email
  585. password:password
  586. completion:^(FIRAuthDataResult *_Nullable authResult,
  587. NSError *_Nullable error) {
  588. decoratedCallback(authResult, error);
  589. }];
  590. });
  591. }
  592. - (void)signInWithEmail:(NSString *)email
  593. link:(NSString *)link
  594. completion:(nullable FIRAuthDataResultCallback)completion {
  595. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  596. FIRAuthDataResultCallback decoratedCallback =
  597. [self signInFlowAuthDataResultCallbackByDecoratingCallback:completion];
  598. FIREmailPasswordAuthCredential *credential =
  599. [[FIREmailPasswordAuthCredential alloc] initWithEmail:email link:link];
  600. [self internalSignInAndRetrieveDataWithCredential:credential
  601. isReauthentication:NO
  602. callback:^(FIRAuthDataResult *_Nullable authResult,
  603. NSError *_Nullable error) {
  604. decoratedCallback(authResult, error);
  605. }];
  606. });
  607. }
  608. /** @fn signInWithEmail:password:callback:
  609. @brief Signs in using an email address and password.
  610. @param email The user's email address.
  611. @param password The user's password.
  612. @param callback A block which is invoked when the sign in finishes (or is cancelled.) Invoked
  613. asynchronously on the global auth work queue in the future.
  614. @remarks This is the internal counterpart of this method, which uses a callback that does not
  615. update the current user.
  616. */
  617. - (void)signInWithEmail:(NSString *)email
  618. password:(NSString *)password
  619. callback:(FIRAuthResultCallback)callback {
  620. FIRVerifyPasswordRequest *request =
  621. [[FIRVerifyPasswordRequest alloc] initWithEmail:email
  622. password:password
  623. requestConfiguration:_requestConfiguration];
  624. if (![request.password length]) {
  625. callback(nil, [FIRAuthErrorUtils wrongPasswordErrorWithMessage:nil]);
  626. return;
  627. }
  628. [FIRAuthBackend
  629. verifyPassword:request
  630. callback:^(FIRVerifyPasswordResponse *_Nullable response, NSError *_Nullable error) {
  631. if (error) {
  632. callback(nil, error);
  633. return;
  634. }
  635. [self completeSignInWithAccessToken:response.IDToken
  636. accessTokenExpirationDate:response.approximateExpirationDate
  637. refreshToken:response.refreshToken
  638. anonymous:NO
  639. callback:callback];
  640. }];
  641. }
  642. /** @fn internalSignInAndRetrieveDataWithEmail:password:callback:
  643. @brief Signs in using an email address and password.
  644. @param email The user's email address.
  645. @param password The user's password.
  646. @param completion A block which is invoked when the sign in finishes (or is cancelled.) Invoked
  647. asynchronously on the global auth work queue in the future.
  648. @remarks This is the internal counterpart of this method, which uses a callback that does not
  649. update the current user.
  650. */
  651. - (void)internalSignInAndRetrieveDataWithEmail:(NSString *)email
  652. password:(NSString *)password
  653. completion:(FIRAuthDataResultCallback)completion {
  654. FIREmailPasswordAuthCredential *credentail =
  655. [[FIREmailPasswordAuthCredential alloc] initWithEmail:email password:password];
  656. [self internalSignInAndRetrieveDataWithCredential:credentail
  657. isReauthentication:NO
  658. callback:completion];
  659. }
  660. /** @fn signInAndRetrieveDataWithGameCenterCredential:callback:
  661. @brief Signs in using a game center credential.
  662. @param credential The Game Center Auth Credential used to sign in.
  663. @param callback A block which is invoked when the sign in finished (or is cancelled). Invoked
  664. asynchronously on the global auth work queue in the future.
  665. */
  666. - (void)signInAndRetrieveDataWithGameCenterCredential:(FIRGameCenterAuthCredential *)credential
  667. callback:(FIRAuthDataResultCallback)callback {
  668. FIRSignInWithGameCenterRequest *request =
  669. [[FIRSignInWithGameCenterRequest alloc] initWithPlayerID:credential.playerID
  670. publicKeyURL:credential.publicKeyURL
  671. signature:credential.signature
  672. salt:credential.salt
  673. timestamp:credential.timestamp
  674. displayName:credential.displayName
  675. requestConfiguration:_requestConfiguration];
  676. [FIRAuthBackend
  677. signInWithGameCenter:request
  678. callback:^(FIRSignInWithGameCenterResponse *_Nullable response,
  679. NSError *_Nullable error) {
  680. if (error) {
  681. if (callback) {
  682. callback(nil, error);
  683. }
  684. return;
  685. }
  686. [self
  687. completeSignInWithAccessToken:response.IDToken
  688. accessTokenExpirationDate:response.approximateExpirationDate
  689. refreshToken:response.refreshToken
  690. anonymous:NO
  691. callback:^(FIRUser *_Nullable user,
  692. NSError *_Nullable error) {
  693. if (error && callback) {
  694. callback(nil, error);
  695. return;
  696. }
  697. FIRAdditionalUserInfo *additionalUserInfo =
  698. [[FIRAdditionalUserInfo alloc]
  699. initWithProviderID:
  700. FIRGameCenterAuthProviderID
  701. profile:nil
  702. username:nil
  703. isNewUser:response.isNewUser];
  704. FIRAuthDataResult *result =
  705. user ? [[FIRAuthDataResult alloc]
  706. initWithUser:user
  707. additionalUserInfo:additionalUserInfo]
  708. : nil;
  709. if (callback) {
  710. callback(result, error);
  711. }
  712. }];
  713. }];
  714. }
  715. /** @fn internalSignInAndRetrieveDataWithEmail:link:completion:
  716. @brief Signs in using an email and email sign-in link.
  717. @param email The user's email address.
  718. @param link The email sign-in link.
  719. @param callback A block which is invoked when the sign in finishes (or is cancelled.) Invoked
  720. asynchronously on the global auth work queue in the future.
  721. */
  722. - (void)internalSignInAndRetrieveDataWithEmail:(nonnull NSString *)email
  723. link:(nonnull NSString *)link
  724. callback:(nullable FIRAuthDataResultCallback)callback {
  725. if (![self isSignInWithEmailLink:link]) {
  726. [FIRAuthExceptionUtils
  727. raiseInvalidParameterExceptionWithReason:kInvalidEmailSignInLinkExceptionMessage];
  728. return;
  729. }
  730. NSDictionary<NSString *, NSString *> *queryItems = [FIRAuthWebUtils parseURL:link];
  731. if (![queryItems count]) {
  732. NSURLComponents *urlComponents = [NSURLComponents componentsWithString:link];
  733. queryItems = [FIRAuthWebUtils parseURL:urlComponents.query];
  734. }
  735. NSString *actionCode = queryItems[@"oobCode"];
  736. FIREmailLinkSignInRequest *request =
  737. [[FIREmailLinkSignInRequest alloc] initWithEmail:email
  738. oobCode:actionCode
  739. requestConfiguration:_requestConfiguration];
  740. [FIRAuthBackend
  741. emailLinkSignin:request
  742. callback:^(FIREmailLinkSignInResponse *_Nullable response, NSError *_Nullable error) {
  743. if (error) {
  744. if (callback) {
  745. callback(nil, error);
  746. }
  747. return;
  748. }
  749. [self completeSignInWithAccessToken:response.IDToken
  750. accessTokenExpirationDate:response.approximateExpirationDate
  751. refreshToken:response.refreshToken
  752. anonymous:NO
  753. callback:^(FIRUser *_Nullable user,
  754. NSError *_Nullable error) {
  755. if (error && callback) {
  756. callback(nil, error);
  757. return;
  758. }
  759. FIRAdditionalUserInfo *additionalUserInfo =
  760. [[FIRAdditionalUserInfo alloc]
  761. initWithProviderID:FIREmailAuthProviderID
  762. profile:nil
  763. username:nil
  764. isNewUser:response.isNewUser];
  765. FIRAuthDataResult *result =
  766. user ? [[FIRAuthDataResult alloc]
  767. initWithUser:user
  768. additionalUserInfo:additionalUserInfo]
  769. : nil;
  770. if (callback) {
  771. callback(result, error);
  772. }
  773. }];
  774. }];
  775. }
  776. #pragma clang diagnostic push
  777. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  778. - (void)signInWithCredential:(FIRAuthCredential *)credential
  779. completion:(nullable FIRAuthDataResultCallback)completion {
  780. [self signInAndRetrieveDataWithCredential:credential completion:completion];
  781. }
  782. #pragma clang diagnostic pop
  783. - (void)signInAndRetrieveDataWithCredential:(FIRAuthCredential *)credential
  784. completion:(nullable FIRAuthDataResultCallback)completion {
  785. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  786. FIRAuthDataResultCallback callback =
  787. [self signInFlowAuthDataResultCallbackByDecoratingCallback:completion];
  788. [self internalSignInAndRetrieveDataWithCredential:credential
  789. isReauthentication:NO
  790. callback:callback];
  791. });
  792. }
  793. - (void)internalSignInWithCredential:(FIRAuthCredential *)credential
  794. callback:(FIRAuthResultCallback)callback {
  795. [self internalSignInAndRetrieveDataWithCredential:credential
  796. isReauthentication:NO
  797. callback:^(FIRAuthDataResult *_Nullable authResult,
  798. NSError *_Nullable error) {
  799. callback(authResult.user, error);
  800. }];
  801. }
  802. - (void)internalSignInAndRetrieveDataWithCredential:(FIRAuthCredential *)credential
  803. isReauthentication:(BOOL)isReauthentication
  804. callback:(nullable FIRAuthDataResultCallback)callback {
  805. if ([credential isKindOfClass:[FIREmailPasswordAuthCredential class]]) {
  806. // Special case for email/password credentials
  807. FIREmailPasswordAuthCredential *emailPasswordCredential =
  808. (FIREmailPasswordAuthCredential *)credential;
  809. if (emailPasswordCredential.link) {
  810. // Email link sign in
  811. [self internalSignInAndRetrieveDataWithEmail:emailPasswordCredential.email
  812. link:emailPasswordCredential.link
  813. callback:callback];
  814. } else {
  815. // Email password sign in
  816. FIRAuthResultCallback completeEmailSignIn =
  817. ^(FIRUser *_Nullable user, NSError *_Nullable error) {
  818. if (callback) {
  819. if (error) {
  820. callback(nil, error);
  821. return;
  822. }
  823. FIRAdditionalUserInfo *additionalUserInfo =
  824. [[FIRAdditionalUserInfo alloc] initWithProviderID:FIREmailAuthProviderID
  825. profile:nil
  826. username:nil
  827. isNewUser:NO];
  828. FIRAuthDataResult *result =
  829. user ? [[FIRAuthDataResult alloc] initWithUser:user
  830. additionalUserInfo:additionalUserInfo]
  831. : nil;
  832. callback(result, error);
  833. }
  834. };
  835. [self signInWithEmail:emailPasswordCredential.email
  836. password:emailPasswordCredential.password
  837. callback:completeEmailSignIn];
  838. }
  839. return;
  840. }
  841. if ([credential isKindOfClass:[FIRGameCenterAuthCredential class]]) {
  842. // Special case for Game Center credentials.
  843. [self signInAndRetrieveDataWithGameCenterCredential:(FIRGameCenterAuthCredential *)credential
  844. callback:callback];
  845. return;
  846. }
  847. #if TARGET_OS_IOS
  848. if ([credential isKindOfClass:[FIRPhoneAuthCredential class]]) {
  849. // Special case for phone auth credentials
  850. FIRPhoneAuthCredential *phoneCredential = (FIRPhoneAuthCredential *)credential;
  851. FIRAuthOperationType operation =
  852. isReauthentication ? FIRAuthOperationTypeReauth : FIRAuthOperationTypeSignUpOrSignIn;
  853. [self
  854. signInWithPhoneCredential:phoneCredential
  855. operation:operation
  856. callback:^(FIRVerifyPhoneNumberResponse *_Nullable response,
  857. NSError *_Nullable error) {
  858. if (callback) {
  859. if (error) {
  860. callback(nil, error);
  861. return;
  862. }
  863. [self
  864. completeSignInWithAccessToken:response.IDToken
  865. accessTokenExpirationDate:response.approximateExpirationDate
  866. refreshToken:response.refreshToken
  867. anonymous:NO
  868. callback:^(FIRUser *_Nullable user,
  869. NSError *_Nullable error) {
  870. if (error && callback) {
  871. callback(nil, error);
  872. return;
  873. }
  874. FIRAdditionalUserInfo *additionalUserInfo =
  875. [[FIRAdditionalUserInfo alloc]
  876. initWithProviderID:
  877. FIRPhoneAuthProviderID
  878. profile:nil
  879. username:nil
  880. isNewUser:response
  881. .isNewUser];
  882. FIRAuthDataResult *result =
  883. user ? [[FIRAuthDataResult alloc]
  884. initWithUser:user
  885. additionalUserInfo:
  886. additionalUserInfo]
  887. : nil;
  888. if (callback) {
  889. callback(result, error);
  890. }
  891. }];
  892. }
  893. }];
  894. return;
  895. }
  896. #endif
  897. FIRVerifyAssertionRequest *request =
  898. [[FIRVerifyAssertionRequest alloc] initWithProviderID:credential.provider
  899. requestConfiguration:_requestConfiguration];
  900. request.autoCreate = !isReauthentication;
  901. [credential prepareVerifyAssertionRequest:request];
  902. [FIRAuthBackend
  903. verifyAssertion:request
  904. callback:^(FIRVerifyAssertionResponse *response, NSError *error) {
  905. if (error) {
  906. if (callback) {
  907. callback(nil, error);
  908. }
  909. return;
  910. }
  911. if (response.needConfirmation) {
  912. if (callback) {
  913. NSString *email = response.email;
  914. FIROAuthCredential *credential =
  915. [[FIROAuthCredential alloc] initWithVerifyAssertionResponse:response];
  916. callback(nil,
  917. [FIRAuthErrorUtils
  918. accountExistsWithDifferentCredentialErrorWithEmail:email
  919. updatedCredential:credential]);
  920. }
  921. return;
  922. }
  923. if (!response.providerID.length) {
  924. if (callback) {
  925. callback(nil, [FIRAuthErrorUtils
  926. unexpectedResponseWithDeserializedResponse:response]);
  927. }
  928. return;
  929. }
  930. [self
  931. completeSignInWithAccessToken:response.IDToken
  932. accessTokenExpirationDate:response.approximateExpirationDate
  933. refreshToken:response.refreshToken
  934. anonymous:NO
  935. callback:^(FIRUser *_Nullable user,
  936. NSError *_Nullable error) {
  937. if (callback) {
  938. if (error) {
  939. callback(nil, error);
  940. return;
  941. }
  942. FIRAdditionalUserInfo *additionalUserInfo =
  943. [FIRAdditionalUserInfo
  944. userInfoWithVerifyAssertionResponse:response];
  945. FIROAuthCredential *updatedOAuthCredential =
  946. [[FIROAuthCredential alloc]
  947. initWithVerifyAssertionResponse:response];
  948. FIRAuthDataResult *result =
  949. user
  950. ? [[FIRAuthDataResult alloc]
  951. initWithUser:user
  952. additionalUserInfo:additionalUserInfo
  953. credential:updatedOAuthCredential]
  954. : nil;
  955. callback(result, error);
  956. }
  957. }];
  958. }];
  959. }
  960. - (void)signInAnonymouslyWithCompletion:(nullable FIRAuthDataResultCallback)completion {
  961. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  962. FIRAuthDataResultCallback decoratedCallback =
  963. [self signInFlowAuthDataResultCallbackByDecoratingCallback:completion];
  964. if (self->_currentUser.anonymous) {
  965. FIRAuthDataResult *result = [[FIRAuthDataResult alloc] initWithUser:self->_currentUser
  966. additionalUserInfo:nil];
  967. decoratedCallback(result, nil);
  968. return;
  969. }
  970. [self internalSignInAnonymouslyWithCompletion:^(FIRSignUpNewUserResponse *_Nullable response,
  971. NSError *_Nullable error) {
  972. if (error) {
  973. decoratedCallback(nil, error);
  974. return;
  975. }
  976. [self completeSignInWithAccessToken:response.IDToken
  977. accessTokenExpirationDate:response.approximateExpirationDate
  978. refreshToken:response.refreshToken
  979. anonymous:YES
  980. callback:^(FIRUser *_Nullable user, NSError *_Nullable error) {
  981. if (error) {
  982. decoratedCallback(nil, error);
  983. return;
  984. }
  985. FIRAdditionalUserInfo *additionalUserInfo =
  986. [[FIRAdditionalUserInfo alloc] initWithProviderID:nil
  987. profile:nil
  988. username:nil
  989. isNewUser:YES];
  990. FIRAuthDataResult *authDataResult =
  991. user ? [[FIRAuthDataResult alloc]
  992. initWithUser:user
  993. additionalUserInfo:additionalUserInfo]
  994. : nil;
  995. decoratedCallback(authDataResult, error);
  996. }];
  997. }];
  998. });
  999. }
  1000. - (void)signInWithCustomToken:(NSString *)token
  1001. completion:(nullable FIRAuthDataResultCallback)completion {
  1002. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  1003. FIRAuthDataResultCallback decoratedCallback =
  1004. [self signInFlowAuthDataResultCallbackByDecoratingCallback:completion];
  1005. [self internalSignInAndRetrieveDataWithCustomToken:token
  1006. completion:^(FIRAuthDataResult *_Nullable authResult,
  1007. NSError *_Nullable error) {
  1008. decoratedCallback(authResult, error);
  1009. }];
  1010. });
  1011. }
  1012. - (void)createUserWithEmail:(NSString *)email
  1013. password:(NSString *)password
  1014. completion:(nullable FIRAuthDataResultCallback)completion {
  1015. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  1016. FIRAuthDataResultCallback decoratedCallback =
  1017. [self signInFlowAuthDataResultCallbackByDecoratingCallback:completion];
  1018. [self internalCreateUserWithEmail:email
  1019. password:password
  1020. completion:^(FIRSignUpNewUserResponse *_Nullable response,
  1021. NSError *_Nullable error) {
  1022. if (error) {
  1023. decoratedCallback(nil, error);
  1024. return;
  1025. }
  1026. [self
  1027. completeSignInWithAccessToken:response.IDToken
  1028. accessTokenExpirationDate:response.approximateExpirationDate
  1029. refreshToken:response.refreshToken
  1030. anonymous:NO
  1031. callback:^(FIRUser *_Nullable user,
  1032. NSError *_Nullable error) {
  1033. if (error) {
  1034. decoratedCallback(nil, error);
  1035. return;
  1036. }
  1037. FIRAdditionalUserInfo *additionalUserInfo =
  1038. [[FIRAdditionalUserInfo alloc]
  1039. initWithProviderID:
  1040. FIREmailAuthProviderID
  1041. profile:nil
  1042. username:nil
  1043. isNewUser:YES];
  1044. FIRAuthDataResult *authDataResult =
  1045. user ? [[FIRAuthDataResult alloc]
  1046. initWithUser:user
  1047. additionalUserInfo:
  1048. additionalUserInfo]
  1049. : nil;
  1050. decoratedCallback(authDataResult, error);
  1051. }];
  1052. }];
  1053. });
  1054. }
  1055. - (void)confirmPasswordResetWithCode:(NSString *)code
  1056. newPassword:(NSString *)newPassword
  1057. completion:(FIRConfirmPasswordResetCallback)completion {
  1058. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  1059. FIRResetPasswordRequest *request =
  1060. [[FIRResetPasswordRequest alloc] initWithOobCode:code
  1061. newPassword:newPassword
  1062. requestConfiguration:self->_requestConfiguration];
  1063. [FIRAuthBackend
  1064. resetPassword:request
  1065. callback:^(FIRResetPasswordResponse *_Nullable response, NSError *_Nullable error) {
  1066. if (completion) {
  1067. dispatch_async(dispatch_get_main_queue(), ^{
  1068. if (error) {
  1069. completion(error);
  1070. return;
  1071. }
  1072. completion(nil);
  1073. });
  1074. }
  1075. }];
  1076. });
  1077. }
  1078. - (void)checkActionCode:(NSString *)code completion:(FIRCheckActionCodeCallBack)completion {
  1079. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  1080. FIRResetPasswordRequest *request =
  1081. [[FIRResetPasswordRequest alloc] initWithOobCode:code
  1082. newPassword:nil
  1083. requestConfiguration:self->_requestConfiguration];
  1084. [FIRAuthBackend
  1085. resetPassword:request
  1086. callback:^(FIRResetPasswordResponse *_Nullable response, NSError *_Nullable error) {
  1087. if (completion) {
  1088. if (error) {
  1089. dispatch_async(dispatch_get_main_queue(), ^{
  1090. completion(nil, error);
  1091. });
  1092. return;
  1093. }
  1094. FIRActionCodeOperation operation =
  1095. [FIRActionCodeInfo actionCodeOperationForRequestType:response.requestType];
  1096. FIRActionCodeInfo *actionCodeInfo =
  1097. [[FIRActionCodeInfo alloc] initWithOperation:operation
  1098. email:response.email
  1099. newEmail:response.verifiedEmail];
  1100. dispatch_async(dispatch_get_main_queue(), ^{
  1101. completion(actionCodeInfo, nil);
  1102. });
  1103. }
  1104. }];
  1105. });
  1106. }
  1107. - (void)verifyPasswordResetCode:(NSString *)code
  1108. completion:(FIRVerifyPasswordResetCodeCallback)completion {
  1109. [self checkActionCode:code
  1110. completion:^(FIRActionCodeInfo *_Nullable info, NSError *_Nullable error) {
  1111. if (completion) {
  1112. if (error) {
  1113. completion(nil, error);
  1114. return;
  1115. }
  1116. completion(info.email, nil);
  1117. }
  1118. }];
  1119. }
  1120. - (void)applyActionCode:(NSString *)code completion:(FIRApplyActionCodeCallback)completion {
  1121. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  1122. FIRSetAccountInfoRequest *request =
  1123. [[FIRSetAccountInfoRequest alloc] initWithRequestConfiguration:self->_requestConfiguration];
  1124. request.OOBCode = code;
  1125. [FIRAuthBackend
  1126. setAccountInfo:request
  1127. callback:^(FIRSetAccountInfoResponse *_Nullable response, NSError *_Nullable error) {
  1128. if (completion) {
  1129. dispatch_async(dispatch_get_main_queue(), ^{
  1130. completion(error);
  1131. });
  1132. }
  1133. }];
  1134. });
  1135. }
  1136. - (void)sendPasswordResetWithEmail:(NSString *)email
  1137. completion:(nullable FIRSendPasswordResetCallback)completion {
  1138. [self sendPasswordResetWithNullableActionCodeSettings:nil email:email completion:completion];
  1139. }
  1140. - (void)sendPasswordResetWithEmail:(NSString *)email
  1141. actionCodeSettings:(FIRActionCodeSettings *)actionCodeSettings
  1142. completion:(nullable FIRSendPasswordResetCallback)completion {
  1143. [self sendPasswordResetWithNullableActionCodeSettings:actionCodeSettings
  1144. email:email
  1145. completion:completion];
  1146. }
  1147. /** @fn sendPasswordResetWithNullableActionCodeSettings:actionCodeSetting:email:completion:
  1148. @brief Initiates a password reset for the given email address and @FIRActionCodeSettings object.
  1149. @param actionCodeSettings Optionally, An @c FIRActionCodeSettings object containing settings
  1150. related to the handling action codes.
  1151. @param email The email address of the user.
  1152. @param completion Optionally; a block which is invoked when the request finishes. Invoked
  1153. asynchronously on the main thread in the future.
  1154. */
  1155. - (void)sendPasswordResetWithNullableActionCodeSettings:
  1156. (nullable FIRActionCodeSettings *)actionCodeSettings
  1157. email:(NSString *)email
  1158. completion:
  1159. (nullable FIRSendPasswordResetCallback)completion {
  1160. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  1161. if (!email) {
  1162. [FIRAuthExceptionUtils
  1163. raiseInvalidParameterExceptionWithReason:kMissingEmailInvalidParameterExceptionReason];
  1164. return;
  1165. }
  1166. FIRGetOOBConfirmationCodeRequest *request = [FIRGetOOBConfirmationCodeRequest
  1167. passwordResetRequestWithEmail:email
  1168. actionCodeSettings:actionCodeSettings
  1169. requestConfiguration:self->_requestConfiguration];
  1170. [FIRAuthBackend getOOBConfirmationCode:request
  1171. callback:^(FIRGetOOBConfirmationCodeResponse *_Nullable response,
  1172. NSError *_Nullable error) {
  1173. if (completion) {
  1174. dispatch_async(dispatch_get_main_queue(), ^{
  1175. completion(error);
  1176. });
  1177. }
  1178. }];
  1179. });
  1180. }
  1181. - (void)sendSignInLinkToEmail:(nonnull NSString *)email
  1182. actionCodeSettings:(nonnull FIRActionCodeSettings *)actionCodeSettings
  1183. completion:(nullable FIRSendSignInLinkToEmailCallback)completion {
  1184. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  1185. if (!email) {
  1186. [FIRAuthExceptionUtils
  1187. raiseInvalidParameterExceptionWithReason:kMissingEmailInvalidParameterExceptionReason];
  1188. }
  1189. if (!actionCodeSettings.handleCodeInApp) {
  1190. [FIRAuthExceptionUtils
  1191. raiseInvalidParameterExceptionWithReason:kHandleCodeInAppFalseExceptionReason];
  1192. }
  1193. FIRGetOOBConfirmationCodeRequest *request =
  1194. [FIRGetOOBConfirmationCodeRequest signInWithEmailLinkRequest:email
  1195. actionCodeSettings:actionCodeSettings
  1196. requestConfiguration:self->_requestConfiguration];
  1197. [FIRAuthBackend getOOBConfirmationCode:request
  1198. callback:^(FIRGetOOBConfirmationCodeResponse *_Nullable response,
  1199. NSError *_Nullable error) {
  1200. if (completion) {
  1201. dispatch_async(dispatch_get_main_queue(), ^{
  1202. completion(error);
  1203. });
  1204. }
  1205. }];
  1206. });
  1207. }
  1208. - (void)updateCurrentUser:(FIRUser *)user completion:(nullable FIRUserUpdateCallback)completion {
  1209. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  1210. if (!user) {
  1211. if (completion) {
  1212. dispatch_async(dispatch_get_main_queue(), ^{
  1213. completion([FIRAuthErrorUtils nullUserErrorWithMessage:nil]);
  1214. });
  1215. }
  1216. return;
  1217. }
  1218. void (^updateUserBlock)(FIRUser *user) = ^(FIRUser *user) {
  1219. NSError *error;
  1220. [self updateCurrentUser:user byForce:YES savingToDisk:YES error:(&error)];
  1221. if (error) {
  1222. if (completion) {
  1223. dispatch_async(dispatch_get_main_queue(), ^{
  1224. completion(error);
  1225. });
  1226. }
  1227. return;
  1228. }
  1229. if (completion) {
  1230. dispatch_async(dispatch_get_main_queue(), ^{
  1231. completion(nil);
  1232. });
  1233. }
  1234. };
  1235. if (![user.requestConfiguration.APIKey isEqualToString:self->_requestConfiguration.APIKey]) {
  1236. // If the API keys are different, then we need to confirm that the user belongs to the same
  1237. // project before proceeding.
  1238. user.requestConfiguration = self->_requestConfiguration;
  1239. [user reloadWithCompletion:^(NSError *_Nullable error) {
  1240. if (error) {
  1241. if (completion) {
  1242. dispatch_async(dispatch_get_main_queue(), ^{
  1243. completion(error);
  1244. });
  1245. }
  1246. return;
  1247. }
  1248. updateUserBlock(user);
  1249. }];
  1250. } else {
  1251. updateUserBlock(user);
  1252. }
  1253. });
  1254. }
  1255. - (BOOL)signOut:(NSError *_Nullable __autoreleasing *_Nullable)error {
  1256. __block BOOL result = YES;
  1257. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  1258. if (!self->_currentUser) {
  1259. return;
  1260. }
  1261. result = [self updateCurrentUser:nil byForce:NO savingToDisk:YES error:error];
  1262. });
  1263. return result;
  1264. }
  1265. - (BOOL)signOutByForceWithUserID:(NSString *)userID error:(NSError *_Nullable *_Nullable)error {
  1266. if (_currentUser.uid != userID) {
  1267. return YES;
  1268. }
  1269. return [self updateCurrentUser:nil byForce:YES savingToDisk:YES error:error];
  1270. }
  1271. - (BOOL)isSignInWithEmailLink:(NSString *)link {
  1272. if (link.length == 0) {
  1273. return NO;
  1274. }
  1275. NSDictionary<NSString *, NSString *> *queryItems = [FIRAuthWebUtils parseURL:link];
  1276. if (![queryItems count]) {
  1277. NSURLComponents *urlComponents = [NSURLComponents componentsWithString:link];
  1278. if (!urlComponents.query) {
  1279. return NO;
  1280. }
  1281. queryItems = [FIRAuthWebUtils parseURL:urlComponents.query];
  1282. }
  1283. if (![queryItems count]) {
  1284. return NO;
  1285. }
  1286. NSString *actionCode = queryItems[@"oobCode"];
  1287. NSString *mode = queryItems[@"mode"];
  1288. if (actionCode && [mode isEqualToString:@"signIn"]) {
  1289. return YES;
  1290. }
  1291. return NO;
  1292. }
  1293. - (FIRAuthStateDidChangeListenerHandle)addAuthStateDidChangeListener:
  1294. (FIRAuthStateDidChangeListenerBlock)listener {
  1295. __block BOOL firstInvocation = YES;
  1296. __block NSString *previousUserID;
  1297. return [self addIDTokenDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) {
  1298. BOOL shouldCallListener = firstInvocation || !(previousUserID == user.uid ||
  1299. [previousUserID isEqualToString:user.uid]);
  1300. firstInvocation = NO;
  1301. previousUserID = [user.uid copy];
  1302. if (shouldCallListener) {
  1303. listener(auth, user);
  1304. }
  1305. }];
  1306. }
  1307. - (void)removeAuthStateDidChangeListener:(FIRAuthStateDidChangeListenerHandle)listenerHandle {
  1308. [self removeIDTokenDidChangeListener:listenerHandle];
  1309. }
  1310. - (FIRIDTokenDidChangeListenerHandle)addIDTokenDidChangeListener:
  1311. (FIRIDTokenDidChangeListenerBlock)listener {
  1312. if (!listener) {
  1313. [NSException raise:NSInvalidArgumentException format:@"listener must not be nil."];
  1314. return nil;
  1315. }
  1316. FIRAuthStateDidChangeListenerHandle handle;
  1317. NSNotificationCenter *notifications = [NSNotificationCenter defaultCenter];
  1318. handle = [notifications addObserverForName:FIRAuthStateDidChangeNotification
  1319. object:self
  1320. queue:[NSOperationQueue mainQueue]
  1321. usingBlock:^(NSNotification *_Nonnull notification) {
  1322. FIRAuth *auth = notification.object;
  1323. listener(auth, auth.currentUser);
  1324. }];
  1325. @synchronized(self) {
  1326. [_listenerHandles addObject:handle];
  1327. }
  1328. dispatch_async(dispatch_get_main_queue(), ^{
  1329. listener(self, self->_currentUser);
  1330. });
  1331. return handle;
  1332. }
  1333. - (void)removeIDTokenDidChangeListener:(FIRIDTokenDidChangeListenerHandle)listenerHandle {
  1334. [[NSNotificationCenter defaultCenter] removeObserver:listenerHandle];
  1335. @synchronized(self) {
  1336. [_listenerHandles removeObject:listenerHandle];
  1337. }
  1338. }
  1339. - (void)useAppLanguage {
  1340. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  1341. self->_requestConfiguration.languageCode = [[NSLocale preferredLanguages] firstObject];
  1342. });
  1343. }
  1344. - (nullable NSString *)languageCode {
  1345. return _requestConfiguration.languageCode;
  1346. }
  1347. - (void)setLanguageCode:(nullable NSString *)languageCode {
  1348. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  1349. self->_requestConfiguration.languageCode = [languageCode copy];
  1350. });
  1351. }
  1352. - (nullable NSString *)additionalFrameworkMarker {
  1353. return self->_requestConfiguration.additionalFrameworkMarker;
  1354. }
  1355. - (void)setAdditionalFrameworkMarker:(nullable NSString *)additionalFrameworkMarker {
  1356. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  1357. self->_requestConfiguration.additionalFrameworkMarker = [additionalFrameworkMarker copy];
  1358. });
  1359. }
  1360. #if TARGET_OS_IOS
  1361. #pragma clang diagnostic push
  1362. #pragma clang diagnostic ignored "-Wunused-property-ivar"
  1363. // The warning is ignored because we use the token manager to get the token, instead of using the
  1364. // ivar.
  1365. - (nullable NSData *)APNSToken {
  1366. __block NSData *result = nil;
  1367. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  1368. result = self->_tokenManager.token.data;
  1369. });
  1370. return result;
  1371. }
  1372. #pragma clang diagnostic pop
  1373. #pragma mark - UIApplicationDelegate
  1374. - (void)application:(UIApplication *)application
  1375. didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
  1376. [self setAPNSToken:deviceToken type:FIRAuthAPNSTokenTypeUnknown];
  1377. }
  1378. - (void)application:(UIApplication *)application
  1379. didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
  1380. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  1381. [self->_tokenManager cancelWithError:error];
  1382. });
  1383. }
  1384. - (void)application:(UIApplication *)application
  1385. didReceiveRemoteNotification:(NSDictionary *)userInfo
  1386. fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  1387. [self canHandleNotification:userInfo];
  1388. }
  1389. // iOS 10 deprecation
  1390. #pragma clang diagnostic push
  1391. #pragma clang diagnostic ignored "-Wdeprecated-implementations"
  1392. - (void)application:(UIApplication *)application
  1393. didReceiveRemoteNotification:(NSDictionary *)userInfo {
  1394. [self canHandleNotification:userInfo];
  1395. }
  1396. #pragma clang diagnostic pop
  1397. - (BOOL)application:(UIApplication *)app
  1398. openURL:(NSURL *)url
  1399. options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
  1400. return [self canHandleURL:url];
  1401. }
  1402. // iOS 10 deprecation
  1403. #pragma clang diagnostic push
  1404. #pragma clang diagnostic ignored "-Wdeprecated-implementations"
  1405. - (BOOL)application:(UIApplication *)application
  1406. openURL:(NSURL *)url
  1407. sourceApplication:(nullable NSString *)sourceApplication
  1408. annotation:(id)annotation {
  1409. return [self canHandleURL:url];
  1410. }
  1411. #pragma clang diagnostic pop
  1412. - (void)setAPNSToken:(NSData *)token type:(FIRAuthAPNSTokenType)type {
  1413. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  1414. self->_tokenManager.token = [[FIRAuthAPNSToken alloc] initWithData:token type:type];
  1415. });
  1416. }
  1417. - (BOOL)canHandleNotification:(NSDictionary *)userInfo {
  1418. __block BOOL result = NO;
  1419. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  1420. result = [self->_notificationManager canHandleNotification:userInfo];
  1421. });
  1422. return result;
  1423. }
  1424. - (BOOL)canHandleURL:(NSURL *)URL {
  1425. __block BOOL result = NO;
  1426. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  1427. result = [self->_authURLPresenter canHandleURL:URL];
  1428. });
  1429. return result;
  1430. }
  1431. #pragma mark - UISceneDelegate
  1432. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
  1433. - (void)scene:(UIScene *)scene
  1434. openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts API_AVAILABLE(ios(13.0)) {
  1435. for (UIOpenURLContext *urlContext in URLContexts) {
  1436. NSURL *url = [urlContext URL];
  1437. [self canHandleURL:url];
  1438. }
  1439. }
  1440. #endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
  1441. #endif // TARGET_OS_IOS
  1442. #pragma mark - Internal Methods
  1443. #if TARGET_OS_IOS
  1444. /** @fn signInWithPhoneCredential:callback:
  1445. @brief Signs in using a phone credential.
  1446. @param credential The Phone Auth credential used to sign in.
  1447. @param operation The type of operation for which this sign-in attempt is initiated.
  1448. @param callback A block which is invoked when the sign in finishes (or is cancelled.) Invoked
  1449. asynchronously on the global auth work queue in the future.
  1450. */
  1451. - (void)signInWithPhoneCredential:(FIRPhoneAuthCredential *)credential
  1452. operation:(FIRAuthOperationType)operation
  1453. callback:(FIRVerifyPhoneNumberResponseCallback)callback {
  1454. if (credential.temporaryProof.length && credential.phoneNumber.length) {
  1455. FIRVerifyPhoneNumberRequest *request =
  1456. [[FIRVerifyPhoneNumberRequest alloc] initWithTemporaryProof:credential.temporaryProof
  1457. phoneNumber:credential.phoneNumber
  1458. operation:operation
  1459. requestConfiguration:_requestConfiguration];
  1460. [FIRAuthBackend verifyPhoneNumber:request callback:callback];
  1461. return;
  1462. }
  1463. if (!credential.verificationID.length) {
  1464. callback(nil, [FIRAuthErrorUtils missingVerificationIDErrorWithMessage:nil]);
  1465. return;
  1466. }
  1467. if (!credential.verificationCode.length) {
  1468. callback(nil, [FIRAuthErrorUtils missingVerificationCodeErrorWithMessage:nil]);
  1469. return;
  1470. }
  1471. FIRVerifyPhoneNumberRequest *request =
  1472. [[FIRVerifyPhoneNumberRequest alloc] initWithVerificationID:credential.verificationID
  1473. verificationCode:credential.verificationCode
  1474. operation:operation
  1475. requestConfiguration:_requestConfiguration];
  1476. [FIRAuthBackend verifyPhoneNumber:request callback:callback];
  1477. }
  1478. #endif
  1479. /** @fn internalSignInAndRetrieveDataWithCustomToken:completion:
  1480. @brief Signs in a Firebase user given a custom token.
  1481. @param token A self-signed custom auth token.
  1482. @param completion A block which is invoked when the custom token sign in request completes.
  1483. */
  1484. - (void)internalSignInAndRetrieveDataWithCustomToken:(NSString *)token
  1485. completion:
  1486. (nullable FIRAuthDataResultCallback)completion {
  1487. FIRVerifyCustomTokenRequest *request =
  1488. [[FIRVerifyCustomTokenRequest alloc] initWithToken:token
  1489. requestConfiguration:_requestConfiguration];
  1490. [FIRAuthBackend
  1491. verifyCustomToken:request
  1492. callback:^(FIRVerifyCustomTokenResponse *_Nullable response,
  1493. NSError *_Nullable error) {
  1494. if (error) {
  1495. if (completion) {
  1496. completion(nil, error);
  1497. return;
  1498. }
  1499. }
  1500. [self completeSignInWithAccessToken:response.IDToken
  1501. accessTokenExpirationDate:response.approximateExpirationDate
  1502. refreshToken:response.refreshToken
  1503. anonymous:NO
  1504. callback:^(FIRUser *_Nullable user,
  1505. NSError *_Nullable error) {
  1506. if (error && completion) {
  1507. completion(nil, error);
  1508. return;
  1509. }
  1510. FIRAdditionalUserInfo *additonalUserInfo =
  1511. [[FIRAdditionalUserInfo alloc]
  1512. initWithProviderID:nil
  1513. profile:nil
  1514. username:nil
  1515. isNewUser:response.isNewUser];
  1516. FIRAuthDataResult *result =
  1517. user ? [[FIRAuthDataResult alloc]
  1518. initWithUser:user
  1519. additionalUserInfo:additonalUserInfo]
  1520. : nil;
  1521. if (completion) {
  1522. completion(result, error);
  1523. }
  1524. }];
  1525. }];
  1526. }
  1527. /** @fn internalCreateUserWithEmail:password:completion:
  1528. @brief Makes a backend request attempting to create a new Firebase user given an email address
  1529. and password.
  1530. @param email The email address used to create the new Firebase user.
  1531. @param password The password used to create the new Firebase user.
  1532. @param completion Optionally; a block which is invoked when the request finishes.
  1533. */
  1534. - (void)internalCreateUserWithEmail:(NSString *)email
  1535. password:(NSString *)password
  1536. completion:(nullable FIRSignupNewUserCallback)completion {
  1537. FIRSignUpNewUserRequest *request =
  1538. [[FIRSignUpNewUserRequest alloc] initWithEmail:email
  1539. password:password
  1540. displayName:nil
  1541. requestConfiguration:_requestConfiguration];
  1542. if (![request.password length]) {
  1543. completion(
  1544. nil, [FIRAuthErrorUtils weakPasswordErrorWithServerResponseReason:kMissingPasswordReason]);
  1545. return;
  1546. }
  1547. if (![request.email length]) {
  1548. completion(nil, [FIRAuthErrorUtils missingEmailErrorWithMessage:nil]);
  1549. return;
  1550. }
  1551. [FIRAuthBackend signUpNewUser:request callback:completion];
  1552. }
  1553. /** @fn internalSignInAnonymouslyWithCompletion:
  1554. @param completion A block which is invoked when the anonymous sign in request completes.
  1555. */
  1556. - (void)internalSignInAnonymouslyWithCompletion:(FIRSignupNewUserCallback)completion {
  1557. FIRSignUpNewUserRequest *request =
  1558. [[FIRSignUpNewUserRequest alloc] initWithRequestConfiguration:_requestConfiguration];
  1559. [FIRAuthBackend signUpNewUser:request callback:completion];
  1560. }
  1561. /** @fn possiblyPostAuthStateChangeNotification
  1562. @brief Posts the auth state change notificaton if current user's token has been changed.
  1563. */
  1564. - (void)possiblyPostAuthStateChangeNotification {
  1565. NSString *token = _currentUser.rawAccessToken;
  1566. if (_lastNotifiedUserToken == token ||
  1567. (token != nil && [_lastNotifiedUserToken isEqualToString:token])) {
  1568. return;
  1569. }
  1570. _lastNotifiedUserToken = token;
  1571. if (_autoRefreshTokens) {
  1572. // Shedule new refresh task after successful attempt.
  1573. [self scheduleAutoTokenRefresh];
  1574. }
  1575. NSMutableDictionary *internalNotificationParameters = [NSMutableDictionary dictionary];
  1576. if (self.app) {
  1577. internalNotificationParameters[FIRAuthStateDidChangeInternalNotificationAppKey] = self.app;
  1578. }
  1579. if (token.length) {
  1580. internalNotificationParameters[FIRAuthStateDidChangeInternalNotificationTokenKey] = token;
  1581. }
  1582. internalNotificationParameters[FIRAuthStateDidChangeInternalNotificationUIDKey] =
  1583. _currentUser.uid;
  1584. NSNotificationCenter *notifications = [NSNotificationCenter defaultCenter];
  1585. dispatch_async(dispatch_get_main_queue(), ^{
  1586. [notifications postNotificationName:FIRAuthStateDidChangeInternalNotification
  1587. object:self
  1588. userInfo:internalNotificationParameters];
  1589. [notifications postNotificationName:FIRAuthStateDidChangeNotification object:self];
  1590. });
  1591. }
  1592. - (BOOL)updateKeychainWithUser:(FIRUser *)user error:(NSError *_Nullable *_Nullable)error {
  1593. if (user != _currentUser) {
  1594. // No-op if the user is no longer signed in. This is not considered an error as we don't check
  1595. // whether the user is still current on other callbacks of user operations either.
  1596. return YES;
  1597. }
  1598. if ([self saveUser:user error:error]) {
  1599. [self possiblyPostAuthStateChangeNotification];
  1600. return YES;
  1601. }
  1602. return NO;
  1603. }
  1604. /** @fn setKeychainServiceNameForApp
  1605. @brief Sets the keychain service name global data for the particular app.
  1606. @param app The Firebase app to set keychain service name for.
  1607. */
  1608. + (void)setKeychainServiceNameForApp:(FIRApp *)app {
  1609. @synchronized(self) {
  1610. gKeychainServiceNameForAppName[app.name] =
  1611. [@"firebase_auth_" stringByAppendingString:app.options.googleAppID];
  1612. }
  1613. }
  1614. /** @fn keychainServiceNameForAppName:
  1615. @brief Gets the keychain service name global data for the particular app by name.
  1616. @param appName The name of the Firebase app to get keychain service name for.
  1617. */
  1618. + (NSString *)keychainServiceNameForAppName:(NSString *)appName {
  1619. @synchronized(self) {
  1620. return gKeychainServiceNameForAppName[appName];
  1621. }
  1622. }
  1623. /** @fn deleteKeychainServiceNameForAppName:
  1624. @brief Deletes the keychain service name global data for the particular app by name.
  1625. @param appName The name of the Firebase app to delete keychain service name for.
  1626. */
  1627. + (void)deleteKeychainServiceNameForAppName:(NSString *)appName {
  1628. @synchronized(self) {
  1629. [gKeychainServiceNameForAppName removeObjectForKey:appName];
  1630. }
  1631. }
  1632. /** @fn scheduleAutoTokenRefreshWithDelay:
  1633. @brief Schedules a task to automatically refresh tokens on the current user. The token refresh
  1634. is scheduled 5 minutes before the scheduled expiration time.
  1635. @remarks If the token expires in less than 5 minutes, schedule the token refresh immediately.
  1636. */
  1637. - (void)scheduleAutoTokenRefresh {
  1638. NSTimeInterval tokenExpirationInterval =
  1639. [_currentUser.accessTokenExpirationDate timeIntervalSinceNow] - kTokenRefreshHeadStart;
  1640. [self scheduleAutoTokenRefreshWithDelay:MAX(tokenExpirationInterval, 0) retry:NO];
  1641. }
  1642. /** @fn scheduleAutoTokenRefreshWithDelay:
  1643. @brief Schedules a task to automatically refresh tokens on the current user.
  1644. @param delay The delay in seconds after which the token refresh task should be scheduled to be
  1645. executed.
  1646. @param retry Flag to determine whether the invocation is a retry attempt or not.
  1647. */
  1648. - (void)scheduleAutoTokenRefreshWithDelay:(NSTimeInterval)delay retry:(BOOL)retry {
  1649. NSString *accessToken = _currentUser.rawAccessToken;
  1650. if (!accessToken) {
  1651. return;
  1652. }
  1653. if (retry) {
  1654. FIRLogInfo(kFIRLoggerAuth, @"I-AUT000003",
  1655. @"Token auto-refresh re-scheduled in %02d:%02d "
  1656. @"because of error on previous refresh attempt.",
  1657. (int)ceil(delay) / 60, (int)ceil(delay) % 60);
  1658. } else {
  1659. FIRLogInfo(kFIRLoggerAuth, @"I-AUT000004",
  1660. @"Token auto-refresh scheduled in %02d:%02d for the new token.",
  1661. (int)ceil(delay) / 60, (int)ceil(delay) % 60);
  1662. }
  1663. _autoRefreshScheduled = YES;
  1664. __weak FIRAuth *weakSelf = self;
  1665. [[FIRAuthDispatcher sharedInstance]
  1666. dispatchAfterDelay:delay
  1667. queue:FIRAuthGlobalWorkQueue()
  1668. task:^(void) {
  1669. FIRAuth *strongSelf = weakSelf;
  1670. if (!strongSelf) {
  1671. return;
  1672. }
  1673. if (![strongSelf->_currentUser.rawAccessToken isEqualToString:accessToken]) {
  1674. // Another auto refresh must have been scheduled, so keep
  1675. // _autoRefreshScheduled unchanged.
  1676. return;
  1677. }
  1678. strongSelf->_autoRefreshScheduled = NO;
  1679. if (strongSelf->_isAppInBackground) {
  1680. return;
  1681. }
  1682. NSString *uid = strongSelf->_currentUser.uid;
  1683. [strongSelf->_currentUser
  1684. internalGetTokenForcingRefresh:YES
  1685. callback:^(NSString *_Nullable token,
  1686. NSError *_Nullable error) {
  1687. if (![strongSelf->_currentUser.uid
  1688. isEqualToString:uid]) {
  1689. return;
  1690. }
  1691. if (error) {
  1692. // Kicks off exponential back off logic to retry
  1693. // failed attempt. Starts with one minute delay
  1694. // (60 seconds) if this is the first failed
  1695. // attempt.
  1696. NSTimeInterval rescheduleDelay;
  1697. if (retry) {
  1698. rescheduleDelay =
  1699. MIN(delay * 2, kMaxWaitTimeForBackoff);
  1700. } else {
  1701. rescheduleDelay = 60;
  1702. }
  1703. [strongSelf
  1704. scheduleAutoTokenRefreshWithDelay:
  1705. rescheduleDelay
  1706. retry:YES];
  1707. }
  1708. }];
  1709. }];
  1710. }
  1711. #pragma mark -
  1712. /** @fn completeSignInWithTokenService:callback:
  1713. @brief Completes a sign-in flow once we have access and refresh tokens for the user.
  1714. @param accessToken The STS access token.
  1715. @param accessTokenExpirationDate The approximate expiration date of the access token.
  1716. @param refreshToken The STS refresh token.
  1717. @param anonymous Whether or not the user is anonymous.
  1718. @param callback Called when the user has been signed in or when an error occurred. Invoked
  1719. asynchronously on the global auth work queue in the future.
  1720. */
  1721. - (void)completeSignInWithAccessToken:(nullable NSString *)accessToken
  1722. accessTokenExpirationDate:(nullable NSDate *)accessTokenExpirationDate
  1723. refreshToken:(nullable NSString *)refreshToken
  1724. anonymous:(BOOL)anonymous
  1725. callback:(FIRAuthResultCallback)callback {
  1726. [FIRUser retrieveUserWithAuth:self
  1727. accessToken:accessToken
  1728. accessTokenExpirationDate:accessTokenExpirationDate
  1729. refreshToken:refreshToken
  1730. anonymous:anonymous
  1731. callback:callback];
  1732. }
  1733. /** @fn signInFlowAuthResultCallbackByDecoratingCallback:
  1734. @brief Creates a FIRAuthResultCallback block which wraps another FIRAuthResultCallback; trying
  1735. to update the current user before forwarding it's invocations along to a subject block
  1736. @param callback Called when the user has been updated or when an error has occurred. Invoked
  1737. asynchronously on the main thread in the future.
  1738. @return Returns a block that updates the current user.
  1739. @remarks Typically invoked as part of the complete sign-in flow. For any other uses please
  1740. consider alternative ways of updating the current user.
  1741. */
  1742. - (FIRAuthResultCallback)signInFlowAuthResultCallbackByDecoratingCallback:
  1743. (nullable FIRAuthResultCallback)callback {
  1744. return ^(FIRUser *_Nullable user, NSError *_Nullable error) {
  1745. if (error) {
  1746. if (callback) {
  1747. dispatch_async(dispatch_get_main_queue(), ^{
  1748. callback(nil, error);
  1749. });
  1750. }
  1751. return;
  1752. }
  1753. if (![self updateCurrentUser:user byForce:NO savingToDisk:YES error:&error]) {
  1754. if (callback) {
  1755. dispatch_async(dispatch_get_main_queue(), ^{
  1756. callback(nil, error);
  1757. });
  1758. }
  1759. return;
  1760. }
  1761. if (callback) {
  1762. dispatch_async(dispatch_get_main_queue(), ^{
  1763. callback(user, nil);
  1764. });
  1765. }
  1766. };
  1767. }
  1768. /** @fn signInFlowAuthDataResultCallbackByDecoratingCallback:
  1769. @brief Creates a FIRAuthDataResultCallback block which wraps another FIRAuthDataResultCallback;
  1770. trying to update the current user before forwarding it's invocations along to a subject
  1771. block.
  1772. @param callback Called when the user has been updated or when an error has occurred. Invoked
  1773. asynchronously on the main thread in the future.
  1774. @return Returns a block that updates the current user.
  1775. @remarks Typically invoked as part of the complete sign-in flow. For any other uses please
  1776. consider alternative ways of updating the current user.
  1777. */
  1778. - (FIRAuthDataResultCallback)signInFlowAuthDataResultCallbackByDecoratingCallback:
  1779. (nullable FIRAuthDataResultCallback)callback {
  1780. return ^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) {
  1781. if (error) {
  1782. if (callback) {
  1783. dispatch_async(dispatch_get_main_queue(), ^{
  1784. callback(nil, error);
  1785. });
  1786. }
  1787. return;
  1788. }
  1789. if (![self updateCurrentUser:authResult.user byForce:NO savingToDisk:YES error:&error]) {
  1790. if (callback) {
  1791. dispatch_async(dispatch_get_main_queue(), ^{
  1792. callback(nil, error);
  1793. });
  1794. }
  1795. return;
  1796. }
  1797. if (callback) {
  1798. dispatch_async(dispatch_get_main_queue(), ^{
  1799. callback(authResult, nil);
  1800. });
  1801. }
  1802. };
  1803. }
  1804. #pragma mark - User-Related Methods
  1805. /** @fn updateCurrentUser:byForce:savingToDisk:error:
  1806. @brief Update the current user; initializing the user's internal properties correctly, and
  1807. optionally saving the user to disk.
  1808. @remarks This method is called during: sign in and sign out events, as well as during class
  1809. initialization time. The only time the saveToDisk parameter should be set to NO is during
  1810. class initialization time because the user was just read from disk.
  1811. @param user The user to use as the current user (including nil, which is passed at sign out
  1812. time.)
  1813. @param saveToDisk Indicates the method should persist the user data to disk.
  1814. */
  1815. - (BOOL)updateCurrentUser:(nullable FIRUser *)user
  1816. byForce:(BOOL)force
  1817. savingToDisk:(BOOL)saveToDisk
  1818. error:(NSError *_Nullable *_Nullable)error {
  1819. if (user == _currentUser) {
  1820. [self possiblyPostAuthStateChangeNotification];
  1821. return YES;
  1822. }
  1823. BOOL success = YES;
  1824. if (saveToDisk) {
  1825. success = [self saveUser:user error:error];
  1826. }
  1827. if (success || force) {
  1828. _currentUser = user;
  1829. [self possiblyPostAuthStateChangeNotification];
  1830. }
  1831. return success;
  1832. }
  1833. /** @fn saveUser:error:
  1834. @brief Persists user.
  1835. @param user The user to save.
  1836. @param outError Return value for any error which occurs.
  1837. @return @YES on success, @NO otherwise.
  1838. */
  1839. - (BOOL)saveUser:(nullable FIRUser *)user error:(NSError *_Nullable *_Nullable)outError {
  1840. BOOL success;
  1841. if (!self.userAccessGroup) {
  1842. NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName];
  1843. if (!user) {
  1844. success = [_keychainServices removeDataForKey:userKey error:outError];
  1845. } else {
  1846. // Encode the user object.
  1847. NSMutableData *archiveData = [NSMutableData data];
  1848. // iOS 12 deprecation
  1849. #pragma clang diagnostic push
  1850. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  1851. NSKeyedArchiver *archiver =
  1852. [[NSKeyedArchiver alloc] initForWritingWithMutableData:archiveData];
  1853. #pragma clang diagnostic pop
  1854. [archiver encodeObject:user forKey:userKey];
  1855. [archiver finishEncoding];
  1856. // Save the user object's encoded value.
  1857. success = [_keychainServices setData:archiveData forKey:userKey error:outError];
  1858. }
  1859. } else {
  1860. if (!user) {
  1861. success = [self.storedUserManager removeStoredUserForAccessGroup:self.userAccessGroup
  1862. projectIdentifier:self.app.options.APIKey
  1863. error:outError];
  1864. } else {
  1865. success = [self.storedUserManager setStoredUser:user
  1866. forAccessGroup:self.userAccessGroup
  1867. projectIdentifier:self.app.options.APIKey
  1868. error:outError];
  1869. }
  1870. }
  1871. return success;
  1872. }
  1873. /** @fn getUser:error:
  1874. @brief Retrieves the saved user associated, if one exists, from the keychain.
  1875. @param outUser An out parameter which is populated with the saved user, if one exists.
  1876. @param error Return value for any error which occurs.
  1877. @return YES if the operation was a success (irrespective of whether or not a saved user existed
  1878. for the given @c firebaseAppId,) NO if an error occurred.
  1879. */
  1880. - (BOOL)getUser:(FIRUser *_Nullable *)outUser error:(NSError *_Nullable *_Nullable)error {
  1881. if (!self.userAccessGroup) {
  1882. NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName];
  1883. NSError *keychainError;
  1884. NSData *encodedUserData = [_keychainServices dataForKey:userKey error:&keychainError];
  1885. if (keychainError) {
  1886. if (error) {
  1887. *error = keychainError;
  1888. }
  1889. return NO;
  1890. }
  1891. if (!encodedUserData) {
  1892. *outUser = nil;
  1893. return YES;
  1894. }
  1895. // iOS 12 deprecation
  1896. #pragma clang diagnostic push
  1897. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  1898. NSKeyedUnarchiver *unarchiver =
  1899. [[NSKeyedUnarchiver alloc] initForReadingWithData:encodedUserData];
  1900. #pragma clang diagnostic pop
  1901. FIRUser *user = [unarchiver decodeObjectOfClass:[FIRUser class] forKey:userKey];
  1902. user.auth = self;
  1903. *outUser = user;
  1904. return YES;
  1905. } else {
  1906. FIRUser *user = [self.storedUserManager getStoredUserForAccessGroup:self.userAccessGroup
  1907. projectIdentifier:self.app.options.APIKey
  1908. error:error];
  1909. user.auth = self;
  1910. *outUser = user;
  1911. if (user) {
  1912. return YES;
  1913. } else {
  1914. if (error && *error) {
  1915. return NO;
  1916. } else {
  1917. return YES;
  1918. }
  1919. }
  1920. }
  1921. }
  1922. #pragma mark - Interoperability
  1923. + (nonnull NSArray<FIRComponent *> *)componentsToRegister {
  1924. FIRComponentCreationBlock authCreationBlock =
  1925. ^id _Nullable(FIRComponentContainer *_Nonnull container, BOOL *_Nonnull isCacheable) {
  1926. *isCacheable = YES;
  1927. return [[FIRAuth alloc] initWithApp:container.app];
  1928. };
  1929. FIRComponent *authInterop = [FIRComponent componentWithProtocol:@protocol(FIRAuthInterop)
  1930. instantiationTiming:FIRInstantiationTimingAlwaysEager
  1931. dependencies:@[]
  1932. creationBlock:authCreationBlock];
  1933. return @[ authInterop ];
  1934. }
  1935. #pragma mark - FIRComponentLifecycleMaintainer
  1936. - (void)appWillBeDeleted:(nonnull FIRApp *)app {
  1937. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  1938. // This doesn't stop any request already issued, see b/27704535 .
  1939. NSString *keychainServiceName = [FIRAuth keychainServiceNameForAppName:app.name];
  1940. if (keychainServiceName) {
  1941. [[self class] deleteKeychainServiceNameForAppName:app.name];
  1942. FIRAuthKeychainServices *keychain =
  1943. [[FIRAuthKeychainServices alloc] initWithService:keychainServiceName];
  1944. NSString *userKey = [NSString stringWithFormat:kUserKey, app.name];
  1945. [keychain removeDataForKey:userKey error:NULL];
  1946. }
  1947. dispatch_async(dispatch_get_main_queue(), ^{
  1948. // TODO: Move over to fire an event instead, once ready.
  1949. [[NSNotificationCenter defaultCenter] postNotificationName:FIRAuthStateDidChangeNotification
  1950. object:nil];
  1951. });
  1952. });
  1953. }
  1954. #pragma mark - FIRAuthInterop
  1955. - (void)getTokenForcingRefresh:(BOOL)forceRefresh withCallback:(FIRTokenCallback)callback {
  1956. __weak FIRAuth *weakSelf = self;
  1957. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  1958. FIRAuth *strongSelf = weakSelf;
  1959. // Enable token auto-refresh if not aleady enabled.
  1960. if (strongSelf && !strongSelf->_autoRefreshTokens) {
  1961. FIRLogInfo(kFIRLoggerAuth, @"I-AUT000002", @"Token auto-refresh enabled.");
  1962. strongSelf->_autoRefreshTokens = YES;
  1963. [strongSelf scheduleAutoTokenRefresh];
  1964. #if TARGET_OS_IOS || TARGET_OS_TV // TODO: Is a similar mechanism needed on macOS?
  1965. strongSelf->_applicationDidBecomeActiveObserver = [[NSNotificationCenter defaultCenter]
  1966. addObserverForName:UIApplicationDidBecomeActiveNotification
  1967. object:nil
  1968. queue:nil
  1969. usingBlock:^(NSNotification *notification) {
  1970. FIRAuth *strongSelf = weakSelf;
  1971. if (strongSelf) {
  1972. strongSelf->_isAppInBackground = NO;
  1973. if (!strongSelf->_autoRefreshScheduled) {
  1974. [weakSelf scheduleAutoTokenRefresh];
  1975. }
  1976. }
  1977. }];
  1978. strongSelf->_applicationDidEnterBackgroundObserver = [[NSNotificationCenter defaultCenter]
  1979. addObserverForName:UIApplicationDidEnterBackgroundNotification
  1980. object:nil
  1981. queue:nil
  1982. usingBlock:^(NSNotification *notification) {
  1983. FIRAuth *strongSelf = weakSelf;
  1984. if (strongSelf) {
  1985. strongSelf->_isAppInBackground = YES;
  1986. }
  1987. }];
  1988. #endif
  1989. }
  1990. // Call back with 'nil' if there is no current user.
  1991. if (!strongSelf || !strongSelf->_currentUser) {
  1992. dispatch_async(dispatch_get_main_queue(), ^{
  1993. callback(nil, nil);
  1994. });
  1995. return;
  1996. }
  1997. // Call back with current user token.
  1998. [strongSelf->_currentUser
  1999. internalGetTokenForcingRefresh:forceRefresh
  2000. callback:^(NSString *_Nullable token, NSError *_Nullable error) {
  2001. dispatch_async(dispatch_get_main_queue(), ^{
  2002. callback(token, error);
  2003. });
  2004. }];
  2005. });
  2006. }
  2007. - (nullable NSString *)getUserID {
  2008. return _currentUser.uid;
  2009. }
  2010. #pragma mark - Keychain sharing
  2011. - (BOOL)internalUseUserAccessGroup:(NSString *_Nullable)accessGroup
  2012. error:(NSError *_Nullable *_Nullable)outError {
  2013. BOOL success;
  2014. success = [self.storedUserManager setStoredUserAccessGroup:accessGroup error:outError];
  2015. if (!success) {
  2016. return NO;
  2017. }
  2018. FIRUser *user = [self getStoredUserForAccessGroup:accessGroup error:outError];
  2019. if (!user && outError && *outError) {
  2020. return NO;
  2021. }
  2022. success = [self updateCurrentUser:user byForce:NO savingToDisk:NO error:outError];
  2023. if (!success) {
  2024. return NO;
  2025. }
  2026. if (_userAccessGroup == nil && accessGroup != nil) {
  2027. NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName];
  2028. [_keychainServices removeDataForKey:userKey error:outError];
  2029. }
  2030. _userAccessGroup = accessGroup;
  2031. self->_lastNotifiedUserToken = user.rawAccessToken;
  2032. return YES;
  2033. }
  2034. - (BOOL)useUserAccessGroup:(NSString *_Nullable)accessGroup
  2035. error:(NSError *_Nullable *_Nullable)outError {
  2036. // self.storedUserManager is initialized asynchronously. Make sure it is done.
  2037. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  2038. });
  2039. return [self internalUseUserAccessGroup:accessGroup error:outError];
  2040. }
  2041. - (nullable FIRUser *)getStoredUserForAccessGroup:(NSString *_Nullable)accessGroup
  2042. error:(NSError *_Nullable *_Nullable)outError {
  2043. FIRUser *user;
  2044. if (!accessGroup) {
  2045. NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName];
  2046. NSData *encodedUserData = [_keychainServices dataForKey:userKey error:outError];
  2047. if (!encodedUserData) {
  2048. return nil;
  2049. }
  2050. // iOS 12 deprecation
  2051. #pragma clang diagnostic push
  2052. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  2053. NSKeyedUnarchiver *unarchiver =
  2054. [[NSKeyedUnarchiver alloc] initForReadingWithData:encodedUserData];
  2055. #pragma clang diagnostic pop
  2056. user = [unarchiver decodeObjectOfClass:[FIRUser class] forKey:userKey];
  2057. } else {
  2058. user = [self.storedUserManager getStoredUserForAccessGroup:self.userAccessGroup
  2059. projectIdentifier:self.app.options.APIKey
  2060. error:outError];
  2061. }
  2062. user.auth = self;
  2063. return user;
  2064. }
  2065. @end
  2066. NS_ASSUME_NONNULL_END