FIRAuth.m 118 KB

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