Skip to content

Commit 7605a18

Browse files
author
renkelvin
authored
Email enumeration protection related error and doc updates (#12081)
1 parent 2aa184c commit 7605a18

File tree

7 files changed

+49
-15
lines changed

7 files changed

+49
-15
lines changed

FirebaseAuth/Sources/Backend/FIRAuthBackend.m

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,12 @@
551551
*/
552552
static NSString *const kInvalidRecaptchaVersion = @"INVALID_RECAPTCHA_VERSION";
553553

554+
/** @var kInvalidLoginCredentials
555+
@brief This is the error message the server will respond with if the login credentials is
556+
invalid. in the request.
557+
*/
558+
static NSString *const kInvalidLoginCredentials = @"INVALID_LOGIN_CREDENTIALS";
559+
554560
/** @var gBackendImplementation
555561
@brief The singleton FIRAuthBackendImplementation instance to use.
556562
*/
@@ -1404,7 +1410,8 @@ + (nullable NSError *)clientErrorWithServerErrorMessage:(NSString *)serverErrorM
14041410
}
14051411

14061412
if ([shortErrorMessage isEqualToString:kInvalidCredentialErrorMessage] ||
1407-
[shortErrorMessage isEqualToString:kInvalidPendingToken]) {
1413+
[shortErrorMessage isEqualToString:kInvalidPendingToken] ||
1414+
[shortErrorMessage isEqualToString:kInvalidLoginCredentials]) {
14081415
return [FIRAuthErrorUtils invalidCredentialErrorWithMessage:serverDetailErrorMessage];
14091416
}
14101417

FirebaseAuth/Sources/Public/FirebaseAuth/FIRAuth.h

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,9 @@ NS_SWIFT_NAME(Auth)
378378

379379
/** @fn fetchSignInMethodsForEmail:completion:
380380
@brief Fetches the list of all sign-in methods previously used for the provided email address.
381-
381+
This method returns an empty list when [Email Enumeration
382+
Protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection)
383+
is enabled, irrespective of the number of authentication methods available for the given email.
382384
@param email The email address for which to obtain a list of sign-in methods.
383385
@param completion Optionally; a block which is invoked when the list of sign in methods for the
384386
specified email address is ready or an error was encountered. Invoked asynchronously on the
@@ -393,10 +395,15 @@ NS_SWIFT_NAME(Auth)
393395

394396
- (void)fetchSignInMethodsForEmail:(NSString *)email
395397
completion:(nullable void (^)(NSArray<NSString *> *_Nullable,
396-
NSError *_Nullable))completion;
398+
NSError *_Nullable))completion
399+
DEPRECATED_MSG_ATTRIBUTE(
400+
"This method returns an empty list when Email Enumeration Protection is enabled.");
397401

398402
/** @fn signInWithEmail:password:completion:
399-
@brief Signs in using an email address and password.
403+
@brief Signs in using an email address and password. When [Email Enumeration
404+
Protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection)
405+
is enabled, this method fails with FIRAuthErrorCodeInvalidCredentials in case of an invalid
406+
email/password.
400407
401408
@param email The user's email address.
402409
@param password The user's password.
@@ -663,8 +670,10 @@ NS_SWIFT_NAME(Auth)
663670
- (void)applyActionCode:(NSString *)code completion:(void (^)(NSError *_Nullable error))completion;
664671

665672
/** @fn sendPasswordResetWithEmail:completion:
666-
@brief Initiates a password reset for the given email address.
667-
673+
@brief Initiates a password reset for the given email address. This method does not throw an
674+
error when there's no user account with the given email address and [Email Enumeration
675+
Protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection)
676+
is enabled.
668677
@param email The email address of the user.
669678
@param completion Optionally; a block which is invoked when the request finishes. Invoked
670679
asynchronously on the main thread in the future.

FirebaseAuth/Sources/Public/FirebaseAuth/FIRUser.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ NS_SWIFT_NAME(User)
132132

133133
/** @fn updateEmail:completion:
134134
@brief Updates the email address for the user. On success, the cached user profile data is
135-
updated.
135+
updated. Throws FIRAuthErrorCodeInvalidCredentials error when [Email Enumeration
136+
Protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection)
137+
is enabled.
136138
@remarks May fail if there is already an account with this email address that was created using
137139
email and password authentication.
138140
@@ -160,7 +162,8 @@ NS_SWIFT_NAME(User)
160162
*/
161163
- (void)updateEmail:(NSString *)email
162164
completion:(nullable void (^)(NSError *_Nullable error))completion
163-
NS_SWIFT_NAME(updateEmail(to:completion:));
165+
NS_SWIFT_NAME(updateEmail(to:completion:))
166+
DEPRECATED_MSG_ATTRIBUTE("Use sendEmailVerificationBeforeUpdatingEmail: instead.");
164167

165168
/** @fn updatePassword:completion:
166169
@brief Updates the password for the user. On success, the cached user profile data is updated.

FirebaseAuth/Sources/Utilities/FIRAuthErrorUtils.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,10 @@
626626
@"The reCAPTCHA SDK is not linked to your app. See "
627627
@"https://cloud.google.com/recaptcha-enterprise/docs/instrument-ios-apps";
628628

629+
static NSString *const kFIRAuthErrorMessageInvalidLoginCredentials =
630+
@"Login credentials invalid. It is possible that the email/password combination does not "
631+
@"exist.";
632+
629633
/** @var FIRAuthErrorDescription
630634
@brief The error descrioption, based on the error code.
631635
@remarks No default case so that we get a compiler warning if a new value was added to the enum.

FirebaseAuth/Tests/Unit/FIRAuthTests.m

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,8 @@ - (void)testFetchSignInMethodsForEmailSuccess {
433433
});
434434
});
435435
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
436+
#pragma clang diagnostic push
437+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
436438
[[FIRAuth auth] fetchSignInMethodsForEmail:kEmail
437439
completion:^(NSArray<NSString *> *_Nullable signInMethods,
438440
NSError *_Nullable error) {
@@ -442,6 +444,7 @@ - (void)testFetchSignInMethodsForEmailSuccess {
442444
XCTAssertNil(error);
443445
[expectation fulfill];
444446
}];
447+
#pragma clang diagnostic pop
445448
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
446449
OCMVerifyAll(_mockBackend);
447450
}
@@ -453,6 +456,8 @@ - (void)testFetchSignInMethodsForEmailFailure {
453456
OCMExpect([_mockBackend createAuthURI:[OCMArg any] callback:[OCMArg any]])
454457
.andDispatchError2([FIRAuthErrorUtils tooManyRequestsErrorWithMessage:nil]);
455458
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
459+
#pragma clang diagnostic push
460+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
456461
[[FIRAuth auth] fetchSignInMethodsForEmail:kEmail
457462
completion:^(NSArray<NSString *> *_Nullable signInMethods,
458463
NSError *_Nullable error) {
@@ -462,6 +467,7 @@ - (void)testFetchSignInMethodsForEmailFailure {
462467
XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
463468
[expectation fulfill];
464469
}];
470+
#pragma clang diagnostic pop
465471
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
466472
OCMVerifyAll(_mockBackend);
467473
}

FirebaseAuth/Tests/Unit/FIRUserTests.m

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,8 @@ - (void)testUpdateEmailSuccess {
769769
callback(mockSetAccountInfoResponse, nil);
770770
});
771771
});
772+
#pragma clang diagnostic push
773+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
772774
[user updateEmail:kNewEmail
773775
completion:^(NSError *_Nullable error) {
774776
XCTAssertNil(error);
@@ -777,6 +779,7 @@ - (void)testUpdateEmailSuccess {
777779
kNewDisplayName);
778780
[expectation fulfill];
779781
}];
782+
#pragma clang diagnostic pop
780783
}];
781784
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
782785
OCMVerifyAll(_mockBackend);
@@ -829,6 +832,8 @@ - (void)testUpdateEmailWithAuthLinkAccountSuccess {
829832
callback(mockSetAccountInfoResponse, nil);
830833
});
831834
});
835+
#pragma clang diagnostic push
836+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
832837
[user updateEmail:kNewEmail
833838
completion:^(NSError *_Nullable error) {
834839
XCTAssertNil(error);
@@ -838,6 +843,7 @@ - (void)testUpdateEmailWithAuthLinkAccountSuccess {
838843
XCTAssertFalse(user.isAnonymous);
839844
[expectation fulfill];
840845
}];
846+
#pragma clang diagnostic pop
841847
}];
842848
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
843849
OCMVerifyAll(_mockBackend);
@@ -862,6 +868,8 @@ - (void)testUpdateEmailFailure {
862868
callback:[OCMArg any]])
863869
.andDispatchError2([FIRAuthErrorUtils
864870
invalidEmailErrorWithMessage:nil]);
871+
#pragma clang diagnostic push
872+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
865873
[user
866874
updateEmail:kNewEmail
867875
completion:^(NSError *_Nullable error) {
@@ -876,6 +884,7 @@ - (void)testUpdateEmailFailure {
876884
user);
877885
[expectation fulfill];
878886
}];
887+
#pragma clang diagnostic pop
879888
}];
880889
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
881890
OCMVerifyAll(_mockBackend);
@@ -900,6 +909,8 @@ - (void)testUpdateEmailAutoSignOut {
900909
callback:[OCMArg any]])
901910
.andDispatchError2([FIRAuthErrorUtils
902911
invalidUserTokenErrorWithMessage:nil]);
912+
#pragma clang diagnostic push
913+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
903914
[user updateEmail:kNewEmail
904915
completion:^(NSError *_Nullable error) {
905916
XCTAssertTrue([NSThread isMainThread]);
@@ -913,6 +924,7 @@ - (void)testUpdateEmailAutoSignOut {
913924
XCTAssertNil([FIRAuth auth].currentUser);
914925
[expectation fulfill];
915926
}];
927+
#pragma clang diagnostic pop
916928
}];
917929
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
918930
OCMVerifyAll(_mockBackend);

FirebaseAuth/Tests/Unit/SwiftAPI.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,9 @@ class AuthAPI_hOnlyTests: XCTestCase {
9696

9797
func FIRAuth_h(credential: AuthCredential) throws {
9898
let auth = FirebaseAuth.Auth.auth()
99-
let authApp = FirebaseAuth.Auth.auth(app: FirebaseApp.app()!)
10099
let user = auth.currentUser!
101100
auth.updateCurrentUser(user) { _ in
102101
}
103-
authApp.fetchSignInMethods(forEmail: "[email protected]") { string, error in
104-
}
105102
auth.signIn(withEmail: "[email protected]", password: "password") { result, error in
106103
}
107104
auth.signIn(withEmail: "[email protected]", link: "link") { result, error in
@@ -173,7 +170,6 @@ class AuthAPI_hOnlyTests: XCTestCase {
173170
let auth = FirebaseAuth.Auth.auth()
174171
let user = auth.currentUser!
175172
try await auth.updateCurrentUser(user)
176-
_ = try await auth.fetchSignInMethods(forEmail: "[email protected]")
177173
_ = try await auth.signIn(withEmail: "[email protected]", password: "password")
178174
_ = try await auth.signIn(withEmail: "[email protected]", link: "link")
179175
_ = try await auth.signIn(with: credential)
@@ -574,8 +570,6 @@ class AuthAPI_hOnlyTests: XCTestCase {
574570
let auth = FirebaseAuth.Auth.auth()
575571
let user = auth.currentUser!
576572
let credential = GoogleAuthProvider.credential(withIDToken: "token", accessToken: "aToken")
577-
user.updateEmail(to: "email") { _ in
578-
}
579573
user.updatePassword(to: "password") { _ in
580574
}
581575
let changeRequest = user.createProfileChangeRequest()
@@ -648,7 +642,6 @@ class AuthAPI_hOnlyTests: XCTestCase {
648642
let auth = FirebaseAuth.Auth.auth()
649643
let user = auth.currentUser!
650644
let credential = GoogleAuthProvider.credential(withIDToken: "token", accessToken: "aToken")
651-
try await user.updateEmail(to: "email")
652645
try await user.updatePassword(to: "password")
653646
let changeRequest = user.createProfileChangeRequest()
654647
try await user.reload()

0 commit comments

Comments
 (0)