FIRUser.m 76 KB

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