FIRAuth.m 124 KB

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