FIRAuth.m 103 KB

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