FIRUser.m 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254
  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 "FIRUser_Internal.h"
  18. #import "FIRAdditionalUserInfo_Internal.h"
  19. #import "FIRAuth.h"
  20. #import "FIRAuthCredential_Internal.h"
  21. #import "FIRAuthDataResult_Internal.h"
  22. #import "FIRAuthErrorUtils.h"
  23. #import "FIRAuthGlobalWorkQueue.h"
  24. #import "FIRAuthSerialTaskQueue.h"
  25. #import "FIRAuthOperationType.h"
  26. #import "FIRAuth_Internal.h"
  27. #import "FIRAuthBackend.h"
  28. #import "FIRAuthRequestConfiguration.h"
  29. #import "FIRDeleteAccountRequest.h"
  30. #import "FIRDeleteAccountResponse.h"
  31. #import "FIREmailAuthProvider.h"
  32. #import "FIREmailPasswordAuthCredential.h"
  33. #import "FIRGetAccountInfoRequest.h"
  34. #import "FIRGetAccountInfoResponse.h"
  35. #import "FIRGetOOBConfirmationCodeRequest.h"
  36. #import "FIRGetOOBConfirmationCodeResponse.h"
  37. #import <FirebaseCore/FIRLogger.h>
  38. #import "FIRSecureTokenService.h"
  39. #import "FIRSetAccountInfoRequest.h"
  40. #import "FIRSetAccountInfoResponse.h"
  41. #import "FIRUserInfoImpl.h"
  42. #import "FIRUserMetadata_Internal.h"
  43. #import "FIRVerifyAssertionRequest.h"
  44. #import "FIRVerifyAssertionResponse.h"
  45. #import "FIRVerifyCustomTokenRequest.h"
  46. #import "FIRVerifyCustomTokenResponse.h"
  47. #import "FIRVerifyPasswordRequest.h"
  48. #import "FIRVerifyPasswordResponse.h"
  49. #import "FIRVerifyPhoneNumberRequest.h"
  50. #import "FIRVerifyPhoneNumberResponse.h"
  51. #if TARGET_OS_IOS
  52. #import "FIRPhoneAuthProvider.h"
  53. #import "AuthProviders/Phone/FIRPhoneAuthCredential_Internal.h"
  54. #endif
  55. NS_ASSUME_NONNULL_BEGIN
  56. /** @var kUserIDCodingKey
  57. @brief The key used to encode the user ID for NSSecureCoding.
  58. */
  59. static NSString *const kUserIDCodingKey = @"userID";
  60. /** @var kHasEmailPasswordCredentialCodingKey
  61. @brief The key used to encode the hasEmailPasswordCredential property for NSSecureCoding.
  62. */
  63. static NSString *const kHasEmailPasswordCredentialCodingKey = @"hasEmailPassword";
  64. /** @var kAnonymousCodingKey
  65. @brief The key used to encode the anonymous property for NSSecureCoding.
  66. */
  67. static NSString *const kAnonymousCodingKey = @"anonymous";
  68. /** @var kEmailCodingKey
  69. @brief The key used to encode the email property for NSSecureCoding.
  70. */
  71. static NSString *const kEmailCodingKey = @"email";
  72. /** @var kPhoneNumberCodingKey
  73. @brief The key used to encode the phoneNumber property for NSSecureCoding.
  74. */
  75. static NSString *const kPhoneNumberCodingKey = @"phoneNumber";
  76. /** @var kEmailVerifiedCodingKey
  77. @brief The key used to encode the isEmailVerified property for NSSecureCoding.
  78. */
  79. static NSString *const kEmailVerifiedCodingKey = @"emailVerified";
  80. /** @var kDisplayNameCodingKey
  81. @brief The key used to encode the displayName property for NSSecureCoding.
  82. */
  83. static NSString *const kDisplayNameCodingKey = @"displayName";
  84. /** @var kPhotoURLCodingKey
  85. @brief The key used to encode the photoURL property for NSSecureCoding.
  86. */
  87. static NSString *const kPhotoURLCodingKey = @"photoURL";
  88. /** @var kProviderDataKey
  89. @brief The key used to encode the providerData instance variable for NSSecureCoding.
  90. */
  91. static NSString *const kProviderDataKey = @"providerData";
  92. /** @var kAPIKeyCodingKey
  93. @brief The key used to encode the APIKey instance variable for NSSecureCoding.
  94. */
  95. static NSString *const kAPIKeyCodingKey = @"APIKey";
  96. /** @var kTokenServiceCodingKey
  97. @brief The key used to encode the tokenService instance variable for NSSecureCoding.
  98. */
  99. static NSString *const kTokenServiceCodingKey = @"tokenService";
  100. /** @var kMetadataCodingKey
  101. @brief The key used to encode the metadata instance variable for NSSecureCoding.
  102. */
  103. static NSString *const kMetadataCodingKey = @"metadata";
  104. /** @var kMissingUsersErrorMessage
  105. @brief The error message when there is no users array in the getAccountInfo response.
  106. */
  107. static NSString *const kMissingUsersErrorMessage = @"users";
  108. /** @typedef CallbackWithError
  109. @brief The type for a callback block that only takes an error parameter.
  110. */
  111. typedef void (^CallbackWithError)(NSError *_Nullable);
  112. /** @typedef CallbackWithUserAndError
  113. @brief The type for a callback block that takes a user parameter and an error parameter.
  114. */
  115. typedef void (^CallbackWithUserAndError)(FIRUser *_Nullable, NSError *_Nullable);
  116. /** @typedef CallbackWithUserAndError
  117. @brief The type for a callback block that takes a user parameter and an error parameter.
  118. */
  119. typedef void (^CallbackWithAuthDataResultAndError)(FIRAuthDataResult *_Nullable,
  120. NSError *_Nullable);
  121. /** @var kMissingPasswordReason
  122. @brief The reason why the @c FIRAuthErrorCodeWeakPassword error is thrown.
  123. @remarks This error message will be localized in the future.
  124. */
  125. static NSString *const kMissingPasswordReason = @"Missing Password";
  126. /** @fn callInMainThreadWithError
  127. @brief Calls a callback in main thread with error.
  128. @param callback The callback to be called in main thread.
  129. @param error The error to pass to callback.
  130. */
  131. static void callInMainThreadWithError(_Nullable CallbackWithError callback,
  132. NSError *_Nullable error) {
  133. if (callback) {
  134. dispatch_async(dispatch_get_main_queue(), ^{
  135. callback(error);
  136. });
  137. }
  138. }
  139. /** @fn callInMainThreadWithUserAndError
  140. @brief Calls a callback in main thread with user and error.
  141. @param callback The callback to be called in main thread.
  142. @param user The user to pass to callback if there is no error.
  143. @param error The error to pass to callback.
  144. */
  145. static void callInMainThreadWithUserAndError(_Nullable CallbackWithUserAndError callback,
  146. FIRUser *_Nonnull user,
  147. NSError *_Nullable error) {
  148. if (callback) {
  149. dispatch_async(dispatch_get_main_queue(), ^{
  150. callback(error ? nil : user, error);
  151. });
  152. }
  153. }
  154. /** @fn callInMainThreadWithUserAndError
  155. @brief Calls a callback in main thread with user and error.
  156. @param callback The callback to be called in main thread.
  157. @param result The result to pass to callback if there is no error.
  158. @param error The error to pass to callback.
  159. */
  160. static void callInMainThreadWithAuthDataResultAndError(
  161. _Nullable CallbackWithAuthDataResultAndError callback,
  162. FIRAuthDataResult *_Nullable result,
  163. NSError *_Nullable error) {
  164. if (callback) {
  165. dispatch_async(dispatch_get_main_queue(), ^{
  166. callback(result, error);
  167. });
  168. }
  169. }
  170. @interface FIRUserProfileChangeRequest ()
  171. /** @fn initWithUser:
  172. @brief Designated initializer.
  173. @param user The user for which we are updating profile information.
  174. */
  175. - (nullable instancetype)initWithUser:(FIRUser *)user NS_DESIGNATED_INITIALIZER;
  176. @end
  177. @implementation FIRUser {
  178. /** @var _hasEmailPasswordCredential
  179. @brief Whether or not the user can be authenticated by using Firebase email and password.
  180. */
  181. BOOL _hasEmailPasswordCredential;
  182. /** @var _providerData
  183. @brief Provider specific user data.
  184. */
  185. NSDictionary<NSString *, FIRUserInfoImpl *> *_providerData;
  186. /** @var _taskQueue
  187. @brief Used to serialize the update profile calls.
  188. */
  189. FIRAuthSerialTaskQueue *_taskQueue;
  190. /** @var _tokenService
  191. @brief A secure token service associated with this user. For performing token exchanges and
  192. refreshing access tokens.
  193. */
  194. FIRSecureTokenService *_tokenService;
  195. }
  196. #pragma mark - Properties
  197. // Explicitly @synthesize because these properties are defined in FIRUserInfo protocol.
  198. @synthesize uid = _userID;
  199. @synthesize displayName = _displayName;
  200. @synthesize photoURL = _photoURL;
  201. @synthesize email = _email;
  202. @synthesize phoneNumber = _phoneNumber;
  203. #pragma mark -
  204. + (void)retrieveUserWithAuth:(FIRAuth *)auth
  205. accessToken:(NSString *)accessToken
  206. accessTokenExpirationDate:(NSDate *)accessTokenExpirationDate
  207. refreshToken:(NSString *)refreshToken
  208. anonymous:(BOOL)anonymous
  209. callback:(FIRRetrieveUserCallback)callback {
  210. FIRSecureTokenService *tokenService =
  211. [[FIRSecureTokenService alloc] initWithRequestConfiguration:auth.requestConfiguration
  212. accessToken:accessToken
  213. accessTokenExpirationDate:accessTokenExpirationDate
  214. refreshToken:refreshToken];
  215. FIRUser *user = [[self alloc] initWithTokenService:tokenService];
  216. user.auth = auth;
  217. [user internalGetTokenWithCallback:^(NSString *_Nullable accessToken, NSError *_Nullable error) {
  218. if (error) {
  219. callback(nil, error);
  220. return;
  221. }
  222. FIRGetAccountInfoRequest *getAccountInfoRequest =
  223. [[FIRGetAccountInfoRequest alloc] initWithAccessToken:accessToken
  224. requestConfiguration:auth.requestConfiguration];
  225. [FIRAuthBackend getAccountInfo:getAccountInfoRequest
  226. callback:^(FIRGetAccountInfoResponse *_Nullable response,
  227. NSError *_Nullable error) {
  228. if (error) {
  229. // No need to sign out user here for errors because the user hasn't been signed in yet.
  230. callback(nil, error);
  231. return;
  232. }
  233. user->_anonymous = anonymous;
  234. [user updateWithGetAccountInfoResponse:response];
  235. callback(user, nil);
  236. }];
  237. }];
  238. }
  239. - (instancetype)initWithTokenService:(FIRSecureTokenService *)tokenService {
  240. self = [super init];
  241. if (self) {
  242. _providerData = @{ };
  243. _taskQueue = [[FIRAuthSerialTaskQueue alloc] init];
  244. _tokenService = tokenService;
  245. }
  246. return self;
  247. }
  248. #pragma mark - NSSecureCoding
  249. + (BOOL)supportsSecureCoding {
  250. return YES;
  251. }
  252. - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
  253. NSString *userID = [aDecoder decodeObjectOfClass:[NSString class] forKey:kUserIDCodingKey];
  254. BOOL hasAnonymousKey = [aDecoder containsValueForKey:kAnonymousCodingKey];
  255. BOOL anonymous = [aDecoder decodeBoolForKey:kAnonymousCodingKey];
  256. BOOL hasEmailPasswordCredential =
  257. [aDecoder decodeBoolForKey:kHasEmailPasswordCredentialCodingKey];
  258. NSString *displayName =
  259. [aDecoder decodeObjectOfClass:[NSString class] forKey:kDisplayNameCodingKey];
  260. NSURL *photoURL =
  261. [aDecoder decodeObjectOfClass:[NSURL class] forKey:kPhotoURLCodingKey];
  262. NSString *email =
  263. [aDecoder decodeObjectOfClass:[NSString class] forKey:kEmailCodingKey];
  264. NSString *phoneNumber =
  265. [aDecoder decodeObjectOfClass:[NSString class] forKey:kPhoneNumberCodingKey];
  266. BOOL emailVerified = [aDecoder decodeBoolForKey:kEmailVerifiedCodingKey];
  267. NSSet *providerDataClasses = [NSSet setWithArray:@[
  268. [NSDictionary class],
  269. [NSString class],
  270. [FIRUserInfoImpl class]
  271. ]];
  272. NSDictionary<NSString *, FIRUserInfoImpl *> *providerData =
  273. [aDecoder decodeObjectOfClasses:providerDataClasses forKey:kProviderDataKey];
  274. FIRSecureTokenService *tokenService =
  275. [aDecoder decodeObjectOfClass:[FIRSecureTokenService class] forKey:kTokenServiceCodingKey];
  276. FIRUserMetadata *metadata =
  277. [aDecoder decodeObjectOfClass:[FIRUserMetadata class] forKey:kMetadataCodingKey];
  278. if (!userID || !tokenService) {
  279. return nil;
  280. }
  281. self = [self initWithTokenService:tokenService];
  282. if (self) {
  283. _userID = userID;
  284. // Previous version of this code didn't save 'anonymous' bit directly but deduced it from
  285. // 'hasEmailPasswordCredential' and 'providerData' instead, so here backward compatibility is
  286. // provided to read old format data.
  287. _anonymous = hasAnonymousKey ? anonymous : (!hasEmailPasswordCredential && !providerData.count);
  288. _hasEmailPasswordCredential = hasEmailPasswordCredential;
  289. _email = email;
  290. _emailVerified = emailVerified;
  291. _displayName = displayName;
  292. _photoURL = photoURL;
  293. _providerData = providerData;
  294. _phoneNumber = phoneNumber;
  295. _metadata = metadata ?: [[FIRUserMetadata alloc] initWithCreationDate:nil lastSignInDate:nil];
  296. }
  297. return self;
  298. }
  299. - (void)encodeWithCoder:(NSCoder *)aCoder {
  300. [aCoder encodeObject:_userID forKey:kUserIDCodingKey];
  301. [aCoder encodeBool:_anonymous forKey:kAnonymousCodingKey];
  302. [aCoder encodeBool:_hasEmailPasswordCredential forKey:kHasEmailPasswordCredentialCodingKey];
  303. [aCoder encodeObject:_providerData forKey:kProviderDataKey];
  304. [aCoder encodeObject:_email forKey:kEmailCodingKey];
  305. [aCoder encodeObject:_phoneNumber forKey:kPhoneNumberCodingKey];
  306. [aCoder encodeBool:_emailVerified forKey:kEmailVerifiedCodingKey];
  307. [aCoder encodeObject:_photoURL forKey:kPhotoURLCodingKey];
  308. [aCoder encodeObject:_displayName forKey:kDisplayNameCodingKey];
  309. [aCoder encodeObject:_metadata forKey:kMetadataCodingKey];
  310. // The API key is encoded even it is not used in decoding to be compatible with previous versions
  311. // of the library.
  312. [aCoder encodeObject:_auth.requestConfiguration.APIKey forKey:kAPIKeyCodingKey];
  313. [aCoder encodeObject:_tokenService forKey:kTokenServiceCodingKey];
  314. }
  315. #pragma mark -
  316. - (void)setAuth:(nullable FIRAuth *)auth {
  317. _auth = auth;
  318. _tokenService.requestConfiguration = auth.requestConfiguration;
  319. }
  320. - (NSString *)providerID {
  321. return @"Firebase";
  322. }
  323. - (NSArray<id<FIRUserInfo>> *)providerData {
  324. return _providerData.allValues;
  325. }
  326. /** @fn getAccountInfoRefreshingCache:
  327. @brief Gets the users's account data from the server, updating our local values.
  328. @param callback Invoked when the request to getAccountInfo has completed, or when an error has
  329. been detected. Invoked asynchronously on the auth global work queue in the future.
  330. */
  331. - (void)getAccountInfoRefreshingCache:(void(^)(FIRGetAccountInfoResponseUser *_Nullable user,
  332. NSError *_Nullable error))callback {
  333. [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken, NSError *_Nullable error) {
  334. if (error) {
  335. callback(nil, error);
  336. return;
  337. }
  338. FIRGetAccountInfoRequest *getAccountInfoRequest =
  339. [[FIRGetAccountInfoRequest alloc] initWithAccessToken:accessToken
  340. requestConfiguration:_auth.requestConfiguration];
  341. [FIRAuthBackend getAccountInfo:getAccountInfoRequest
  342. callback:^(FIRGetAccountInfoResponse *_Nullable response,
  343. NSError *_Nullable error) {
  344. if (error) {
  345. [self signOutIfTokenIsInvalidWithError:error];
  346. callback(nil, error);
  347. return;
  348. }
  349. [self updateWithGetAccountInfoResponse:response];
  350. if (![self updateKeychain:&error]) {
  351. callback(nil, error);
  352. return;
  353. }
  354. callback(response.users.firstObject, nil);
  355. }];
  356. }];
  357. }
  358. - (void)updateWithGetAccountInfoResponse:(FIRGetAccountInfoResponse *)response {
  359. FIRGetAccountInfoResponseUser *user = response.users.firstObject;
  360. _userID = user.localID;
  361. _email = user.email;
  362. _emailVerified = user.emailVerified;
  363. _displayName = user.displayName;
  364. _photoURL = user.photoURL;
  365. _phoneNumber = user.phoneNumber;
  366. _hasEmailPasswordCredential = user.passwordHash.length > 0;
  367. _metadata =
  368. [[FIRUserMetadata alloc]initWithCreationDate:user.creationDate
  369. lastSignInDate:user.lastLoginDate];
  370. NSMutableDictionary<NSString *, FIRUserInfoImpl *> *providerData =
  371. [NSMutableDictionary dictionary];
  372. for (FIRGetAccountInfoResponseProviderUserInfo *providerUserInfo in user.providerUserInfo) {
  373. FIRUserInfoImpl *userInfo =
  374. [FIRUserInfoImpl userInfoWithGetAccountInfoResponseProviderUserInfo:providerUserInfo];
  375. if (userInfo) {
  376. providerData[providerUserInfo.providerID] = userInfo;
  377. }
  378. }
  379. _providerData = [providerData copy];
  380. }
  381. /** @fn executeUserUpdateWithChanges:callback:
  382. @brief Performs a setAccountInfo request by mutating the results of a getAccountInfo response,
  383. atomically in regards to other calls to this method.
  384. @param changeBlock A block responsible for mutating a template @c FIRSetAccountInfoRequest
  385. @param callback A block to invoke when the change is complete. Invoked asynchronously on the
  386. auth global work queue in the future.
  387. */
  388. - (void)executeUserUpdateWithChanges:(void(^)(FIRGetAccountInfoResponseUser *,
  389. FIRSetAccountInfoRequest *))changeBlock
  390. callback:(nonnull FIRUserProfileChangeCallback)callback {
  391. [_taskQueue enqueueTask:^(FIRAuthSerialTaskCompletionBlock _Nonnull complete) {
  392. [self getAccountInfoRefreshingCache:^(FIRGetAccountInfoResponseUser *_Nullable user,
  393. NSError *_Nullable error) {
  394. if (error) {
  395. complete();
  396. callback(error);
  397. return;
  398. }
  399. [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
  400. NSError *_Nullable error) {
  401. if (error) {
  402. complete();
  403. callback(error);
  404. return;
  405. }
  406. FIRAuthRequestConfiguration *configuration = _auth.requestConfiguration;
  407. // Mutate setAccountInfoRequest in block:
  408. FIRSetAccountInfoRequest *setAccountInfoRequest =
  409. [[FIRSetAccountInfoRequest alloc] initWithRequestConfiguration:configuration];
  410. setAccountInfoRequest.accessToken = accessToken;
  411. changeBlock(user, setAccountInfoRequest);
  412. // Execute request:
  413. [FIRAuthBackend setAccountInfo:setAccountInfoRequest
  414. callback:^(FIRSetAccountInfoResponse *_Nullable response,
  415. NSError *_Nullable error) {
  416. if (error) {
  417. [self signOutIfTokenIsInvalidWithError:error];
  418. complete();
  419. callback(error);
  420. return;
  421. }
  422. if (response.IDToken && response.refreshToken) {
  423. FIRSecureTokenService *tokenService = [[FIRSecureTokenService alloc]
  424. initWithRequestConfiguration:configuration
  425. accessToken:response.IDToken
  426. accessTokenExpirationDate:response.approximateExpirationDate
  427. refreshToken:response.refreshToken];
  428. [self setTokenService:tokenService callback:^(NSError *_Nullable error) {
  429. complete();
  430. callback(error);
  431. }];
  432. return;
  433. }
  434. complete();
  435. callback(nil);
  436. }];
  437. }];
  438. }];
  439. }];
  440. }
  441. /** @fn updateKeychain:
  442. @brief Updates the keychain for user token or info changes.
  443. @param error The error if NO is returned.
  444. @return Whether the operation is successful.
  445. */
  446. - (BOOL)updateKeychain:(NSError *_Nullable *_Nullable)error {
  447. return [_auth updateKeychainWithUser:self error:error];
  448. }
  449. /** @fn setTokenService:callback:
  450. @brief Sets a new token service for the @c FIRUser instance.
  451. @param tokenService The new token service object.
  452. @param callback The block to be called in the global auth working queue once finished.
  453. @remarks The method makes sure the token service has access and refresh token and the new tokens
  454. are saved in the keychain before calling back.
  455. */
  456. - (void)setTokenService:(FIRSecureTokenService *)tokenService
  457. callback:(nonnull CallbackWithError)callback {
  458. [tokenService fetchAccessTokenForcingRefresh:NO
  459. callback:^(NSString *_Nullable token,
  460. NSError *_Nullable error,
  461. BOOL tokenUpdated) {
  462. if (error) {
  463. callback(error);
  464. return;
  465. }
  466. _tokenService = tokenService;
  467. if (![self updateKeychain:&error]) {
  468. callback(error);
  469. return;
  470. }
  471. callback(nil);
  472. }];
  473. }
  474. #pragma mark -
  475. /** @fn updateEmail:password:callback:
  476. @brief Updates email address and/or password for the current user.
  477. @remarks May fail if there is already an email/password-based account for the same email
  478. address.
  479. @param email The email address for the user, if to be updated.
  480. @param password The new password for the user, if to be updated.
  481. @param callback The block called when the user profile change has finished. Invoked
  482. asynchronously on the auth global work queue in the future.
  483. @remarks May fail with a @c FIRAuthErrorCodeRequiresRecentLogin error code.
  484. Call @c reauthentateWithCredential:completion: beforehand to avoid this error case.
  485. */
  486. - (void)updateEmail:(nullable NSString *)email
  487. password:(nullable NSString *)password
  488. callback:(nonnull FIRUserProfileChangeCallback)callback {
  489. if (password && ![password length]){
  490. callback([FIRAuthErrorUtils weakPasswordErrorWithServerResponseReason:kMissingPasswordReason]);
  491. return;
  492. }
  493. BOOL hadEmailPasswordCredential = _hasEmailPasswordCredential;
  494. [self executeUserUpdateWithChanges:^(FIRGetAccountInfoResponseUser *user,
  495. FIRSetAccountInfoRequest *request) {
  496. if (email) {
  497. request.email = email;
  498. }
  499. if (password) {
  500. request.password = password;
  501. }
  502. }
  503. callback:^(NSError *error) {
  504. if (error) {
  505. callback(error);
  506. return;
  507. }
  508. if (email) {
  509. _email = email;
  510. }
  511. if (_email && password) {
  512. _anonymous = NO;
  513. _hasEmailPasswordCredential = YES;
  514. if (!hadEmailPasswordCredential) {
  515. // The list of providers need to be updated for the newly added email-password provider.
  516. [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
  517. NSError *_Nullable error) {
  518. if (error) {
  519. callback(error);
  520. return;
  521. }
  522. FIRAuthRequestConfiguration *requestConfiguration = _auth.requestConfiguration;
  523. FIRGetAccountInfoRequest *getAccountInfoRequest =
  524. [[FIRGetAccountInfoRequest alloc] initWithAccessToken:accessToken
  525. requestConfiguration:requestConfiguration];
  526. [FIRAuthBackend getAccountInfo:getAccountInfoRequest
  527. callback:^(FIRGetAccountInfoResponse *_Nullable response,
  528. NSError *_Nullable error) {
  529. if (error) {
  530. [self signOutIfTokenIsInvalidWithError:error];
  531. callback(error);
  532. return;
  533. }
  534. [self updateWithGetAccountInfoResponse:response];
  535. if (![self updateKeychain:&error]) {
  536. callback(error);
  537. return;
  538. }
  539. callback(nil);
  540. }];
  541. }];
  542. return;
  543. }
  544. }
  545. if (![self updateKeychain:&error]) {
  546. callback(error);
  547. return;
  548. }
  549. callback(nil);
  550. }];
  551. }
  552. - (void)updateEmail:(NSString *)email completion:(nullable FIRUserProfileChangeCallback)completion {
  553. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  554. [self updateEmail:email password:nil callback:^(NSError *_Nullable error) {
  555. callInMainThreadWithError(completion, error);
  556. }];
  557. });
  558. }
  559. - (void)updatePassword:(NSString *)password
  560. completion:(nullable FIRUserProfileChangeCallback)completion {
  561. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  562. [self updateEmail:nil password:password callback:^(NSError *_Nullable error){
  563. callInMainThreadWithError(completion, error);
  564. }];
  565. });
  566. }
  567. #if TARGET_OS_IOS
  568. /** @fn internalUpdateOrLinkPhoneNumberCredential:completion:
  569. @brief Updates the phone number for the user. On success, the cached user profile data is
  570. updated.
  571. @param phoneAuthCredential The new phone number credential corresponding to the phone number
  572. to be added to the firebaes account, if a phone number is already linked to the account this
  573. new phone number will replace it.
  574. @param isLinkOperation Boolean value indicating whether or not this is a link operation.
  575. @param completion Optionally; the block invoked when the user profile change has finished.
  576. Invoked asynchronously on the global work queue in the future.
  577. */
  578. - (void)internalUpdateOrLinkPhoneNumberCredential:(FIRPhoneAuthCredential *)phoneAuthCredential
  579. isLinkOperation:(BOOL)isLinkOperation
  580. completion:(FIRUserProfileChangeCallback)completion {
  581. [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
  582. NSError *_Nullable error) {
  583. if (error) {
  584. completion(error);
  585. return;
  586. }
  587. FIRAuthOperationType operation =
  588. isLinkOperation ? FIRAuthOperationTypeLink : FIRAuthOperationTypeUpdate;
  589. FIRVerifyPhoneNumberRequest *request = [[FIRVerifyPhoneNumberRequest alloc]
  590. initWithVerificationID:phoneAuthCredential.verificationID
  591. verificationCode:phoneAuthCredential.verificationCode
  592. operation:operation
  593. requestConfiguration:_auth.requestConfiguration];
  594. request.accessToken = accessToken;
  595. [FIRAuthBackend verifyPhoneNumber:request
  596. callback:^(FIRVerifyPhoneNumberResponse *_Nullable response,
  597. NSError *_Nullable error) {
  598. if (error) {
  599. [self signOutIfTokenIsInvalidWithError:error];
  600. completion(error);
  601. return;
  602. }
  603. // Get account info to update cached user info.
  604. [self getAccountInfoRefreshingCache:^(FIRGetAccountInfoResponseUser *_Nullable user,
  605. NSError *_Nullable error) {
  606. if (error) {
  607. [self signOutIfTokenIsInvalidWithError:error];
  608. completion(error);
  609. return;
  610. }
  611. _anonymous = NO;
  612. if (![self updateKeychain:&error]) {
  613. completion(error);
  614. return;
  615. }
  616. completion(nil);
  617. }];
  618. }];
  619. }];
  620. }
  621. - (void)updatePhoneNumberCredential:(FIRPhoneAuthCredential *)phoneAuthCredential
  622. completion:(nullable FIRUserProfileChangeCallback)completion {
  623. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  624. [self internalUpdateOrLinkPhoneNumberCredential:phoneAuthCredential
  625. isLinkOperation:NO
  626. completion:^(NSError *_Nullable error) {
  627. callInMainThreadWithError(completion, error);
  628. }];
  629. });
  630. }
  631. #endif
  632. - (FIRUserProfileChangeRequest *)profileChangeRequest {
  633. __block FIRUserProfileChangeRequest *result;
  634. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  635. result = [[FIRUserProfileChangeRequest alloc] initWithUser:self];
  636. });
  637. return result;
  638. }
  639. - (void)setDisplayName:(NSString *)displayName {
  640. _displayName = [displayName copy];
  641. }
  642. - (void)setPhotoURL:(NSURL *)photoURL {
  643. _photoURL = [photoURL copy];
  644. }
  645. - (NSString *)rawAccessToken {
  646. return _tokenService.rawAccessToken;
  647. }
  648. - (NSDate *)accessTokenExpirationDate {
  649. return _tokenService.accessTokenExpirationDate;
  650. }
  651. #pragma mark -
  652. - (void)reloadWithCompletion:(nullable FIRUserProfileChangeCallback)completion {
  653. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  654. [self getAccountInfoRefreshingCache:^(FIRGetAccountInfoResponseUser *_Nullable user,
  655. NSError *_Nullable error) {
  656. callInMainThreadWithError(completion, error);
  657. }];
  658. });
  659. }
  660. #pragma mark -
  661. - (void)reauthenticateWithCredential:(FIRAuthCredential *)credential
  662. completion:(nullable FIRUserProfileChangeCallback)completion {
  663. FIRAuthDataResultCallback callback = ^(FIRAuthDataResult *_Nullable authResult,
  664. NSError *_Nullable error) {
  665. completion(error);
  666. };
  667. [self reauthenticateAndRetrieveDataWithCredential:credential completion:callback];
  668. }
  669. - (void)
  670. reauthenticateAndRetrieveDataWithCredential:(FIRAuthCredential *) credential
  671. completion:(nullable FIRAuthDataResultCallback) completion {
  672. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  673. [_auth internalSignInAndRetrieveDataWithCredential:credential
  674. isReauthentication:YES
  675. callback:^(FIRAuthDataResult *_Nullable authResult,
  676. NSError *_Nullable error) {
  677. if (error) {
  678. // If "user not found" error returned by backend, translate to user mismatch error which is
  679. // more accurate.
  680. if (error.code == FIRAuthErrorCodeUserNotFound) {
  681. error = [FIRAuthErrorUtils userMismatchError];
  682. }
  683. callInMainThreadWithAuthDataResultAndError(completion, authResult, error);
  684. return;
  685. }
  686. if (![authResult.user.uid isEqual:[_auth getUID]]) {
  687. callInMainThreadWithAuthDataResultAndError(completion, authResult,
  688. [FIRAuthErrorUtils userMismatchError]);
  689. return;
  690. }
  691. // Successful reauthenticate
  692. [self setTokenService:authResult.user->_tokenService callback:^(NSError *_Nullable error) {
  693. callInMainThreadWithAuthDataResultAndError(completion, authResult, error);
  694. }];
  695. }];
  696. });
  697. }
  698. - (nullable NSString *)refreshToken {
  699. __block NSString *result;
  700. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  701. result = _tokenService.refreshToken;
  702. });
  703. return result;
  704. }
  705. - (void)getIDTokenWithCompletion:(nullable FIRAuthTokenCallback)completion {
  706. // |getTokenForcingRefresh:completion:| is also a public API so there is no need to dispatch to
  707. // global work queue here.
  708. [self getIDTokenForcingRefresh:NO completion:completion];
  709. }
  710. - (void)getTokenWithCompletion:(nullable FIRAuthTokenCallback)completion {
  711. [self getIDTokenWithCompletion:completion];
  712. }
  713. - (void)getIDTokenForcingRefresh:(BOOL)forceRefresh
  714. completion:(nullable FIRAuthTokenCallback)completion {
  715. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  716. [self internalGetTokenForcingRefresh:forceRefresh
  717. callback:^(NSString *_Nullable token, NSError *_Nullable error) {
  718. if (completion) {
  719. dispatch_async(dispatch_get_main_queue(), ^{
  720. completion(token, error);
  721. });
  722. }
  723. }];
  724. });
  725. }
  726. - (void)getTokenForcingRefresh:(BOOL)forceRefresh
  727. completion:(nullable FIRAuthTokenCallback)completion {
  728. [self getIDTokenForcingRefresh:forceRefresh completion:completion];
  729. }
  730. /** @fn internalGetTokenForcingRefresh:callback:
  731. @brief Retrieves the Firebase authentication token, possibly refreshing it if it has expired.
  732. @param callback The block to invoke when the token is available. Invoked asynchronously on the
  733. global work thread in the future.
  734. */
  735. - (void)internalGetTokenWithCallback:(nonnull FIRAuthTokenCallback)callback {
  736. [self internalGetTokenForcingRefresh:NO callback:callback];
  737. }
  738. - (void)internalGetTokenForcingRefresh:(BOOL)forceRefresh
  739. callback:(nonnull FIRAuthTokenCallback)callback {
  740. [_tokenService fetchAccessTokenForcingRefresh:forceRefresh
  741. callback:^(NSString *_Nullable token,
  742. NSError *_Nullable error,
  743. BOOL tokenUpdated) {
  744. if (error) {
  745. [self signOutIfTokenIsInvalidWithError:error];
  746. callback(nil, error);
  747. return;
  748. }
  749. if (tokenUpdated) {
  750. if (![self updateKeychain:&error]) {
  751. callback(nil, error);
  752. return;
  753. }
  754. }
  755. callback(token, nil);
  756. }];
  757. }
  758. - (void)linkWithCredential:(FIRAuthCredential *)credential
  759. completion:(nullable FIRAuthResultCallback)completion {
  760. FIRAuthDataResultCallback callback = ^(FIRAuthDataResult *_Nullable authResult,
  761. NSError *_Nullable error) {
  762. completion(authResult.user, error);
  763. };
  764. [self linkAndRetrieveDataWithCredential:credential completion:callback];
  765. }
  766. - (void)linkAndRetrieveDataWithCredential:(FIRAuthCredential *)credential
  767. completion:(nullable FIRAuthDataResultCallback)completion {
  768. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  769. if (_providerData[credential.provider]) {
  770. callInMainThreadWithAuthDataResultAndError(completion,
  771. nil,
  772. [FIRAuthErrorUtils providerAlreadyLinkedError]);
  773. return;
  774. }
  775. FIRAuthDataResult *result =
  776. [[FIRAuthDataResult alloc] initWithUser:self additionalUserInfo:nil];
  777. if ([credential isKindOfClass:[FIREmailPasswordAuthCredential class]]) {
  778. if (_hasEmailPasswordCredential) {
  779. callInMainThreadWithAuthDataResultAndError(completion,
  780. nil,
  781. [FIRAuthErrorUtils providerAlreadyLinkedError]);
  782. return;
  783. }
  784. FIREmailPasswordAuthCredential *emailPasswordCredential =
  785. (FIREmailPasswordAuthCredential *)credential;
  786. [self updateEmail:emailPasswordCredential.email
  787. password:emailPasswordCredential.password
  788. callback:^(NSError *error) {
  789. if (error) {
  790. callInMainThreadWithAuthDataResultAndError(completion, nil, error);
  791. } else {
  792. callInMainThreadWithAuthDataResultAndError(completion, result, nil);
  793. }
  794. }];
  795. return;
  796. }
  797. #if TARGET_OS_IOS
  798. if ([credential isKindOfClass:[FIRPhoneAuthCredential class]]) {
  799. FIRPhoneAuthCredential *phoneAuthCredential = (FIRPhoneAuthCredential *)credential;
  800. [self internalUpdateOrLinkPhoneNumberCredential:phoneAuthCredential
  801. isLinkOperation:YES
  802. completion:^(NSError *_Nullable error) {
  803. if (error){
  804. callInMainThreadWithAuthDataResultAndError(completion, nil, error);
  805. } else {
  806. callInMainThreadWithAuthDataResultAndError(completion, result, nil);
  807. }
  808. }];
  809. return;
  810. }
  811. #endif
  812. [_taskQueue enqueueTask:^(FIRAuthSerialTaskCompletionBlock _Nonnull complete) {
  813. CallbackWithAuthDataResultAndError completeWithError =
  814. ^(FIRAuthDataResult *result, NSError *error) {
  815. complete();
  816. callInMainThreadWithAuthDataResultAndError(completion, result, error);
  817. };
  818. [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
  819. NSError *_Nullable error) {
  820. if (error) {
  821. completeWithError(nil, error);
  822. return;
  823. }
  824. FIRAuthRequestConfiguration *requestConfiguration = _auth.requestConfiguration;
  825. FIRVerifyAssertionRequest *request =
  826. [[FIRVerifyAssertionRequest alloc] initWithProviderID:credential.provider
  827. requestConfiguration:requestConfiguration];
  828. [credential prepareVerifyAssertionRequest:request];
  829. request.accessToken = accessToken;
  830. [FIRAuthBackend verifyAssertion:request
  831. callback:^(FIRVerifyAssertionResponse *response, NSError *error) {
  832. if (error) {
  833. [self signOutIfTokenIsInvalidWithError:error];
  834. completeWithError(nil, error);
  835. return;
  836. }
  837. FIRAdditionalUserInfo *additionalUserInfo =
  838. [FIRAdditionalUserInfo userInfoWithVerifyAssertionResponse:response];
  839. FIRAuthDataResult *result =
  840. [[FIRAuthDataResult alloc] initWithUser:self additionalUserInfo:additionalUserInfo];
  841. // Update the new token and refresh user info again.
  842. _tokenService = [[FIRSecureTokenService alloc]
  843. initWithRequestConfiguration:requestConfiguration
  844. accessToken:response.IDToken
  845. accessTokenExpirationDate:response.approximateExpirationDate
  846. refreshToken:response.refreshToken];
  847. [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
  848. NSError *_Nullable error) {
  849. if (error) {
  850. completeWithError(nil, error);
  851. return;
  852. }
  853. FIRGetAccountInfoRequest *getAccountInfoRequest =
  854. [[FIRGetAccountInfoRequest alloc] initWithAccessToken:accessToken
  855. requestConfiguration:requestConfiguration];
  856. [FIRAuthBackend getAccountInfo:getAccountInfoRequest
  857. callback:^(FIRGetAccountInfoResponse *_Nullable response,
  858. NSError *_Nullable error) {
  859. if (error) {
  860. [self signOutIfTokenIsInvalidWithError:error];
  861. completeWithError(nil, error);
  862. return;
  863. }
  864. _anonymous = NO;
  865. [self updateWithGetAccountInfoResponse:response];
  866. if (![self updateKeychain:&error]) {
  867. completeWithError(nil, error);
  868. return;
  869. }
  870. completeWithError(result, nil);
  871. }];
  872. }];
  873. }];
  874. }];
  875. }];
  876. });
  877. }
  878. - (void)unlinkFromProvider:(NSString *)provider
  879. completion:(nullable FIRAuthResultCallback)completion {
  880. [_taskQueue enqueueTask:^(FIRAuthSerialTaskCompletionBlock _Nonnull complete) {
  881. CallbackWithError completeAndCallbackWithError = ^(NSError *error) {
  882. complete();
  883. callInMainThreadWithUserAndError(completion, self, error);
  884. };
  885. [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
  886. NSError *_Nullable error) {
  887. if (error) {
  888. completeAndCallbackWithError(error);
  889. return;
  890. }
  891. FIRAuthRequestConfiguration *requestConfiguration = _auth.requestConfiguration;
  892. FIRSetAccountInfoRequest *setAccountInfoRequest =
  893. [[FIRSetAccountInfoRequest alloc] initWithRequestConfiguration:requestConfiguration];
  894. setAccountInfoRequest.accessToken = accessToken;
  895. BOOL isEmailPasswordProvider = [provider isEqualToString:FIREmailAuthProviderID];
  896. if (isEmailPasswordProvider) {
  897. if (!_hasEmailPasswordCredential) {
  898. completeAndCallbackWithError([FIRAuthErrorUtils noSuchProviderError]);
  899. return;
  900. }
  901. setAccountInfoRequest.deleteAttributes = @[ FIRSetAccountInfoUserAttributePassword ];
  902. } else {
  903. if (!_providerData[provider]) {
  904. completeAndCallbackWithError([FIRAuthErrorUtils noSuchProviderError]);
  905. return;
  906. }
  907. setAccountInfoRequest.deleteProviders = @[ provider ];
  908. }
  909. [FIRAuthBackend setAccountInfo:setAccountInfoRequest
  910. callback:^(FIRSetAccountInfoResponse *_Nullable response,
  911. NSError *_Nullable error) {
  912. if (error) {
  913. [self signOutIfTokenIsInvalidWithError:error];
  914. completeAndCallbackWithError(error);
  915. return;
  916. }
  917. if (isEmailPasswordProvider) {
  918. _hasEmailPasswordCredential = NO;
  919. } else {
  920. // We can't just use the provider info objects in FIRSetAcccountInfoResponse because they
  921. // don't have localID and email fields. Remove the specific provider manually.
  922. NSMutableDictionary *mutableProviderData = [_providerData mutableCopy];
  923. [mutableProviderData removeObjectForKey:provider];
  924. _providerData = [mutableProviderData copy];
  925. #if TARGET_OS_IOS
  926. // After successfully unlinking a phone auth provider, remove the phone number from the
  927. // cached user info.
  928. if ([provider isEqualToString:FIRPhoneAuthProviderID]) {
  929. _phoneNumber = nil;
  930. }
  931. #endif
  932. }
  933. if (response.IDToken && response.refreshToken) {
  934. FIRSecureTokenService *tokenService = [[FIRSecureTokenService alloc]
  935. initWithRequestConfiguration:requestConfiguration
  936. accessToken:response.IDToken
  937. accessTokenExpirationDate:response.approximateExpirationDate
  938. refreshToken:response.refreshToken];
  939. [self setTokenService:tokenService callback:^(NSError *_Nullable error) {
  940. completeAndCallbackWithError(error);
  941. }];
  942. return;
  943. }
  944. if (![self updateKeychain:&error]) {
  945. completeAndCallbackWithError(error);
  946. return;
  947. }
  948. completeAndCallbackWithError(nil);
  949. }];
  950. }];
  951. }];
  952. }
  953. - (void)sendEmailVerificationWithCompletion:(nullable FIRSendEmailVerificationCallback)completion {
  954. [self sendEmailVerificationWithNullableActionCodeSettings:nil completion:completion];
  955. }
  956. - (void)sendEmailVerificationWithActionCodeSettings:(FIRActionCodeSettings *)actionCodeSettings
  957. completion:(nullable FIRSendEmailVerificationCallback)
  958. completion {
  959. [self sendEmailVerificationWithNullableActionCodeSettings:actionCodeSettings
  960. completion:completion];
  961. }
  962. /** @fn sendEmailVerificationWithNullableActionCodeSettings:completion:
  963. @brief Initiates email verification for the user.
  964. @param actionCodeSettings Optionally, a @c FIRActionCodeSettings object containing settings
  965. related to the handling action codes.
  966. */
  967. - (void)sendEmailVerificationWithNullableActionCodeSettings:(nullable FIRActionCodeSettings *)
  968. actionCodeSettings
  969. completion:
  970. (nullable FIRSendEmailVerificationCallback)
  971. completion {
  972. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  973. [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
  974. NSError *_Nullable error) {
  975. if (error) {
  976. callInMainThreadWithError(completion, error);
  977. return;
  978. }
  979. FIRAuthRequestConfiguration *configuration = _auth.requestConfiguration;
  980. FIRGetOOBConfirmationCodeRequest *request =
  981. [FIRGetOOBConfirmationCodeRequest verifyEmailRequestWithAccessToken:accessToken
  982. actionCodeSettings:actionCodeSettings
  983. requestConfiguration:configuration];
  984. [FIRAuthBackend getOOBConfirmationCode:request
  985. callback:^(FIRGetOOBConfirmationCodeResponse *_Nullable
  986. response,
  987. NSError *_Nullable error) {
  988. [self signOutIfTokenIsInvalidWithError:error];
  989. callInMainThreadWithError(completion, error);
  990. }];
  991. }];
  992. });
  993. }
  994. - (void)deleteWithCompletion:(nullable FIRUserProfileChangeCallback)completion {
  995. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  996. [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
  997. NSError *_Nullable error) {
  998. if (error) {
  999. callInMainThreadWithError(completion, error);
  1000. return;
  1001. }
  1002. FIRDeleteAccountRequest *deleteUserRequest =
  1003. [[FIRDeleteAccountRequest alloc] initWitLocalID:_userID
  1004. accessToken:accessToken
  1005. requestConfiguration:_auth.requestConfiguration];
  1006. [FIRAuthBackend deleteAccount:deleteUserRequest callback:^(NSError *_Nullable error) {
  1007. if (error) {
  1008. callInMainThreadWithError(completion, error);
  1009. return;
  1010. }
  1011. if (![_auth signOutByForceWithUserID:_userID error:&error]) {
  1012. callInMainThreadWithError(completion, error);
  1013. return;
  1014. }
  1015. callInMainThreadWithError(completion, error);
  1016. }];
  1017. }];
  1018. });
  1019. }
  1020. /** @fn signOutIfTokenIsInvalidWithError:
  1021. @brief Signs out this user if the user or the token is invalid.
  1022. @param error The error from the server.
  1023. */
  1024. - (void)signOutIfTokenIsInvalidWithError:(nullable NSError *)error {
  1025. NSInteger errorCode = error.code;
  1026. if (errorCode == FIRAuthErrorCodeUserNotFound ||
  1027. errorCode == FIRAuthErrorCodeUserDisabled ||
  1028. errorCode == FIRAuthErrorCodeInvalidUserToken ||
  1029. errorCode == FIRAuthErrorCodeUserTokenExpired) {
  1030. FIRLogNotice(kFIRLoggerAuth, @"I-AUT000016",
  1031. @"Invalid user token detected, user is automatically signed out.");
  1032. [_auth signOutByForceWithUserID:_userID error:NULL];
  1033. }
  1034. }
  1035. @end
  1036. @implementation FIRUserProfileChangeRequest {
  1037. /** @var _user
  1038. @brief The user associated with the change request.
  1039. */
  1040. FIRUser *_user;
  1041. /** @var _displayName
  1042. @brief The display name value to set if @c _displayNameSet is YES.
  1043. */
  1044. NSString *_displayName;
  1045. /** @var _displayNameSet
  1046. @brief Indicates the display name should be part of the change request.
  1047. */
  1048. BOOL _displayNameSet;
  1049. /** @var _photoURL
  1050. @brief The photo URL value to set if @c _displayNameSet is YES.
  1051. */
  1052. NSURL *_photoURL;
  1053. /** @var _photoURLSet
  1054. @brief Indicates the photo URL should be part of the change request.
  1055. */
  1056. BOOL _photoURLSet;
  1057. /** @var _consumed
  1058. @brief Indicates the @c commitChangesWithCallback: method has already been invoked.
  1059. */
  1060. BOOL _consumed;
  1061. }
  1062. - (nullable instancetype)initWithUser:(FIRUser *)user {
  1063. self = [super init];
  1064. if (self) {
  1065. _user = user;
  1066. }
  1067. return self;
  1068. }
  1069. - (nullable NSString *)displayName {
  1070. return _displayName;
  1071. }
  1072. - (void)setDisplayName:(nullable NSString *)displayName {
  1073. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  1074. if (_consumed) {
  1075. [NSException raise:NSInternalInconsistencyException
  1076. format:@"%@",
  1077. @"Invalid call to setDisplayName: after commitChangesWithCallback:."];
  1078. return;
  1079. }
  1080. _displayNameSet = YES;
  1081. _displayName = [displayName copy];
  1082. });
  1083. }
  1084. - (nullable NSURL *)photoURL {
  1085. return _photoURL;
  1086. }
  1087. - (void)setPhotoURL:(nullable NSURL *)photoURL {
  1088. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  1089. if (_consumed) {
  1090. [NSException raise:NSInternalInconsistencyException
  1091. format:@"%@",
  1092. @"Invalid call to setPhotoURL: after commitChangesWithCallback:."];
  1093. return;
  1094. }
  1095. _photoURLSet = YES;
  1096. _photoURL = [photoURL copy];
  1097. });
  1098. }
  1099. /** @fn hasUpdates
  1100. @brief Indicates at least one field has a value which needs to be committed.
  1101. */
  1102. - (BOOL)hasUpdates {
  1103. return _displayNameSet || _photoURLSet;
  1104. }
  1105. - (void)commitChangesWithCompletion:(nullable FIRUserProfileChangeCallback)completion {
  1106. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  1107. if (_consumed) {
  1108. [NSException raise:NSInternalInconsistencyException
  1109. format:@"%@",
  1110. @"commitChangesWithCallback: should only be called once."];
  1111. return;
  1112. }
  1113. _consumed = YES;
  1114. // Return fast if there is nothing to update:
  1115. if (![self hasUpdates]) {
  1116. callInMainThreadWithError(completion, nil);
  1117. return;
  1118. }
  1119. NSString *displayName = [_displayName copy];
  1120. BOOL displayNameWasSet = _displayNameSet;
  1121. NSURL *photoURL = [_photoURL copy];
  1122. BOOL photoURLWasSet = _photoURLSet;
  1123. [_user executeUserUpdateWithChanges:^(FIRGetAccountInfoResponseUser *user,
  1124. FIRSetAccountInfoRequest *request) {
  1125. if (photoURLWasSet) {
  1126. request.photoURL = photoURL;
  1127. }
  1128. if (displayNameWasSet) {
  1129. request.displayName = displayName;
  1130. }
  1131. }
  1132. callback:^(NSError *_Nullable error) {
  1133. if (error) {
  1134. callInMainThreadWithError(completion, error);
  1135. return;
  1136. }
  1137. if (displayNameWasSet) {
  1138. [_user setDisplayName:displayName];
  1139. }
  1140. if (photoURLWasSet) {
  1141. [_user setPhotoURL:photoURL];
  1142. }
  1143. if (![_user updateKeychain:&error]) {
  1144. callInMainThreadWithError(completion, error);
  1145. return;
  1146. }
  1147. callInMainThreadWithError(completion, nil);
  1148. }];
  1149. });
  1150. }
  1151. @end
  1152. NS_ASSUME_NONNULL_END