FIRAuth.m 97 KB

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