FIRUser.m 90 KB

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