FIRUser.m 48 KB

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