FIRAuth.m 101 KB

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