FIRAuth.m 101 KB

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