FIRAuth.m 123 KB

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