FIRUser.m 57 KB

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