| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906 |
- /*
- * Copyright 2017 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRAuth.h"
- #import "FirebaseAuth/Sources/Public/FirebaseAuth/FIREmailAuthProvider.h"
- #import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRFederatedAuthProvider.h"
- #import "FirebaseCore/Extension/FirebaseCoreInternal.h"
- #import "FirebaseAuth/Sources/Auth/FIRAuthDataResult_Internal.h"
- #import "FirebaseAuth/Sources/Auth/FIRAuthGlobalWorkQueue.h"
- #import "FirebaseAuth/Sources/Auth/FIRAuthOperationType.h"
- #import "FirebaseAuth/Sources/Auth/FIRAuthSerialTaskQueue.h"
- #import "FirebaseAuth/Sources/Auth/FIRAuthTokenResult_Internal.h"
- #import "FirebaseAuth/Sources/Auth/FIRAuth_Internal.h"
- #import "FirebaseAuth/Sources/AuthProvider/Email/FIREmailPasswordAuthCredential.h"
- #import "FirebaseAuth/Sources/AuthProvider/FIRAuthCredential_Internal.h"
- #import "FirebaseAuth/Sources/AuthProvider/GameCenter/FIRGameCenterAuthCredential.h"
- #import "FirebaseAuth/Sources/AuthProvider/OAuth/FIROAuthCredential_Internal.h"
- #import "FirebaseAuth/Sources/Backend/FIRAuthBackend.h"
- #import "FirebaseAuth/Sources/Backend/FIRAuthRequestConfiguration.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRDeleteAccountRequest.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRDeleteAccountResponse.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIREmailLinkSignInRequest.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIREmailLinkSignInResponse.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRFinalizePasskeyEnrollmentRequest.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRFinalizePasskeyEnrollmentResponse.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRGetAccountInfoRequest.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRGetAccountInfoResponse.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRGetOOBConfirmationCodeRequest.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRGetOOBConfirmationCodeResponse.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRSetAccountInfoRequest.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRSetAccountInfoResponse.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRSignInWithGameCenterRequest.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRSignInWithGameCenterResponse.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRSignUpNewUserRequest.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRSignUpNewUserResponse.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRStartPasskeyEnrollmentRequest.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRStartPasskeyEnrollmentResponse.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyAssertionRequest.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyAssertionResponse.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyCustomTokenRequest.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyCustomTokenResponse.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPasswordRequest.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPasswordResponse.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPhoneNumberRequest.h"
- #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPhoneNumberResponse.h"
- #import "FirebaseAuth/Sources/MultiFactor/FIRMultiFactor+Internal.h"
- #import "FirebaseAuth/Sources/SystemService/FIRSecureTokenService.h"
- #import "FirebaseAuth/Sources/User/FIRAdditionalUserInfo_Internal.h"
- #import "FirebaseAuth/Sources/User/FIRUserInfoImpl.h"
- #import "FirebaseAuth/Sources/User/FIRUserMetadata_Internal.h"
- #import "FirebaseAuth/Sources/User/FIRUser_Internal.h"
- #import "FirebaseAuth/Sources/Utilities/FIRAuthErrorUtils.h"
- #import "FirebaseAuth/Sources/Utilities/FIRAuthWebUtils.h"
- #if TARGET_OS_IOS
- #import "FirebaseAuth/Sources/AuthProvider/Phone/FIRPhoneAuthCredential_Internal.h"
- #import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRPhoneAuthProvider.h"
- #import "FirebaseAuth/Sources/Utilities/FIRAuthRecaptchaVerifier.h"
- #endif
- #if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_OSX || TARGET_OS_MACCATALYST
- #import <AuthenticationServices/AuthenticationServices.h>
- #endif
- NS_ASSUME_NONNULL_BEGIN
- /** @var kUserIDCodingKey
- @brief The key used to encode the user ID for NSSecureCoding.
- */
- static NSString *const kUserIDCodingKey = @"userID";
- /** @var kHasEmailPasswordCredentialCodingKey
- @brief The key used to encode the hasEmailPasswordCredential property for NSSecureCoding.
- */
- static NSString *const kHasEmailPasswordCredentialCodingKey = @"hasEmailPassword";
- /** @var kAnonymousCodingKey
- @brief The key used to encode the anonymous property for NSSecureCoding.
- */
- static NSString *const kAnonymousCodingKey = @"anonymous";
- /** @var kEmailCodingKey
- @brief The key used to encode the email property for NSSecureCoding.
- */
- static NSString *const kEmailCodingKey = @"email";
- /** @var kPhoneNumberCodingKey
- @brief The key used to encode the phoneNumber property for NSSecureCoding.
- */
- static NSString *const kPhoneNumberCodingKey = @"phoneNumber";
- /** @var kEmailVerifiedCodingKey
- @brief The key used to encode the isEmailVerified property for NSSecureCoding.
- */
- static NSString *const kEmailVerifiedCodingKey = @"emailVerified";
- /** @var kDisplayNameCodingKey
- @brief The key used to encode the displayName property for NSSecureCoding.
- */
- static NSString *const kDisplayNameCodingKey = @"displayName";
- /** @var kPhotoURLCodingKey
- @brief The key used to encode the photoURL property for NSSecureCoding.
- */
- static NSString *const kPhotoURLCodingKey = @"photoURL";
- /** @var kProviderDataKey
- @brief The key used to encode the providerData instance variable for NSSecureCoding.
- */
- static NSString *const kProviderDataKey = @"providerData";
- /** @var kAPIKeyCodingKey
- @brief The key used to encode the APIKey instance variable for NSSecureCoding.
- */
- static NSString *const kAPIKeyCodingKey = @"APIKey";
- /** @var kFirebaseAppIDCodingKey
- @brief The key used to encode the appID instance variable for NSSecureCoding.
- */
- static NSString *const kFirebaseAppIDCodingKey = @"firebaseAppID";
- /** @var kTokenServiceCodingKey
- @brief The key used to encode the tokenService instance variable for NSSecureCoding.
- */
- static NSString *const kTokenServiceCodingKey = @"tokenService";
- /** @var kMetadataCodingKey
- @brief The key used to encode the metadata instance variable for NSSecureCoding.
- */
- static NSString *const kMetadataCodingKey = @"metadata";
- static NSString *const kMultiFactorCodingKey = @"multiFactor";
- /** @var kTenantIDKey
- @brief The key used to encode the tenantID instance variable for NSSecureCoding.
- */
- static NSString *const kTenantIDCodingKey = @"tenantID";
- /** @var kMissingUsersErrorMessage
- @brief The error message when there is no users array in the getAccountInfo response.
- */
- static NSString *const kMissingUsersErrorMessage = @"users";
- /** @typedef CallbackWithError
- @brief The type for a callback block that only takes an error parameter.
- */
- typedef void (^CallbackWithError)(NSError *_Nullable);
- /** @typedef CallbackWithUserAndError
- @brief The type for a callback block that takes a user parameter and an error parameter.
- */
- typedef void (^CallbackWithUserAndError)(FIRUser *_Nullable, NSError *_Nullable);
- /** @typedef CallbackWithUserAndError
- @brief The type for a callback block that takes a user parameter and an error parameter.
- */
- typedef void (^CallbackWithAuthDataResultAndError)(FIRAuthDataResult *_Nullable,
- NSError *_Nullable);
- /** @var kMissingPasswordReason
- @brief The reason why the @c FIRAuthErrorCodeWeakPassword error is thrown.
- @remarks This error message will be localized in the future.
- */
- static NSString *const kMissingPasswordReason = @"Missing Password";
- /** @fn callInMainThreadWithError
- @brief Calls a callback in main thread with error.
- @param callback The callback to be called in main thread.
- @param error The error to pass to callback.
- */
- static void callInMainThreadWithError(_Nullable CallbackWithError callback,
- NSError *_Nullable error) {
- if (callback) {
- dispatch_async(dispatch_get_main_queue(), ^{
- callback(error);
- });
- }
- }
- /** @fn callInMainThreadWithUserAndError
- @brief Calls a callback in main thread with user and error.
- @param callback The callback to be called in main thread.
- @param user The user to pass to callback if there is no error.
- @param error The error to pass to callback.
- */
- static void callInMainThreadWithUserAndError(_Nullable CallbackWithUserAndError callback,
- FIRUser *_Nonnull user,
- NSError *_Nullable error) {
- if (callback) {
- dispatch_async(dispatch_get_main_queue(), ^{
- callback(error ? nil : user, error);
- });
- }
- }
- /** @fn callInMainThreadWithUserAndError
- @brief Calls a callback in main thread with user and error.
- @param callback The callback to be called in main thread.
- @param result The result to pass to callback if there is no error.
- @param error The error to pass to callback.
- */
- static void callInMainThreadWithAuthDataResultAndError(
- _Nullable CallbackWithAuthDataResultAndError callback,
- FIRAuthDataResult *_Nullable result,
- NSError *_Nullable error) {
- if (callback) {
- dispatch_async(dispatch_get_main_queue(), ^{
- callback(result, error);
- });
- }
- }
- @interface FIRUserProfileChangeRequest ()
- /** @fn initWithUser:
- @brief Designated initializer.
- @param user The user for which we are updating profile information.
- */
- - (nullable instancetype)initWithUser:(FIRUser *)user NS_DESIGNATED_INITIALIZER;
- @end
- @interface FIRUser ()
- /** @property anonymous
- @brief Whether the current user is anonymous.
- */
- @property(nonatomic, readwrite) BOOL anonymous;
- /** @property tenantID
- @brief The tenant ID of the current user. nil if none is available.
- */
- @property(nonatomic, readwrite, nullable) NSString *tenantID;
- @end
- @implementation FIRUser {
- /** @var _hasEmailPasswordCredential
- @brief Whether or not the user can be authenticated by using Firebase email and password.
- */
- BOOL _hasEmailPasswordCredential;
- /** @var _providerData
- @brief Provider specific user data.
- */
- NSDictionary<NSString *, FIRUserInfoImpl *> *_providerData;
- /** @var _taskQueue
- @brief Used to serialize the update profile calls.
- */
- FIRAuthSerialTaskQueue *_taskQueue;
- /** @var _tokenService
- @brief A secure token service associated with this user. For performing token exchanges and
- refreshing access tokens.
- */
- FIRSecureTokenService *_tokenService;
- }
- #pragma mark - Properties
- // Explicitly @synthesize because these properties are defined in FIRUserInfo protocol.
- @synthesize uid = _userID;
- @synthesize displayName = _displayName;
- @synthesize photoURL = _photoURL;
- @synthesize email = _email;
- @synthesize phoneNumber = _phoneNumber;
- #pragma mark -
- + (void)retrieveUserWithAuth:(FIRAuth *)auth
- accessToken:(nullable NSString *)accessToken
- accessTokenExpirationDate:(nullable NSDate *)accessTokenExpirationDate
- refreshToken:(nullable NSString *)refreshToken
- anonymous:(BOOL)anonymous
- callback:(FIRRetrieveUserCallback)callback {
- FIRSecureTokenService *tokenService =
- [[FIRSecureTokenService alloc] initWithRequestConfiguration:auth.requestConfiguration
- accessToken:accessToken
- accessTokenExpirationDate:accessTokenExpirationDate
- refreshToken:refreshToken];
- FIRUser *user = [[self alloc] initWithTokenService:tokenService];
- user.auth = auth;
- user.tenantID = auth.tenantID;
- user.requestConfiguration = auth.requestConfiguration;
- [user internalGetTokenWithCallback:^(NSString *_Nullable accessToken, NSError *_Nullable error) {
- if (error) {
- callback(nil, error);
- return;
- }
- FIRGetAccountInfoRequest *getAccountInfoRequest =
- [[FIRGetAccountInfoRequest alloc] initWithAccessToken:accessToken
- requestConfiguration:auth.requestConfiguration];
- [FIRAuthBackend
- getAccountInfo:getAccountInfoRequest
- callback:^(FIRGetAccountInfoResponse *_Nullable response, NSError *_Nullable error) {
- if (error) {
- // No need to sign out user here for errors because the user hasn't been signed in
- // yet.
- callback(nil, error);
- return;
- }
- user.anonymous = anonymous;
- [user updateWithGetAccountInfoResponse:response];
- callback(user, nil);
- }];
- }];
- }
- - (instancetype)initWithTokenService:(FIRSecureTokenService *)tokenService {
- self = [super init];
- if (self) {
- _providerData = @{};
- _taskQueue = [[FIRAuthSerialTaskQueue alloc] init];
- _tokenService = tokenService;
- }
- return self;
- }
- #pragma mark - NSSecureCoding
- + (BOOL)supportsSecureCoding {
- return YES;
- }
- - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
- NSString *userID = [aDecoder decodeObjectOfClass:[NSString class] forKey:kUserIDCodingKey];
- BOOL hasAnonymousKey = [aDecoder containsValueForKey:kAnonymousCodingKey];
- BOOL anonymous = [aDecoder decodeBoolForKey:kAnonymousCodingKey];
- BOOL hasEmailPasswordCredential =
- [aDecoder decodeBoolForKey:kHasEmailPasswordCredentialCodingKey];
- NSString *displayName = [aDecoder decodeObjectOfClass:[NSString class]
- forKey:kDisplayNameCodingKey];
- NSURL *photoURL = [aDecoder decodeObjectOfClass:[NSURL class] forKey:kPhotoURLCodingKey];
- NSString *email = [aDecoder decodeObjectOfClass:[NSString class] forKey:kEmailCodingKey];
- NSString *phoneNumber = [aDecoder decodeObjectOfClass:[NSString class]
- forKey:kPhoneNumberCodingKey];
- BOOL emailVerified = [aDecoder decodeBoolForKey:kEmailVerifiedCodingKey];
- NSSet *providerDataClasses =
- [NSSet setWithArray:@[ [NSDictionary class], [NSString class], [FIRUserInfoImpl class] ]];
- NSDictionary<NSString *, FIRUserInfoImpl *> *providerData =
- [aDecoder decodeObjectOfClasses:providerDataClasses forKey:kProviderDataKey];
- FIRSecureTokenService *tokenService = [aDecoder decodeObjectOfClass:[FIRSecureTokenService class]
- forKey:kTokenServiceCodingKey];
- FIRUserMetadata *metadata = [aDecoder decodeObjectOfClass:[FIRUserMetadata class]
- forKey:kMetadataCodingKey];
- NSString *tenantID = [aDecoder decodeObjectOfClass:[NSString class] forKey:kTenantIDCodingKey];
- NSString *APIKey = [aDecoder decodeObjectOfClass:[NSString class] forKey:kAPIKeyCodingKey];
- NSString *appID = [aDecoder decodeObjectOfClass:[NSString class] forKey:kFirebaseAppIDCodingKey];
- #if TARGET_OS_IOS
- FIRMultiFactor *multiFactor = [aDecoder decodeObjectOfClass:[FIRMultiFactor class]
- forKey:kMultiFactorCodingKey];
- #endif
- if (!userID || !tokenService) {
- return nil;
- }
- self = [self initWithTokenService:tokenService];
- if (self) {
- _userID = userID;
- // Previous version of this code didn't save 'anonymous' bit directly but deduced it from
- // 'hasEmailPasswordCredential' and 'providerData' instead, so here backward compatibility is
- // provided to read old format data.
- _anonymous = hasAnonymousKey ? anonymous : (!hasEmailPasswordCredential && !providerData.count);
- _hasEmailPasswordCredential = hasEmailPasswordCredential;
- _email = email;
- _emailVerified = emailVerified;
- _displayName = displayName;
- _photoURL = photoURL;
- _providerData = providerData;
- _phoneNumber = phoneNumber;
- _metadata = metadata ?: [[FIRUserMetadata alloc] initWithCreationDate:nil lastSignInDate:nil];
- _tenantID = tenantID;
- // The `heartbeatLogger` and `appCheck` will be set later via a property update.
- _requestConfiguration = [[FIRAuthRequestConfiguration alloc] initWithAPIKey:APIKey
- appID:appID
- auth:_auth
- heartbeatLogger:nil
- appCheck:nil];
- #if TARGET_OS_IOS
- _multiFactor = multiFactor ?: [[FIRMultiFactor alloc] init];
- _multiFactor.user = self;
- #endif
- }
- return self;
- }
- - (void)encodeWithCoder:(NSCoder *)aCoder {
- [aCoder encodeObject:_userID forKey:kUserIDCodingKey];
- [aCoder encodeBool:self.anonymous forKey:kAnonymousCodingKey];
- [aCoder encodeBool:_hasEmailPasswordCredential forKey:kHasEmailPasswordCredentialCodingKey];
- [aCoder encodeObject:_providerData forKey:kProviderDataKey];
- [aCoder encodeObject:_email forKey:kEmailCodingKey];
- [aCoder encodeObject:_phoneNumber forKey:kPhoneNumberCodingKey];
- [aCoder encodeBool:_emailVerified forKey:kEmailVerifiedCodingKey];
- [aCoder encodeObject:_photoURL forKey:kPhotoURLCodingKey];
- [aCoder encodeObject:_displayName forKey:kDisplayNameCodingKey];
- [aCoder encodeObject:_metadata forKey:kMetadataCodingKey];
- [aCoder encodeObject:_tenantID forKey:kTenantIDCodingKey];
- [aCoder encodeObject:_auth.requestConfiguration.APIKey forKey:kAPIKeyCodingKey];
- [aCoder encodeObject:_auth.requestConfiguration.appID forKey:kFirebaseAppIDCodingKey];
- [aCoder encodeObject:_tokenService forKey:kTokenServiceCodingKey];
- #if TARGET_OS_IOS
- [aCoder encodeObject:_multiFactor forKey:kMultiFactorCodingKey];
- #endif
- }
- #pragma mark -
- - (void)setAuth:(nullable FIRAuth *)auth {
- _auth = auth;
- _tokenService.requestConfiguration = auth.requestConfiguration;
- _requestConfiguration = auth.requestConfiguration;
- }
- - (NSString *)providerID {
- return @"Firebase";
- }
- - (NSArray<id<FIRUserInfo>> *)providerData {
- return _providerData.allValues;
- }
- /** @fn getAccountInfoRefreshingCache:
- @brief Gets the users's account data from the server, updating our local values.
- @param callback Invoked when the request to getAccountInfo has completed, or when an error has
- been detected. Invoked asynchronously on the auth global work queue in the future.
- */
- - (void)getAccountInfoRefreshingCache:(void (^)(FIRGetAccountInfoResponseUser *_Nullable user,
- NSError *_Nullable error))callback {
- [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken, NSError *_Nullable error) {
- if (error) {
- callback(nil, error);
- return;
- }
- FIRGetAccountInfoRequest *getAccountInfoRequest =
- [[FIRGetAccountInfoRequest alloc] initWithAccessToken:accessToken
- requestConfiguration:self->_auth.requestConfiguration];
- [FIRAuthBackend
- getAccountInfo:getAccountInfoRequest
- callback:^(FIRGetAccountInfoResponse *_Nullable response, NSError *_Nullable error) {
- if (error) {
- [self signOutIfTokenIsInvalidWithError:error];
- callback(nil, error);
- return;
- }
- [self updateWithGetAccountInfoResponse:response];
- if (![self updateKeychain:&error]) {
- callback(nil, error);
- return;
- }
- callback(response.users.firstObject, nil);
- }];
- }];
- }
- - (void)updateWithGetAccountInfoResponse:(FIRGetAccountInfoResponse *)response {
- FIRGetAccountInfoResponseUser *user = response.users.firstObject;
- _userID = user.localID;
- _email = user.email;
- _emailVerified = user.emailVerified;
- _displayName = user.displayName;
- _photoURL = user.photoURL;
- _phoneNumber = user.phoneNumber;
- _hasEmailPasswordCredential = user.passwordHash.length > 0;
- _metadata = [[FIRUserMetadata alloc] initWithCreationDate:user.creationDate
- lastSignInDate:user.lastLoginDate];
- NSMutableDictionary<NSString *, FIRUserInfoImpl *> *providerData =
- [NSMutableDictionary dictionary];
- for (FIRGetAccountInfoResponseProviderUserInfo *providerUserInfo in user.providerUserInfo) {
- FIRUserInfoImpl *userInfo =
- [FIRUserInfoImpl userInfoWithGetAccountInfoResponseProviderUserInfo:providerUserInfo];
- if (userInfo) {
- providerData[providerUserInfo.providerID] = userInfo;
- }
- }
- _providerData = [providerData copy];
- #if TARGET_OS_IOS
- _multiFactor = [[FIRMultiFactor alloc] initWithMFAEnrollments:user.MFAEnrollments];
- _multiFactor.user = self;
- #endif
- _enrolledPasskeys = [user.enrolledPasskeys copy];
- }
- /** @fn executeUserUpdateWithChanges:callback:
- @brief Performs a setAccountInfo request by mutating the results of a getAccountInfo response,
- atomically in regards to other calls to this method.
- @param changeBlock A block responsible for mutating a template @c FIRSetAccountInfoRequest
- @param callback A block to invoke when the change is complete. Invoked asynchronously on the
- auth global work queue in the future.
- */
- - (void)executeUserUpdateWithChanges:(void (^)(FIRGetAccountInfoResponseUser *,
- FIRSetAccountInfoRequest *))changeBlock
- callback:(nonnull FIRUserProfileChangeCallback)callback {
- [_taskQueue enqueueTask:^(FIRAuthSerialTaskCompletionBlock _Nonnull complete) {
- [self getAccountInfoRefreshingCache:^(FIRGetAccountInfoResponseUser *_Nullable user,
- NSError *_Nullable error) {
- if (error) {
- complete();
- callback(error);
- return;
- }
- [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
- NSError *_Nullable error) {
- if (error) {
- complete();
- callback(error);
- return;
- }
- FIRAuthRequestConfiguration *configuration = self->_auth.requestConfiguration;
- // Mutate setAccountInfoRequest in block:
- FIRSetAccountInfoRequest *setAccountInfoRequest =
- [[FIRSetAccountInfoRequest alloc] initWithRequestConfiguration:configuration];
- setAccountInfoRequest.accessToken = accessToken;
- changeBlock(user, setAccountInfoRequest);
- // Execute request:
- [FIRAuthBackend
- setAccountInfo:setAccountInfoRequest
- callback:^(FIRSetAccountInfoResponse *_Nullable response,
- NSError *_Nullable error) {
- if (error) {
- [self signOutIfTokenIsInvalidWithError:error];
- complete();
- callback(error);
- return;
- }
- if (response.IDToken && response.refreshToken) {
- FIRSecureTokenService *tokenService = [[FIRSecureTokenService alloc]
- initWithRequestConfiguration:configuration
- accessToken:response.IDToken
- accessTokenExpirationDate:response.approximateExpirationDate
- refreshToken:response.refreshToken];
- [self setTokenService:tokenService
- callback:^(NSError *_Nullable error) {
- complete();
- callback(error);
- }];
- return;
- }
- complete();
- callback(nil);
- }];
- }];
- }];
- }];
- }
- /** @fn updateKeychain:
- @brief Updates the keychain for user token or info changes.
- @param error The error if NO is returned.
- @return Whether the operation is successful.
- */
- - (BOOL)updateKeychain:(NSError *_Nullable *_Nullable)error {
- return [_auth updateKeychainWithUser:self error:error];
- }
- /** @fn setTokenService:callback:
- @brief Sets a new token service for the @c FIRUser instance.
- @param tokenService The new token service object.
- @param callback The block to be called in the global auth working queue once finished.
- @remarks The method makes sure the token service has access and refresh token and the new tokens
- are saved in the keychain before calling back.
- */
- - (void)setTokenService:(FIRSecureTokenService *)tokenService
- callback:(nonnull CallbackWithError)callback {
- [tokenService fetchAccessTokenForcingRefresh:NO
- callback:^(NSString *_Nullable token,
- NSError *_Nullable error, BOOL tokenUpdated) {
- if (error) {
- callback(error);
- return;
- }
- self->_tokenService = tokenService;
- if (![self updateKeychain:&error]) {
- callback(error);
- return;
- }
- callback(nil);
- }];
- }
- #pragma mark -
- #if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_OSX || TARGET_OS_MACCATALYST
- - (void)startPasskeyEnrollmentWithName:(nullable NSString *)name
- completion:
- (nullable void (^)(
- ASAuthorizationPlatformPublicKeyCredentialRegistrationRequest
- *_Nullable request,
- NSError *_Nullable error))completion {
- FIRAuthRequestConfiguration *requestConfiguration = self->_auth.requestConfiguration;
- FIRStartPasskeyEnrollmentRequest *request =
- [[FIRStartPasskeyEnrollmentRequest alloc] initWithIDToken:self.rawAccessToken
- requestConfiguration:requestConfiguration];
- [FIRAuthBackend
- startPasskeyEnrollment:request
- callback:^(FIRStartPasskeyEnrollmentResponse *_Nullable response,
- NSError *_Nullable error) {
- if (error) {
- // reset the passkey name cache.
- self.passkeyName = nil;
- completion(nil, error);
- return;
- } else {
- // cached the passkey name. This is needed when calling
- // finalizePasskeyEnrollment
- self.passkeyName = name;
- // If passkey name is not provided, we will provide a firebase formatted
- // default name.
- if (self.passkeyName == nil || [self.passkeyName isEqual:@""]) {
- self.passkeyName = @"Unnamed account (Apple)";
- }
- NSData *challengeInData =
- [[NSData alloc] initWithBase64EncodedString:response.challenge
- options:0];
- NSData *userIdInData =
- [[NSData alloc] initWithBase64EncodedString:response.userID options:0];
- ASAuthorizationPlatformPublicKeyCredentialProvider *provider =
- [[ASAuthorizationPlatformPublicKeyCredentialProvider alloc]
- initWithRelyingPartyIdentifier:response.rpID];
- ASAuthorizationPlatformPublicKeyCredentialRegistrationRequest *request =
- [provider
- createCredentialRegistrationRequestWithChallenge:challengeInData
- name:self.passkeyName
- userID:userIdInData];
- completion(request, nil);
- }
- }];
- }
- - (void)finalizePasskeyEnrollmentWithPlatformCredential:
- (ASAuthorizationPlatformPublicKeyCredentialRegistration *)platformCredential
- completion:(nullable void (^)(
- FIRAuthDataResult *_Nullable authResult,
- NSError *_Nullable error))completion {
- dispatch_async(FIRAuthGlobalWorkQueue(), ^{
- FIRAuthDataResultCallback decoratedCallback =
- [FIRAuth.auth signInFlowAuthDataResultCallbackByDecoratingCallback:completion];
- FIRAuthRequestConfiguration *requestConfiguration = self->_auth.requestConfiguration;
- NSString *credentialID = [platformCredential.credentialID base64EncodedStringWithOptions:0];
- NSString *clientDataJson =
- [platformCredential.rawClientDataJSON base64EncodedStringWithOptions:0];
- NSString *attestationObject =
- [platformCredential.rawAttestationObject base64EncodedStringWithOptions:0];
- FIRFinalizePasskeyEnrollmentRequest *request =
- [[FIRFinalizePasskeyEnrollmentRequest alloc] initWithIDToken:self.rawAccessToken
- name:self.passkeyName
- credentialID:credentialID
- clientDataJson:clientDataJson
- attestationObject:attestationObject
- requestConfiguration:requestConfiguration];
- [FIRAuthBackend
- finalizePasskeyEnrollment:request
- callback:^(FIRFinalizePasskeyEnrollmentResponse *_Nullable response,
- NSError *_Nullable error) {
- if (error) {
- decoratedCallback(nil, error);
- return;
- } else {
- [FIRAuth.auth
- completeSignInWithAccessToken:response.idToken
- accessTokenExpirationDate:nil
- refreshToken:response.refreshToken
- anonymous:NO
- callback:^(FIRUser *_Nullable user,
- NSError *_Nullable error) {
- if (error) {
- completion(nil, error);
- return;
- }
- FIRAuthDataResult *authDataResult =
- user ? [[FIRAuthDataResult alloc]
- initWithUser:user
- additionalUserInfo:nil]
- : nil;
- decoratedCallback(authDataResult, error);
- }];
- }
- }];
- });
- }
- #endif // #if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_OSX || TARGET_OS_MACCATALYST
- - (void)unenrollPasskeyWithCredentialID:(NSString *)credentialID
- completion:(nullable void (^)(NSError *_Nullable error))completion {
- dispatch_async(FIRAuthGlobalWorkQueue(), ^{
- FIRSetAccountInfoRequest *request = [[FIRSetAccountInfoRequest alloc]
- initWithRequestConfiguration:self->_auth.requestConfiguration];
- request.deletePasskeys = @[ credentialID ];
- request.accessToken = self.rawAccessToken;
- [FIRAuthBackend
- setAccountInfo:request
- callback:^(FIRSetAccountInfoResponse *_Nullable response, NSError *_Nullable error) {
- if (error) {
- callInMainThreadWithError(completion, error);
- } else {
- [FIRAuth.auth
- completeSignInWithAccessToken:response.IDToken
- accessTokenExpirationDate:response.approximateExpirationDate
- refreshToken:response.refreshToken
- anonymous:NO
- callback:^(FIRUser *_Nullable user,
- NSError *_Nullable error) {
- FIRAuthDataResult *result =
- [[FIRAuthDataResult alloc] initWithUser:user
- additionalUserInfo:nil];
- FIRAuthDataResultCallback decoratedCallback = [FIRAuth
- .auth
- signInFlowAuthDataResultCallbackByDecoratingCallback:
- ^(FIRAuthDataResult *_Nullable authResult,
- NSError *_Nullable error) {
- if (error) {
- [[FIRAuth auth] signOut:NULL];
- }
- if (completion) {
- completion(error);
- }
- }];
- decoratedCallback(result, error);
- }];
- }
- }];
- });
- }
- /** @fn updateEmail:password:callback:
- @brief Updates email address and/or password for the current user.
- @remarks May fail if there is already an email/password-based account for the same email
- address.
- @param email The email address for the user, if to be updated.
- @param password The new password for the user, if to be updated.
- @param callback The block called when the user profile change has finished. Invoked
- asynchronously on the auth global work queue in the future.
- @remarks May fail with a @c FIRAuthErrorCodeRequiresRecentLogin error code.
- Call @c reauthentateWithCredential:completion: beforehand to avoid this error case.
- */
- - (void)updateEmail:(nullable NSString *)email
- password:(nullable NSString *)password
- callback:(nonnull FIRUserProfileChangeCallback)callback {
- if (password && ![password length]) {
- callback([FIRAuthErrorUtils weakPasswordErrorWithServerResponseReason:kMissingPasswordReason]);
- return;
- }
- BOOL hadEmailPasswordCredential = _hasEmailPasswordCredential;
- [self
- executeUserUpdateWithChanges:^(FIRGetAccountInfoResponseUser *user,
- FIRSetAccountInfoRequest *request) {
- if (email) {
- request.email = email;
- }
- if (password) {
- request.password = password;
- }
- }
- callback:^(NSError *error) {
- if (error) {
- callback(error);
- return;
- }
- if (email) {
- self->_email = [email copy];
- }
- if (self->_email) {
- if (!hadEmailPasswordCredential) {
- // The list of providers need to be updated for the newly added email-password provider.
- [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
- NSError *_Nullable error) {
- if (error) {
- callback(error);
- return;
- }
- FIRAuthRequestConfiguration *requestConfiguration = self->_auth.requestConfiguration;
- FIRGetAccountInfoRequest *getAccountInfoRequest =
- [[FIRGetAccountInfoRequest alloc] initWithAccessToken:accessToken
- requestConfiguration:requestConfiguration];
- [FIRAuthBackend
- getAccountInfo:getAccountInfoRequest
- callback:^(FIRGetAccountInfoResponse *_Nullable response,
- NSError *_Nullable error) {
- if (error) {
- [self signOutIfTokenIsInvalidWithError:error];
- callback(error);
- return;
- }
- for (FIRGetAccountInfoResponseUser *userAccountInfo in response.users) {
- // Set the account to non-anonymous if there are any providers, even if
- // they're not email/password ones.
- if (userAccountInfo.providerUserInfo.count > 0) {
- self.anonymous = NO;
- }
- for (FIRGetAccountInfoResponseProviderUserInfo
- *providerUserInfo in userAccountInfo.providerUserInfo) {
- if ([providerUserInfo.providerID
- isEqualToString:FIREmailAuthProviderID]) {
- self->_hasEmailPasswordCredential = YES;
- break;
- }
- }
- }
- [self updateWithGetAccountInfoResponse:response];
- if (![self updateKeychain:&error]) {
- callback(error);
- return;
- }
- callback(nil);
- }];
- }];
- return;
- }
- }
- if (![self updateKeychain:&error]) {
- callback(error);
- return;
- }
- callback(nil);
- }];
- }
- - (void)updateEmail:(NSString *)email completion:(nullable FIRUserProfileChangeCallback)completion {
- dispatch_async(FIRAuthGlobalWorkQueue(), ^{
- [self updateEmail:email
- password:nil
- callback:^(NSError *_Nullable error) {
- callInMainThreadWithError(completion, error);
- }];
- });
- }
- - (void)updatePassword:(NSString *)password
- completion:(nullable FIRUserProfileChangeCallback)completion {
- dispatch_async(FIRAuthGlobalWorkQueue(), ^{
- [self updateEmail:nil
- password:password
- callback:^(NSError *_Nullable error) {
- callInMainThreadWithError(completion, error);
- }];
- });
- }
- #if TARGET_OS_IOS
- /** @fn internalUpdateOrLinkPhoneNumberCredential:completion:
- @brief Updates the phone number for the user. On success, the cached user profile data is
- updated.
- @param phoneAuthCredential The new phone number credential corresponding to the phone number
- to be added to the Firebase account, if a phone number is already linked to the account this
- new phone number will replace it.
- @param isLinkOperation Boolean value indicating whether or not this is a link operation.
- @param completion Optionally; the block invoked when the user profile change has finished.
- Invoked asynchronously on the global work queue in the future.
- */
- - (void)internalUpdateOrLinkPhoneNumberCredential:(FIRPhoneAuthCredential *)phoneAuthCredential
- isLinkOperation:(BOOL)isLinkOperation
- completion:(FIRUserProfileChangeCallback)completion {
- [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken, NSError *_Nullable error) {
- if (error) {
- completion(error);
- return;
- }
- FIRAuthOperationType operation =
- isLinkOperation ? FIRAuthOperationTypeLink : FIRAuthOperationTypeUpdate;
- FIRVerifyPhoneNumberRequest *request = [[FIRVerifyPhoneNumberRequest alloc]
- initWithVerificationID:phoneAuthCredential.verificationID
- verificationCode:phoneAuthCredential.verificationCode
- operation:operation
- requestConfiguration:self->_auth.requestConfiguration];
- request.accessToken = accessToken;
- [FIRAuthBackend verifyPhoneNumber:request
- callback:^(FIRVerifyPhoneNumberResponse *_Nullable response,
- NSError *_Nullable error) {
- if (error) {
- [self signOutIfTokenIsInvalidWithError:error];
- completion(error);
- return;
- }
- FIRAuthRequestConfiguration *requestConfiguration =
- self.auth.requestConfiguration;
- // Update the new token and refresh user info again.
- self->_tokenService = [[FIRSecureTokenService alloc]
- initWithRequestConfiguration:requestConfiguration
- accessToken:response.IDToken
- accessTokenExpirationDate:response.approximateExpirationDate
- refreshToken:response.refreshToken];
- // Get account info to update cached user info.
- [self getAccountInfoRefreshingCache:^(
- FIRGetAccountInfoResponseUser *_Nullable user,
- NSError *_Nullable error) {
- if (error) {
- [self signOutIfTokenIsInvalidWithError:error];
- completion(error);
- return;
- }
- self.anonymous = NO;
- if (![self updateKeychain:&error]) {
- completion(error);
- return;
- }
- completion(nil);
- }];
- }];
- }];
- }
- - (void)updatePhoneNumberCredential:(FIRPhoneAuthCredential *)phoneAuthCredential
- completion:(nullable FIRUserProfileChangeCallback)completion {
- dispatch_async(FIRAuthGlobalWorkQueue(), ^{
- [self internalUpdateOrLinkPhoneNumberCredential:phoneAuthCredential
- isLinkOperation:NO
- completion:^(NSError *_Nullable error) {
- callInMainThreadWithError(completion, error);
- }];
- });
- }
- #endif
- - (FIRUserProfileChangeRequest *)profileChangeRequest {
- __block FIRUserProfileChangeRequest *result;
- dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
- result = [[FIRUserProfileChangeRequest alloc] initWithUser:self];
- });
- return result;
- }
- - (void)setDisplayName:(NSString *)displayName {
- _displayName = [displayName copy];
- }
- - (void)setPhotoURL:(NSURL *)photoURL {
- _photoURL = [photoURL copy];
- }
- - (NSString *)rawAccessToken {
- return _tokenService.rawAccessToken;
- }
- - (NSDate *)accessTokenExpirationDate {
- return _tokenService.accessTokenExpirationDate;
- }
- #pragma mark -
- - (void)reloadWithCompletion:(nullable FIRUserProfileChangeCallback)completion {
- dispatch_async(FIRAuthGlobalWorkQueue(), ^{
- [self getAccountInfoRefreshingCache:^(FIRGetAccountInfoResponseUser *_Nullable user,
- NSError *_Nullable error) {
- callInMainThreadWithError(completion, error);
- }];
- });
- }
- #pragma mark -
- - (void)reauthenticateWithCredential:(FIRAuthCredential *)credential
- completion:(nullable FIRAuthDataResultCallback)completion {
- dispatch_async(FIRAuthGlobalWorkQueue(), ^{
- [self->_auth
- internalSignInAndRetrieveDataWithCredential:credential
- isReauthentication:YES
- callback:^(FIRAuthDataResult *_Nullable authResult,
- NSError *_Nullable error) {
- if (error) {
- // If "user not found" error returned by backend,
- // translate to user mismatch error which is more
- // accurate.
- if (error.code == FIRAuthErrorCodeUserNotFound) {
- error = [FIRAuthErrorUtils userMismatchError];
- }
- callInMainThreadWithAuthDataResultAndError(
- completion, authResult, error);
- return;
- }
- if (![authResult.user.uid
- isEqual:[self->_auth getUserID]]) {
- callInMainThreadWithAuthDataResultAndError(
- completion, authResult,
- [FIRAuthErrorUtils userMismatchError]);
- return;
- }
- // Successful reauthenticate
- [self
- setTokenService:authResult.user->_tokenService
- callback:^(NSError *_Nullable error) {
- callInMainThreadWithAuthDataResultAndError(
- completion, authResult, error);
- }];
- }];
- });
- }
- - (void)reauthenticateWithProvider:(id<FIRFederatedAuthProvider>)provider
- UIDelegate:(nullable id<FIRAuthUIDelegate>)UIDelegate
- completion:(nullable FIRAuthDataResultCallback)completion {
- #if TARGET_OS_IOS && (!defined(TARGET_OS_VISION) || !TARGET_OS_VISION)
- dispatch_async(FIRAuthGlobalWorkQueue(), ^{
- [provider getCredentialWithUIDelegate:UIDelegate
- completion:^(FIRAuthCredential *_Nullable credential,
- NSError *_Nullable error) {
- if (error) {
- completion(nil, error);
- return;
- }
- [self reauthenticateWithCredential:credential
- completion:completion];
- }];
- });
- #endif // TARGET_OS_IOS && (!defined(TARGET_OS_VISION) || !TARGET_OS_VISION)
- }
- - (nullable NSString *)refreshToken {
- __block NSString *result;
- dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
- result = self->_tokenService.refreshToken;
- });
- return result;
- }
- - (void)getIDTokenWithCompletion:(nullable FIRAuthTokenCallback)completion {
- // |getIDTokenForcingRefresh:completion:| is also a public API so there is no need to dispatch to
- // global work queue here.
- [self getIDTokenForcingRefresh:NO completion:completion];
- }
- - (void)getIDTokenForcingRefresh:(BOOL)forceRefresh
- completion:(nullable FIRAuthTokenCallback)completion {
- [self getIDTokenResultForcingRefresh:forceRefresh
- completion:^(FIRAuthTokenResult *_Nullable tokenResult,
- NSError *_Nullable error) {
- if (completion) {
- dispatch_async(dispatch_get_main_queue(), ^{
- completion(tokenResult.token, error);
- });
- }
- }];
- }
- - (void)getIDTokenResultWithCompletion:(nullable FIRAuthTokenResultCallback)completion {
- [self getIDTokenResultForcingRefresh:NO
- completion:^(FIRAuthTokenResult *_Nullable tokenResult,
- NSError *_Nullable error) {
- if (completion) {
- dispatch_async(dispatch_get_main_queue(), ^{
- completion(tokenResult, error);
- });
- }
- }];
- }
- - (void)getIDTokenResultForcingRefresh:(BOOL)forceRefresh
- completion:(nullable FIRAuthTokenResultCallback)completion {
- dispatch_async(FIRAuthGlobalWorkQueue(), ^{
- [self
- internalGetTokenForcingRefresh:forceRefresh
- callback:^(NSString *_Nullable token, NSError *_Nullable error) {
- FIRAuthTokenResult *tokenResult;
- if (token) {
- tokenResult = [FIRAuthTokenResult tokenResultWithToken:token];
- FIRLogDebug(kFIRLoggerAuth, @"I-AUT000017",
- @"Actual token expiration date: %@, current date: %@",
- tokenResult.expirationDate, [NSDate date]);
- }
- if (completion) {
- dispatch_async(dispatch_get_main_queue(), ^{
- completion(tokenResult, error);
- });
- }
- }];
- });
- }
- /** @fn parseIDToken:error:
- @brief Parses the provided IDToken and returns an instance of FIRAuthTokenResult containing
- claims obtained from the IDToken.
- @param token The raw text of the Firebase IDToken encoded in base64.
- @param error An out parameter which would contain any error that occurs during parsing.
- @return An instance of FIRAuthTokenResult containing claims obtained from the IDToken.
- @remarks IDToken returned from the backend in some cases is of a length that is not a multiple
- of 4. In these cases this function pads the token with as many "=" characters as needed and
- then attempts to parse the token. If the token cannot be parsed an error is returned via the
- "error" out parameter.
- */
- - (nullable FIRAuthTokenResult *)parseIDToken:(NSString *)token error:(NSError **)error {
- // Though this is an internal method, errors returned here are surfaced in user-visible
- // callbacks.
- if (error) {
- *error = nil;
- }
- NSArray *tokenStringArray = [token componentsSeparatedByString:@"."];
- // The JWT should have three parts, though we only use the second in this method.
- if (tokenStringArray.count != 3) {
- if (error) {
- *error = [FIRAuthErrorUtils malformedJWTErrorWithToken:token underlyingError:nil];
- }
- return nil;
- }
- // The token payload is always the second index of the array.
- NSString *IDToken = tokenStringArray[1];
- // Convert the base64URL encoded string to a base64 encoded string.
- // Replace "_" with "/"
- NSMutableString *tokenPayload = [[IDToken stringByReplacingOccurrencesOfString:@"_"
- withString:@"/"] mutableCopy];
- // Replace "-" with "+"
- [tokenPayload replaceOccurrencesOfString:@"-"
- withString:@"+"
- options:kNilOptions
- range:NSMakeRange(0, tokenPayload.length)];
- // Pad the token payload with "=" signs if the payload's length is not a multiple of 4.
- while ((tokenPayload.length % 4) != 0) {
- [tokenPayload appendFormat:@"="];
- }
- NSData *decodedTokenPayloadData =
- [[NSData alloc] initWithBase64EncodedString:tokenPayload
- options:NSDataBase64DecodingIgnoreUnknownCharacters];
- if (!decodedTokenPayloadData) {
- if (error) {
- *error = [FIRAuthErrorUtils malformedJWTErrorWithToken:token underlyingError:nil];
- }
- return nil;
- }
- NSError *jsonError = nil;
- NSJSONReadingOptions options = NSJSONReadingMutableContainers | NSJSONReadingAllowFragments;
- NSDictionary *tokenPayloadDictionary =
- [NSJSONSerialization JSONObjectWithData:decodedTokenPayloadData
- options:options
- error:&jsonError];
- if (jsonError != nil) {
- if (error) {
- *error = [FIRAuthErrorUtils malformedJWTErrorWithToken:token underlyingError:jsonError];
- }
- return nil;
- }
- if (!tokenPayloadDictionary) {
- if (error) {
- *error = [FIRAuthErrorUtils malformedJWTErrorWithToken:token underlyingError:nil];
- }
- return nil;
- }
- FIRAuthTokenResult *result = [FIRAuthTokenResult tokenResultWithToken:token];
- return result;
- }
- /** @fn internalGetTokenForcingRefresh:callback:
- @brief Retrieves the Firebase authentication token, possibly refreshing it if it has expired.
- @param callback The block to invoke when the token is available. Invoked asynchronously on the
- global work thread in the future.
- */
- - (void)internalGetTokenWithCallback:(nonnull FIRAuthTokenCallback)callback {
- [self internalGetTokenForcingRefresh:NO callback:callback];
- }
- - (void)internalGetTokenForcingRefresh:(BOOL)forceRefresh
- callback:(nonnull FIRAuthTokenCallback)callback {
- [_tokenService fetchAccessTokenForcingRefresh:forceRefresh
- callback:^(NSString *_Nullable token,
- NSError *_Nullable error, BOOL tokenUpdated) {
- if (error) {
- [self signOutIfTokenIsInvalidWithError:error];
- callback(nil, error);
- return;
- }
- if (tokenUpdated) {
- if (![self updateKeychain:&error]) {
- callback(nil, error);
- return;
- }
- }
- callback(token, nil);
- }];
- }
- - (void)sendEmailVerificationBeforeUpdatingEmail:(nonnull NSString *)email
- completion:(nullable FIRAuthVoidErrorCallback)completion {
- [self internalVerifyBeforeUpdateEmailWithNewEmail:email
- actionCodeSettings:nil
- completion:completion];
- }
- - (void)sendEmailVerificationBeforeUpdatingEmail:(nonnull NSString *)email
- actionCodeSettings:(nonnull FIRActionCodeSettings *)actionCodeSettings
- completion:(nullable FIRAuthVoidErrorCallback)completion {
- [self internalVerifyBeforeUpdateEmailWithNewEmail:email
- actionCodeSettings:actionCodeSettings
- completion:completion];
- }
- - (void)internalVerifyBeforeUpdateEmailWithNewEmail:(NSString *)newEmail
- actionCodeSettings:
- (nullable FIRActionCodeSettings *)actionCodeSettings
- completion:(FIRVerifyBeforeUpdateEmailCallback)completion {
- dispatch_async(FIRAuthGlobalWorkQueue(), ^{
- [self
- internalGetTokenWithCallback:^(NSString *_Nullable accessToken, NSError *_Nullable error) {
- if (error) {
- callInMainThreadWithError(completion, error);
- return;
- }
- FIRAuthRequestConfiguration *configuration = self->_auth.requestConfiguration;
- FIRActionCodeSettings *settings = actionCodeSettings;
- FIRGetOOBConfirmationCodeRequest *request = [FIRGetOOBConfirmationCodeRequest
- verifyBeforeUpdateEmailWithAccessToken:accessToken
- newEmail:newEmail
- actionCodeSettings:settings
- requestConfiguration:configuration];
- [FIRAuthBackend
- getOOBConfirmationCode:request
- callback:^(FIRGetOOBConfirmationCodeResponse *_Nullable response,
- NSError *_Nullable error) {
- callInMainThreadWithError(completion, error);
- }];
- }];
- });
- }
- - (void)linkWithEmailPassword:(FIREmailPasswordAuthCredential *)credential
- authResult:(FIRAuthDataResult *)authResult
- completion:(nullable FIRAuthDataResultCallback)completion {
- [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken, NSError *_Nullable error) {
- FIRAuthRequestConfiguration *requestConfiguration = self.auth.requestConfiguration;
- FIRSignUpNewUserRequest *request =
- [[FIRSignUpNewUserRequest alloc] initWithEmail:credential.email
- password:credential.password
- displayName:nil
- idToken:accessToken
- requestConfiguration:requestConfiguration];
- FIRSignupNewUserCallback signUpNewUserCallback = ^(FIRSignUpNewUserResponse *_Nullable response,
- NSError *_Nullable error) {
- if (error) {
- [self signOutIfTokenIsInvalidWithError:error];
- callInMainThreadWithAuthDataResultAndError(completion, nil, error);
- } else {
- // Update the new token and refresh user info again.
- self->_tokenService = [[FIRSecureTokenService alloc]
- initWithRequestConfiguration:requestConfiguration
- accessToken:response.IDToken
- accessTokenExpirationDate:response.approximateExpirationDate
- refreshToken:response.refreshToken];
- [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
- NSError *_Nullable error) {
- if (error) {
- callInMainThreadWithAuthDataResultAndError(completion, nil, error);
- return;
- }
- FIRGetAccountInfoRequest *getAccountInfoRequest =
- [[FIRGetAccountInfoRequest alloc] initWithAccessToken:accessToken
- requestConfiguration:requestConfiguration];
- [FIRAuthBackend
- getAccountInfo:getAccountInfoRequest
- callback:^(FIRGetAccountInfoResponse *_Nullable response,
- NSError *_Nullable error) {
- if (error) {
- [self signOutIfTokenIsInvalidWithError:error];
- callInMainThreadWithAuthDataResultAndError(completion, nil, error);
- return;
- }
- self.anonymous = NO;
- [self updateWithGetAccountInfoResponse:response];
- NSError *keychainError;
- if (![self updateKeychain:&keychainError]) {
- callInMainThreadWithAuthDataResultAndError(completion, nil, keychainError);
- return;
- }
- callInMainThreadWithAuthDataResultAndError(completion, authResult, nil);
- }];
- }];
- }
- };
- #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST && (!defined(TARGET_OS_VISION) || !TARGET_OS_VISION)
- if ([[FIRAuthRecaptchaVerifier sharedRecaptchaVerifier:self.auth]
- enablementStatusForProvider:FIRAuthRecaptchaProviderPassword]) {
- [[FIRAuthRecaptchaVerifier sharedRecaptchaVerifier:self.auth]
- injectRecaptchaFields:request
- provider:FIRAuthRecaptchaProviderPassword
- action:FIRAuthRecaptchaActionSignUpPassword
- completion:^(
- FIRIdentityToolkitRequest<FIRAuthRPCRequest> *requestWithRecaptchaToken) {
- [FIRAuthBackend
- signUpNewUser:(FIRSignUpNewUserRequest *)requestWithRecaptchaToken
- callback:signUpNewUserCallback];
- }];
- } else {
- [FIRAuthBackend
- signUpNewUser:request
- callback:^(FIRSignUpNewUserResponse *_Nullable response, NSError *_Nullable error) {
- if (!error) {
- signUpNewUserCallback(response, nil);
- return;
- }
- NSError *underlyingError = [error.userInfo objectForKey:NSUnderlyingErrorKey];
- if (error.code == FIRAuthErrorCodeInternalError &&
- [[underlyingError.userInfo
- objectForKey:FIRAuthErrorUserInfoDeserializedResponseKey][@"message"]
- hasPrefix:kMissingRecaptchaTokenErrorPrefix]) {
- [[FIRAuthRecaptchaVerifier sharedRecaptchaVerifier:self.auth]
- injectRecaptchaFields:request
- provider:FIRAuthRecaptchaProviderPassword
- action:FIRAuthRecaptchaActionSignUpPassword
- completion:^(FIRIdentityToolkitRequest<FIRAuthRPCRequest>
- *requestWithRecaptchaToken) {
- [FIRAuthBackend signUpNewUser:(FIRSignUpNewUserRequest *)
- requestWithRecaptchaToken
- callback:signUpNewUserCallback];
- }];
- } else {
- signUpNewUserCallback(nil, error);
- }
- }];
- }
- #else
- [FIRAuthBackend signUpNewUser:request callback:signUpNewUserCallback];
- #endif
- }];
- }
- - (void)linkWithCredential:(FIRAuthCredential *)credential
- completion:(nullable FIRAuthDataResultCallback)completion {
- dispatch_async(FIRAuthGlobalWorkQueue(), ^{
- if (self->_providerData[credential.provider]) {
- callInMainThreadWithAuthDataResultAndError(completion, nil,
- [FIRAuthErrorUtils providerAlreadyLinkedError]);
- return;
- }
- FIRAuthDataResult *result = [[FIRAuthDataResult alloc] initWithUser:self
- additionalUserInfo:nil];
- if ([credential isKindOfClass:[FIREmailPasswordAuthCredential class]]) {
- if (self->_hasEmailPasswordCredential) {
- callInMainThreadWithAuthDataResultAndError(completion, nil,
- [FIRAuthErrorUtils providerAlreadyLinkedError]);
- return;
- }
- FIREmailPasswordAuthCredential *emailPasswordCredential =
- (FIREmailPasswordAuthCredential *)credential;
- if (emailPasswordCredential.password) {
- [self linkWithEmailPassword:emailPasswordCredential
- authResult:result
- completion:completion];
- } else {
- [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
- NSError *_Nullable error) {
- NSDictionary<NSString *, NSString *> *queryItems =
- [FIRAuthWebUtils parseURL:emailPasswordCredential.link];
- if (![queryItems count]) {
- NSURLComponents *urlComponents =
- [NSURLComponents componentsWithString:emailPasswordCredential.link];
- queryItems = [FIRAuthWebUtils parseURL:urlComponents.query];
- }
- NSString *actionCode = queryItems[@"oobCode"];
- FIRAuthRequestConfiguration *requestConfiguration = self.auth.requestConfiguration;
- FIREmailLinkSignInRequest *request =
- [[FIREmailLinkSignInRequest alloc] initWithEmail:emailPasswordCredential.email
- oobCode:actionCode
- requestConfiguration:requestConfiguration];
- request.IDToken = accessToken;
- [FIRAuthBackend
- emailLinkSignin:request
- callback:^(FIREmailLinkSignInResponse *_Nullable response,
- NSError *_Nullable error) {
- if (error) {
- callInMainThreadWithAuthDataResultAndError(completion, nil, error);
- } else {
- // Update the new token and refresh user info again.
- self->_tokenService = [[FIRSecureTokenService alloc]
- initWithRequestConfiguration:requestConfiguration
- accessToken:response.IDToken
- accessTokenExpirationDate:response.approximateExpirationDate
- refreshToken:response.refreshToken];
- [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
- NSError *_Nullable error) {
- if (error) {
- callInMainThreadWithAuthDataResultAndError(completion, nil, error);
- return;
- }
- FIRGetAccountInfoRequest *getAccountInfoRequest =
- [[FIRGetAccountInfoRequest alloc]
- initWithAccessToken:accessToken
- requestConfiguration:requestConfiguration];
- [FIRAuthBackend
- getAccountInfo:getAccountInfoRequest
- callback:^(FIRGetAccountInfoResponse *_Nullable response,
- NSError *_Nullable error) {
- if (error) {
- [self signOutIfTokenIsInvalidWithError:error];
- callInMainThreadWithAuthDataResultAndError(completion, nil,
- error);
- return;
- }
- self.anonymous = NO;
- [self updateWithGetAccountInfoResponse:response];
- if (![self updateKeychain:&error]) {
- callInMainThreadWithAuthDataResultAndError(completion, nil,
- error);
- return;
- }
- callInMainThreadWithAuthDataResultAndError(completion,
- result, nil);
- }];
- }];
- }
- }];
- }];
- }
- return;
- }
- if ([credential isKindOfClass:[FIRGameCenterAuthCredential class]]) {
- FIRGameCenterAuthCredential *gameCenterCredential = (FIRGameCenterAuthCredential *)credential;
- [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
- NSError *_Nullable error) {
- FIRAuthRequestConfiguration *requestConfiguration = self.auth.requestConfiguration;
- FIRSignInWithGameCenterRequest *gameCenterRequest = [[FIRSignInWithGameCenterRequest alloc]
- initWithPlayerID:gameCenterCredential.playerID
- teamPlayerID:gameCenterCredential.teamPlayerID
- gamePlayerID:gameCenterCredential.gamePlayerID
- publicKeyURL:gameCenterCredential.publicKeyURL
- signature:gameCenterCredential.signature
- salt:gameCenterCredential.salt
- timestamp:gameCenterCredential.timestamp
- displayName:gameCenterCredential.displayName
- requestConfiguration:requestConfiguration];
- gameCenterRequest.accessToken = accessToken;
- [FIRAuthBackend
- signInWithGameCenter:gameCenterRequest
- callback:^(FIRSignInWithGameCenterResponse *_Nullable response,
- NSError *_Nullable error) {
- if (error) {
- callInMainThreadWithAuthDataResultAndError(completion, nil, error);
- } else {
- // Update the new token and refresh user info again.
- self->_tokenService = [[FIRSecureTokenService alloc]
- initWithRequestConfiguration:requestConfiguration
- accessToken:response.IDToken
- accessTokenExpirationDate:response.approximateExpirationDate
- refreshToken:response.refreshToken];
- [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
- NSError *_Nullable error) {
- if (error) {
- callInMainThreadWithAuthDataResultAndError(completion, nil, error);
- return;
- }
- FIRGetAccountInfoRequest *getAccountInfoRequest =
- [[FIRGetAccountInfoRequest alloc]
- initWithAccessToken:accessToken
- requestConfiguration:requestConfiguration];
- [FIRAuthBackend
- getAccountInfo:getAccountInfoRequest
- callback:^(FIRGetAccountInfoResponse *_Nullable response,
- NSError *_Nullable error) {
- if (error) {
- [self signOutIfTokenIsInvalidWithError:error];
- callInMainThreadWithAuthDataResultAndError(completion,
- nil, error);
- return;
- }
- self.anonymous = NO;
- [self updateWithGetAccountInfoResponse:response];
- if (![self updateKeychain:&error]) {
- callInMainThreadWithAuthDataResultAndError(completion,
- nil, error);
- return;
- }
- callInMainThreadWithAuthDataResultAndError(completion,
- result, nil);
- }];
- }];
- }
- }];
- }];
- return;
- }
- #if TARGET_OS_IOS
- if ([credential isKindOfClass:[FIRPhoneAuthCredential class]]) {
- FIRPhoneAuthCredential *phoneAuthCredential = (FIRPhoneAuthCredential *)credential;
- [self internalUpdateOrLinkPhoneNumberCredential:phoneAuthCredential
- isLinkOperation:YES
- completion:^(NSError *_Nullable error) {
- if (error) {
- callInMainThreadWithAuthDataResultAndError(
- completion, nil, error);
- } else {
- callInMainThreadWithAuthDataResultAndError(
- completion, result, nil);
- }
- }];
- return;
- }
- #endif
- [self->_taskQueue enqueueTask:^(FIRAuthSerialTaskCompletionBlock _Nonnull complete) {
- CallbackWithAuthDataResultAndError completeWithError =
- ^(FIRAuthDataResult *result, NSError *error) {
- complete();
- callInMainThreadWithAuthDataResultAndError(completion, result, error);
- };
- [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
- NSError *_Nullable error) {
- if (error) {
- completeWithError(nil, error);
- return;
- }
- FIRAuthRequestConfiguration *requestConfiguration = self->_auth.requestConfiguration;
- FIRVerifyAssertionRequest *request =
- [[FIRVerifyAssertionRequest alloc] initWithProviderID:credential.provider
- requestConfiguration:requestConfiguration];
- [credential prepareVerifyAssertionRequest:request];
- request.accessToken = accessToken;
- [FIRAuthBackend
- verifyAssertion:request
- callback:^(FIRVerifyAssertionResponse *response, NSError *error) {
- if (error) {
- [self signOutIfTokenIsInvalidWithError:error];
- completeWithError(nil, error);
- return;
- }
- FIRAdditionalUserInfo *additionalUserInfo =
- [FIRAdditionalUserInfo userInfoWithVerifyAssertionResponse:response];
- FIROAuthCredential *updatedOAuthCredential =
- [[FIROAuthCredential alloc] initWithVerifyAssertionResponse:response];
- FIRAuthDataResult *result =
- [[FIRAuthDataResult alloc] initWithUser:self
- additionalUserInfo:additionalUserInfo
- credential:updatedOAuthCredential];
- // Update the new token and refresh user info again.
- self->_tokenService = [[FIRSecureTokenService alloc]
- initWithRequestConfiguration:requestConfiguration
- accessToken:response.IDToken
- accessTokenExpirationDate:response.approximateExpirationDate
- refreshToken:response.refreshToken];
- [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken,
- NSError *_Nullable error) {
- if (error) {
- completeWithError(nil, error);
- return;
- }
- FIRGetAccountInfoRequest *getAccountInfoRequest =
- [[FIRGetAccountInfoRequest alloc]
- initWithAccessToken:accessToken
- requestConfiguration:requestConfiguration];
- [FIRAuthBackend
- getAccountInfo:getAccountInfoRequest
- callback:^(FIRGetAccountInfoResponse *_Nullable response,
- NSError *_Nullable error) {
- if (error) {
- [self signOutIfTokenIsInvalidWithError:error];
- completeWithError(nil, error);
- return;
- }
- self.anonymous = NO;
- [self updateWithGetAccountInfoResponse:response];
- if (![self updateKeychain:&error]) {
- completeWithError(nil, error);
- return;
- }
- completeWithError(result, nil);
- }];
- }];
- }];
- }];
- }];
- });
- }
- - (void)linkWithProvider:(id<FIRFederatedAuthProvider>)provider
- UIDelegate:(nullable id<FIRAuthUIDelegate>)UIDelegate
- completion:(nullable FIRAuthDataResultCallback)completion {
- #if TARGET_OS_IOS && (!defined(TARGET_OS_VISION) || !TARGET_OS_VISION)
- dispatch_async(FIRAuthGlobalWorkQueue(), ^{
- [provider getCredentialWithUIDelegate:UIDelegate
- completion:^(FIRAuthCredential *_Nullable credential,
- NSError *_Nullable error) {
- if (error) {
- completion(nil, error);
- return;
- }
- [self linkWithCredential:credential completion:completion];
- }];
- });
- #endif // TARGET_OS_IOS && (!defined(TARGET_OS_VISION) || !TARGET_OS_VISION)
- }
- - (void)unlinkFromProvider:(NSString *)provider
- completion:(nullable FIRAuthResultCallback)completion {
- [_taskQueue enqueueTask:^(FIRAuthSerialTaskCompletionBlock _Nonnull complete) {
- CallbackWithError completeAndCallbackWithError = ^(NSError *error) {
- complete();
- callInMainThreadWithUserAndError(completion, self, error);
- };
- [self
- internalGetTokenWithCallback:^(NSString *_Nullable accessToken, NSError *_Nullable error) {
- if (error) {
- completeAndCallbackWithError(error);
- return;
- }
- FIRAuthRequestConfiguration *requestConfiguration = self->_auth.requestConfiguration;
- FIRSetAccountInfoRequest *setAccountInfoRequest =
- [[FIRSetAccountInfoRequest alloc] initWithRequestConfiguration:requestConfiguration];
- setAccountInfoRequest.accessToken = accessToken;
- if (!self->_providerData[provider]) {
- completeAndCallbackWithError([FIRAuthErrorUtils noSuchProviderError]);
- return;
- }
- setAccountInfoRequest.deleteProviders = @[ provider ];
- [FIRAuthBackend
- setAccountInfo:setAccountInfoRequest
- callback:^(FIRSetAccountInfoResponse *_Nullable response,
- NSError *_Nullable error) {
- if (error) {
- [self signOutIfTokenIsInvalidWithError:error];
- completeAndCallbackWithError(error);
- return;
- }
- // We can't just use the provider info objects in FIRSetAccountInfoResponse
- // because they don't have localID and email fields. Remove the specific
- // provider manually.
- NSMutableDictionary *mutableProviderData = [self->_providerData mutableCopy];
- [mutableProviderData removeObjectForKey:provider];
- self->_providerData = [mutableProviderData copy];
- if ([provider isEqualToString:FIREmailAuthProviderID]) {
- self->_hasEmailPasswordCredential = NO;
- }
- #if TARGET_OS_IOS
- // After successfully unlinking a phone auth provider, remove the phone number
- // from the cached user info.
- if ([provider isEqualToString:FIRPhoneAuthProviderID]) {
- self->_phoneNumber = nil;
- }
- #endif
- if (response.IDToken && response.refreshToken) {
- FIRSecureTokenService *tokenService = [[FIRSecureTokenService alloc]
- initWithRequestConfiguration:requestConfiguration
- accessToken:response.IDToken
- accessTokenExpirationDate:response.approximateExpirationDate
- refreshToken:response.refreshToken];
- [self setTokenService:tokenService
- callback:^(NSError *_Nullable error) {
- completeAndCallbackWithError(error);
- }];
- return;
- }
- if (![self updateKeychain:&error]) {
- completeAndCallbackWithError(error);
- return;
- }
- completeAndCallbackWithError(nil);
- }];
- }];
- }];
- }
- - (void)sendEmailVerificationWithCompletion:(nullable FIRSendEmailVerificationCallback)completion {
- [self sendEmailVerificationWithNullableActionCodeSettings:nil completion:completion];
- }
- - (void)sendEmailVerificationWithActionCodeSettings:(FIRActionCodeSettings *)actionCodeSettings
- completion:
- (nullable FIRSendEmailVerificationCallback)completion {
- [self sendEmailVerificationWithNullableActionCodeSettings:actionCodeSettings
- completion:completion];
- }
- /** @fn sendEmailVerificationWithNullableActionCodeSettings:completion:
- @brief Initiates email verification for the user.
- @param actionCodeSettings Optionally, a @c FIRActionCodeSettings object containing settings
- related to the handling action codes.
- */
- - (void)sendEmailVerificationWithNullableActionCodeSettings:
- (nullable FIRActionCodeSettings *)actionCodeSettings
- completion:
- (nullable FIRSendEmailVerificationCallback)
- completion {
- dispatch_async(FIRAuthGlobalWorkQueue(), ^{
- [self
- internalGetTokenWithCallback:^(NSString *_Nullable accessToken, NSError *_Nullable error) {
- if (error) {
- callInMainThreadWithError(completion, error);
- return;
- }
- FIRAuthRequestConfiguration *configuration = self->_auth.requestConfiguration;
- FIRGetOOBConfirmationCodeRequest *request =
- [FIRGetOOBConfirmationCodeRequest verifyEmailRequestWithAccessToken:accessToken
- actionCodeSettings:actionCodeSettings
- requestConfiguration:configuration];
- [FIRAuthBackend
- getOOBConfirmationCode:request
- callback:^(FIRGetOOBConfirmationCodeResponse *_Nullable response,
- NSError *_Nullable error) {
- [self signOutIfTokenIsInvalidWithError:error];
- callInMainThreadWithError(completion, error);
- }];
- }];
- });
- }
- - (void)deleteWithCompletion:(nullable FIRUserProfileChangeCallback)completion {
- dispatch_async(FIRAuthGlobalWorkQueue(), ^{
- [self
- internalGetTokenWithCallback:^(NSString *_Nullable accessToken, NSError *_Nullable error) {
- if (error) {
- callInMainThreadWithError(completion, error);
- return;
- }
- FIRDeleteAccountRequest *deleteUserRequest =
- [[FIRDeleteAccountRequest alloc] initWitLocalID:self->_userID
- accessToken:accessToken
- requestConfiguration:self->_auth.requestConfiguration];
- [FIRAuthBackend deleteAccount:deleteUserRequest
- callback:^(NSError *_Nullable error) {
- if (error) {
- callInMainThreadWithError(completion, error);
- return;
- }
- if (![self->_auth signOutByForceWithUserID:self->_userID
- error:&error]) {
- callInMainThreadWithError(completion, error);
- return;
- }
- callInMainThreadWithError(completion, error);
- }];
- }];
- });
- }
- /** @fn signOutIfTokenIsInvalidWithError:
- @brief Signs out this user if the user or the token is invalid.
- @param error The error from the server.
- */
- - (void)signOutIfTokenIsInvalidWithError:(nullable NSError *)error {
- NSInteger errorCode = error.code;
- if (errorCode == FIRAuthErrorCodeUserNotFound || errorCode == FIRAuthErrorCodeUserDisabled ||
- errorCode == FIRAuthErrorCodeInvalidUserToken ||
- errorCode == FIRAuthErrorCodeUserTokenExpired) {
- FIRLogNotice(kFIRLoggerAuth, @"I-AUT000016",
- @"Invalid user token detected, user is automatically signed out.");
- [_auth signOutByForceWithUserID:_userID error:NULL];
- }
- }
- @end
- @implementation FIRUserProfileChangeRequest {
- /** @var _user
- @brief The user associated with the change request.
- */
- FIRUser *_user;
- /** @var _displayName
- @brief The display name value to set if @c _displayNameSet is YES.
- */
- NSString *_displayName;
- /** @var _displayNameSet
- @brief Indicates the display name should be part of the change request.
- */
- BOOL _displayNameSet;
- /** @var _photoURL
- @brief The photo URL value to set if @c _displayNameSet is YES.
- */
- NSURL *_photoURL;
- /** @var _photoURLSet
- @brief Indicates the photo URL should be part of the change request.
- */
- BOOL _photoURLSet;
- /** @var _consumed
- @brief Indicates the @c commitChangesWithCallback: method has already been invoked.
- */
- BOOL _consumed;
- }
- - (nullable instancetype)initWithUser:(FIRUser *)user {
- self = [super init];
- if (self) {
- _user = user;
- }
- return self;
- }
- - (nullable NSString *)displayName {
- return _displayName;
- }
- - (void)setDisplayName:(nullable NSString *)displayName {
- dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
- if (self->_consumed) {
- [NSException
- raise:NSInternalInconsistencyException
- format:@"%@", @"Invalid call to setDisplayName: after commitChangesWithCallback:."];
- return;
- }
- self->_displayNameSet = YES;
- self->_displayName = [displayName copy];
- });
- }
- - (nullable NSURL *)photoURL {
- return _photoURL;
- }
- - (void)setPhotoURL:(nullable NSURL *)photoURL {
- dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
- if (self->_consumed) {
- [NSException raise:NSInternalInconsistencyException
- format:@"%@", @"Invalid call to setPhotoURL: after commitChangesWithCallback:."];
- return;
- }
- self->_photoURLSet = YES;
- self->_photoURL = [photoURL copy];
- });
- }
- /** @fn hasUpdates
- @brief Indicates at least one field has a value which needs to be committed.
- */
- - (BOOL)hasUpdates {
- return _displayNameSet || _photoURLSet;
- }
- - (void)commitChangesWithCompletion:(nullable FIRUserProfileChangeCallback)completion {
- dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
- if (self->_consumed) {
- [NSException raise:NSInternalInconsistencyException
- format:@"%@", @"commitChangesWithCallback: should only be called once."];
- return;
- }
- self->_consumed = YES;
- // Return fast if there is nothing to update:
- if (![self hasUpdates]) {
- callInMainThreadWithError(completion, nil);
- return;
- }
- NSString *displayName = [self->_displayName copy];
- BOOL displayNameWasSet = self->_displayNameSet;
- NSURL *photoURL = [self->_photoURL copy];
- BOOL photoURLWasSet = self->_photoURLSet;
- [self->_user
- executeUserUpdateWithChanges:^(FIRGetAccountInfoResponseUser *user,
- FIRSetAccountInfoRequest *request) {
- if (photoURLWasSet) {
- request.photoURL = photoURL;
- }
- if (displayNameWasSet) {
- request.displayName = displayName;
- }
- }
- callback:^(NSError *_Nullable error) {
- if (error) {
- callInMainThreadWithError(completion, error);
- return;
- }
- if (displayNameWasSet) {
- [self->_user setDisplayName:displayName];
- }
- if (photoURLWasSet) {
- [self->_user setPhotoURL:photoURL];
- }
- if (![self->_user updateKeychain:&error]) {
- callInMainThreadWithError(completion, error);
- return;
- }
- callInMainThreadWithError(completion, nil);
- }];
- });
- }
- @end
- NS_ASSUME_NONNULL_END
|