FIRAuth.m 118 KB

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