FIRUser.m 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255
  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:self->_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 = self->_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. self->_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. self->_email = email;
  510. }
  511. if (self->_email && password) {
  512. self->_anonymous = NO;
  513. self->_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 = self->_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 Firebase 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:self->_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. self->_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. [self->_auth internalSignInAndRetrieveDataWithCredential:credential
  674. isReauthentication:YES
  675. callback:^(FIRAuthDataResult *_Nullable
  676. authResult,
  677. NSError *_Nullable error) {
  678. if (error) {
  679. // If "user not found" error returned by backend, translate to user mismatch error which is
  680. // more accurate.
  681. if (error.code == FIRAuthErrorCodeUserNotFound) {
  682. error = [FIRAuthErrorUtils userMismatchError];
  683. }
  684. callInMainThreadWithAuthDataResultAndError(completion, authResult, error);
  685. return;
  686. }
  687. if (![authResult.user.uid isEqual:[self->_auth getUID]]) {
  688. callInMainThreadWithAuthDataResultAndError(completion, authResult,
  689. [FIRAuthErrorUtils userMismatchError]);
  690. return;
  691. }
  692. // Successful reauthenticate
  693. [self setTokenService:authResult.user->_tokenService callback:^(NSError *_Nullable error) {
  694. callInMainThreadWithAuthDataResultAndError(completion, authResult, error);
  695. }];
  696. }];
  697. });
  698. }
  699. - (nullable NSString *)refreshToken {
  700. __block NSString *result;
  701. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  702. result = self->_tokenService.refreshToken;
  703. });
  704. return result;
  705. }
  706. - (void)getIDTokenWithCompletion:(nullable FIRAuthTokenCallback)completion {
  707. // |getTokenForcingRefresh:completion:| is also a public API so there is no need to dispatch to
  708. // global work queue here.
  709. [self getIDTokenForcingRefresh:NO completion:completion];
  710. }
  711. - (void)getTokenWithCompletion:(nullable FIRAuthTokenCallback)completion {
  712. [self getIDTokenWithCompletion:completion];
  713. }
  714. - (void)getIDTokenForcingRefresh:(BOOL)forceRefresh
  715. completion:(nullable FIRAuthTokenCallback)completion {
  716. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  717. [self internalGetTokenForcingRefresh:forceRefresh
  718. callback:^(NSString *_Nullable token, NSError *_Nullable error) {
  719. if (completion) {
  720. dispatch_async(dispatch_get_main_queue(), ^{
  721. completion(token, error);
  722. });
  723. }
  724. }];
  725. });
  726. }
  727. - (void)getTokenForcingRefresh:(BOOL)forceRefresh
  728. completion:(nullable FIRAuthTokenCallback)completion {
  729. [self getIDTokenForcingRefresh:forceRefresh completion:completion];
  730. }
  731. /** @fn internalGetTokenForcingRefresh:callback:
  732. @brief Retrieves the Firebase authentication token, possibly refreshing it if it has expired.
  733. @param callback The block to invoke when the token is available. Invoked asynchronously on the
  734. global work thread in the future.
  735. */
  736. - (void)internalGetTokenWithCallback:(nonnull FIRAuthTokenCallback)callback {
  737. [self internalGetTokenForcingRefresh:NO callback:callback];
  738. }
  739. - (void)internalGetTokenForcingRefresh:(BOOL)forceRefresh
  740. callback:(nonnull FIRAuthTokenCallback)callback {
  741. [_tokenService fetchAccessTokenForcingRefresh:forceRefresh
  742. callback:^(NSString *_Nullable token,
  743. NSError *_Nullable error,
  744. BOOL tokenUpdated) {
  745. if (error) {
  746. [self signOutIfTokenIsInvalidWithError:error];
  747. callback(nil, error);
  748. return;
  749. }
  750. if (tokenUpdated) {
  751. if (![self updateKeychain:&error]) {
  752. callback(nil, error);
  753. return;
  754. }
  755. }
  756. callback(token, nil);
  757. }];
  758. }
  759. - (void)linkWithCredential:(FIRAuthCredential *)credential
  760. completion:(nullable FIRAuthResultCallback)completion {
  761. FIRAuthDataResultCallback callback = ^(FIRAuthDataResult *_Nullable authResult,
  762. NSError *_Nullable error) {
  763. completion(authResult.user, error);
  764. };
  765. [self linkAndRetrieveDataWithCredential:credential completion:callback];
  766. }
  767. - (void)linkAndRetrieveDataWithCredential:(FIRAuthCredential *)credential
  768. completion:(nullable FIRAuthDataResultCallback)completion {
  769. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  770. if (self->_providerData[credential.provider]) {
  771. callInMainThreadWithAuthDataResultAndError(completion,
  772. nil,
  773. [FIRAuthErrorUtils providerAlreadyLinkedError]);
  774. return;
  775. }
  776. FIRAuthDataResult *result =
  777. [[FIRAuthDataResult alloc] initWithUser:self additionalUserInfo:nil];
  778. if ([credential isKindOfClass:[FIREmailPasswordAuthCredential class]]) {
  779. if (self->_hasEmailPasswordCredential) {
  780. callInMainThreadWithAuthDataResultAndError(completion,
  781. nil,
  782. [FIRAuthErrorUtils providerAlreadyLinkedError]);
  783. return;
  784. }
  785. FIREmailPasswordAuthCredential *emailPasswordCredential =
  786. (FIREmailPasswordAuthCredential *)credential;
  787. [self updateEmail:emailPasswordCredential.email
  788. password:emailPasswordCredential.password
  789. callback:^(NSError *error) {
  790. if (error) {
  791. callInMainThreadWithAuthDataResultAndError(completion, nil, error);
  792. } else {
  793. callInMainThreadWithAuthDataResultAndError(completion, result, nil);
  794. }
  795. }];
  796. return;
  797. }
  798. #if TARGET_OS_IOS
  799. if ([credential isKindOfClass:[FIRPhoneAuthCredential class]]) {
  800. FIRPhoneAuthCredential *phoneAuthCredential = (FIRPhoneAuthCredential *)credential;
  801. [self internalUpdateOrLinkPhoneNumberCredential:phoneAuthCredential
  802. isLinkOperation:YES
  803. completion:^(NSError *_Nullable error) {
  804. if (error){
  805. callInMainThreadWithAuthDataResultAndError(completion, nil, error);
  806. } else {
  807. callInMainThreadWithAuthDataResultAndError(completion, result, nil);
  808. }
  809. }];
  810. return;
  811. }
  812. #endif
  813. [self->_taskQueue enqueueTask:^(FIRAuthSerialTaskCompletionBlock _Nonnull complete) {
  814. CallbackWithAuthDataResultAndError completeWithError =
  815. ^(FIRAuthDataResult *result, NSError *error) {
  816. complete();
  817. callInMainThreadWithAuthDataResultAndError(completion, result, error);
  818. };
  819. [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
  820. NSError *_Nullable error) {
  821. if (error) {
  822. completeWithError(nil, error);
  823. return;
  824. }
  825. FIRAuthRequestConfiguration *requestConfiguration = self->_auth.requestConfiguration;
  826. FIRVerifyAssertionRequest *request =
  827. [[FIRVerifyAssertionRequest alloc] initWithProviderID:credential.provider
  828. requestConfiguration:requestConfiguration];
  829. [credential prepareVerifyAssertionRequest:request];
  830. request.accessToken = accessToken;
  831. [FIRAuthBackend verifyAssertion:request
  832. callback:^(FIRVerifyAssertionResponse *response, NSError *error) {
  833. if (error) {
  834. [self signOutIfTokenIsInvalidWithError:error];
  835. completeWithError(nil, error);
  836. return;
  837. }
  838. FIRAdditionalUserInfo *additionalUserInfo =
  839. [FIRAdditionalUserInfo userInfoWithVerifyAssertionResponse:response];
  840. FIRAuthDataResult *result =
  841. [[FIRAuthDataResult alloc] initWithUser:self additionalUserInfo:additionalUserInfo];
  842. // Update the new token and refresh user info again.
  843. self->_tokenService = [[FIRSecureTokenService alloc]
  844. initWithRequestConfiguration:requestConfiguration
  845. accessToken:response.IDToken
  846. accessTokenExpirationDate:response.approximateExpirationDate
  847. refreshToken:response.refreshToken];
  848. [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
  849. NSError *_Nullable error) {
  850. if (error) {
  851. completeWithError(nil, error);
  852. return;
  853. }
  854. FIRGetAccountInfoRequest *getAccountInfoRequest =
  855. [[FIRGetAccountInfoRequest alloc] initWithAccessToken:accessToken
  856. requestConfiguration:requestConfiguration];
  857. [FIRAuthBackend getAccountInfo:getAccountInfoRequest
  858. callback:^(FIRGetAccountInfoResponse *_Nullable response,
  859. NSError *_Nullable error) {
  860. if (error) {
  861. [self signOutIfTokenIsInvalidWithError:error];
  862. completeWithError(nil, error);
  863. return;
  864. }
  865. self->_anonymous = NO;
  866. [self updateWithGetAccountInfoResponse:response];
  867. if (![self updateKeychain:&error]) {
  868. completeWithError(nil, error);
  869. return;
  870. }
  871. completeWithError(result, nil);
  872. }];
  873. }];
  874. }];
  875. }];
  876. }];
  877. });
  878. }
  879. - (void)unlinkFromProvider:(NSString *)provider
  880. completion:(nullable FIRAuthResultCallback)completion {
  881. [_taskQueue enqueueTask:^(FIRAuthSerialTaskCompletionBlock _Nonnull complete) {
  882. CallbackWithError completeAndCallbackWithError = ^(NSError *error) {
  883. complete();
  884. callInMainThreadWithUserAndError(completion, self, error);
  885. };
  886. [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
  887. NSError *_Nullable error) {
  888. if (error) {
  889. completeAndCallbackWithError(error);
  890. return;
  891. }
  892. FIRAuthRequestConfiguration *requestConfiguration = self->_auth.requestConfiguration;
  893. FIRSetAccountInfoRequest *setAccountInfoRequest =
  894. [[FIRSetAccountInfoRequest alloc] initWithRequestConfiguration:requestConfiguration];
  895. setAccountInfoRequest.accessToken = accessToken;
  896. BOOL isEmailPasswordProvider = [provider isEqualToString:FIREmailAuthProviderID];
  897. if (isEmailPasswordProvider) {
  898. if (!self->_hasEmailPasswordCredential) {
  899. completeAndCallbackWithError([FIRAuthErrorUtils noSuchProviderError]);
  900. return;
  901. }
  902. setAccountInfoRequest.deleteAttributes = @[ FIRSetAccountInfoUserAttributePassword ];
  903. } else {
  904. if (!self->_providerData[provider]) {
  905. completeAndCallbackWithError([FIRAuthErrorUtils noSuchProviderError]);
  906. return;
  907. }
  908. setAccountInfoRequest.deleteProviders = @[ provider ];
  909. }
  910. [FIRAuthBackend setAccountInfo:setAccountInfoRequest
  911. callback:^(FIRSetAccountInfoResponse *_Nullable response,
  912. NSError *_Nullable error) {
  913. if (error) {
  914. [self signOutIfTokenIsInvalidWithError:error];
  915. completeAndCallbackWithError(error);
  916. return;
  917. }
  918. if (isEmailPasswordProvider) {
  919. self->_hasEmailPasswordCredential = NO;
  920. } else {
  921. // We can't just use the provider info objects in FIRSetAcccountInfoResponse because they
  922. // don't have localID and email fields. Remove the specific provider manually.
  923. NSMutableDictionary *mutableProviderData = [self->_providerData mutableCopy];
  924. [mutableProviderData removeObjectForKey:provider];
  925. self->_providerData = [mutableProviderData copy];
  926. #if TARGET_OS_IOS
  927. // After successfully unlinking a phone auth provider, remove the phone number from the
  928. // cached user info.
  929. if ([provider isEqualToString:FIRPhoneAuthProviderID]) {
  930. self->_phoneNumber = nil;
  931. }
  932. #endif
  933. }
  934. if (response.IDToken && response.refreshToken) {
  935. FIRSecureTokenService *tokenService = [[FIRSecureTokenService alloc]
  936. initWithRequestConfiguration:requestConfiguration
  937. accessToken:response.IDToken
  938. accessTokenExpirationDate:response.approximateExpirationDate
  939. refreshToken:response.refreshToken];
  940. [self setTokenService:tokenService callback:^(NSError *_Nullable error) {
  941. completeAndCallbackWithError(error);
  942. }];
  943. return;
  944. }
  945. if (![self updateKeychain:&error]) {
  946. completeAndCallbackWithError(error);
  947. return;
  948. }
  949. completeAndCallbackWithError(nil);
  950. }];
  951. }];
  952. }];
  953. }
  954. - (void)sendEmailVerificationWithCompletion:(nullable FIRSendEmailVerificationCallback)completion {
  955. [self sendEmailVerificationWithNullableActionCodeSettings:nil completion:completion];
  956. }
  957. - (void)sendEmailVerificationWithActionCodeSettings:(FIRActionCodeSettings *)actionCodeSettings
  958. completion:(nullable FIRSendEmailVerificationCallback)
  959. completion {
  960. [self sendEmailVerificationWithNullableActionCodeSettings:actionCodeSettings
  961. completion:completion];
  962. }
  963. /** @fn sendEmailVerificationWithNullableActionCodeSettings:completion:
  964. @brief Initiates email verification for the user.
  965. @param actionCodeSettings Optionally, a @c FIRActionCodeSettings object containing settings
  966. related to the handling action codes.
  967. */
  968. - (void)sendEmailVerificationWithNullableActionCodeSettings:(nullable FIRActionCodeSettings *)
  969. actionCodeSettings
  970. completion:
  971. (nullable FIRSendEmailVerificationCallback)
  972. completion {
  973. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  974. [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
  975. NSError *_Nullable error) {
  976. if (error) {
  977. callInMainThreadWithError(completion, error);
  978. return;
  979. }
  980. FIRAuthRequestConfiguration *configuration = self->_auth.requestConfiguration;
  981. FIRGetOOBConfirmationCodeRequest *request =
  982. [FIRGetOOBConfirmationCodeRequest verifyEmailRequestWithAccessToken:accessToken
  983. actionCodeSettings:actionCodeSettings
  984. requestConfiguration:configuration];
  985. [FIRAuthBackend getOOBConfirmationCode:request
  986. callback:^(FIRGetOOBConfirmationCodeResponse *_Nullable
  987. response,
  988. NSError *_Nullable error) {
  989. [self signOutIfTokenIsInvalidWithError:error];
  990. callInMainThreadWithError(completion, error);
  991. }];
  992. }];
  993. });
  994. }
  995. - (void)deleteWithCompletion:(nullable FIRUserProfileChangeCallback)completion {
  996. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  997. [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
  998. NSError *_Nullable error) {
  999. if (error) {
  1000. callInMainThreadWithError(completion, error);
  1001. return;
  1002. }
  1003. FIRDeleteAccountRequest *deleteUserRequest =
  1004. [[FIRDeleteAccountRequest alloc] initWitLocalID:self->_userID
  1005. accessToken:accessToken
  1006. requestConfiguration:self->_auth.requestConfiguration];
  1007. [FIRAuthBackend deleteAccount:deleteUserRequest callback:^(NSError *_Nullable error) {
  1008. if (error) {
  1009. callInMainThreadWithError(completion, error);
  1010. return;
  1011. }
  1012. if (![self->_auth signOutByForceWithUserID:self->_userID error:&error]) {
  1013. callInMainThreadWithError(completion, error);
  1014. return;
  1015. }
  1016. callInMainThreadWithError(completion, error);
  1017. }];
  1018. }];
  1019. });
  1020. }
  1021. /** @fn signOutIfTokenIsInvalidWithError:
  1022. @brief Signs out this user if the user or the token is invalid.
  1023. @param error The error from the server.
  1024. */
  1025. - (void)signOutIfTokenIsInvalidWithError:(nullable NSError *)error {
  1026. NSInteger errorCode = error.code;
  1027. if (errorCode == FIRAuthErrorCodeUserNotFound ||
  1028. errorCode == FIRAuthErrorCodeUserDisabled ||
  1029. errorCode == FIRAuthErrorCodeInvalidUserToken ||
  1030. errorCode == FIRAuthErrorCodeUserTokenExpired) {
  1031. FIRLogNotice(kFIRLoggerAuth, @"I-AUT000016",
  1032. @"Invalid user token detected, user is automatically signed out.");
  1033. [_auth signOutByForceWithUserID:_userID error:NULL];
  1034. }
  1035. }
  1036. @end
  1037. @implementation FIRUserProfileChangeRequest {
  1038. /** @var _user
  1039. @brief The user associated with the change request.
  1040. */
  1041. FIRUser *_user;
  1042. /** @var _displayName
  1043. @brief The display name value to set if @c _displayNameSet is YES.
  1044. */
  1045. NSString *_displayName;
  1046. /** @var _displayNameSet
  1047. @brief Indicates the display name should be part of the change request.
  1048. */
  1049. BOOL _displayNameSet;
  1050. /** @var _photoURL
  1051. @brief The photo URL value to set if @c _displayNameSet is YES.
  1052. */
  1053. NSURL *_photoURL;
  1054. /** @var _photoURLSet
  1055. @brief Indicates the photo URL should be part of the change request.
  1056. */
  1057. BOOL _photoURLSet;
  1058. /** @var _consumed
  1059. @brief Indicates the @c commitChangesWithCallback: method has already been invoked.
  1060. */
  1061. BOOL _consumed;
  1062. }
  1063. - (nullable instancetype)initWithUser:(FIRUser *)user {
  1064. self = [super init];
  1065. if (self) {
  1066. _user = user;
  1067. }
  1068. return self;
  1069. }
  1070. - (nullable NSString *)displayName {
  1071. return _displayName;
  1072. }
  1073. - (void)setDisplayName:(nullable NSString *)displayName {
  1074. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  1075. if (self->_consumed) {
  1076. [NSException raise:NSInternalInconsistencyException
  1077. format:@"%@",
  1078. @"Invalid call to setDisplayName: after commitChangesWithCallback:."];
  1079. return;
  1080. }
  1081. self->_displayNameSet = YES;
  1082. self->_displayName = [displayName copy];
  1083. });
  1084. }
  1085. - (nullable NSURL *)photoURL {
  1086. return _photoURL;
  1087. }
  1088. - (void)setPhotoURL:(nullable NSURL *)photoURL {
  1089. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  1090. if (self->_consumed) {
  1091. [NSException raise:NSInternalInconsistencyException
  1092. format:@"%@",
  1093. @"Invalid call to setPhotoURL: after commitChangesWithCallback:."];
  1094. return;
  1095. }
  1096. self->_photoURLSet = YES;
  1097. self->_photoURL = [photoURL copy];
  1098. });
  1099. }
  1100. /** @fn hasUpdates
  1101. @brief Indicates at least one field has a value which needs to be committed.
  1102. */
  1103. - (BOOL)hasUpdates {
  1104. return _displayNameSet || _photoURLSet;
  1105. }
  1106. - (void)commitChangesWithCompletion:(nullable FIRUserProfileChangeCallback)completion {
  1107. dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
  1108. if (self->_consumed) {
  1109. [NSException raise:NSInternalInconsistencyException
  1110. format:@"%@",
  1111. @"commitChangesWithCallback: should only be called once."];
  1112. return;
  1113. }
  1114. self->_consumed = YES;
  1115. // Return fast if there is nothing to update:
  1116. if (![self hasUpdates]) {
  1117. callInMainThreadWithError(completion, nil);
  1118. return;
  1119. }
  1120. NSString *displayName = [self->_displayName copy];
  1121. BOOL displayNameWasSet = self->_displayNameSet;
  1122. NSURL *photoURL = [self->_photoURL copy];
  1123. BOOL photoURLWasSet = self->_photoURLSet;
  1124. [self->_user executeUserUpdateWithChanges:^(FIRGetAccountInfoResponseUser *user,
  1125. FIRSetAccountInfoRequest *request) {
  1126. if (photoURLWasSet) {
  1127. request.photoURL = photoURL;
  1128. }
  1129. if (displayNameWasSet) {
  1130. request.displayName = displayName;
  1131. }
  1132. }
  1133. callback:^(NSError *_Nullable error) {
  1134. if (error) {
  1135. callInMainThreadWithError(completion, error);
  1136. return;
  1137. }
  1138. if (displayNameWasSet) {
  1139. [self->_user setDisplayName:displayName];
  1140. }
  1141. if (photoURLWasSet) {
  1142. [self->_user setPhotoURL:photoURL];
  1143. }
  1144. if (![self->_user updateKeychain:&error]) {
  1145. callInMainThreadWithError(completion, error);
  1146. return;
  1147. }
  1148. callInMainThreadWithError(completion, nil);
  1149. }];
  1150. });
  1151. }
  1152. @end
  1153. NS_ASSUME_NONNULL_END