Bez popisu

FIRAuth.m 81KB

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