Skip to content

Email enumeration protection related error and doc updates#12081

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion FirebaseAuth/Sources/Backend/FIRAuthBackend.m
Original file line numberDiff line numberDiff line change
Expand Up@@ -551,6 +551,12 @@
*/
static NSString *const kInvalidRecaptchaVersion = @"INVALID_RECAPTCHA_VERSION";

/** @var kInvalidLoginCredentials
@brief This is the error message the server will respond with if the login credentials is
invalid. in the request.
*/
static NSString *const kInvalidLoginCredentials = @"INVALID_LOGIN_CREDENTIALS";

/** @var gBackendImplementation
@brief The singleton FIRAuthBackendImplementation instance to use.
*/
Expand DownExpand Up@@ -1404,7 +1410,8 @@ + (nullable NSError *)clientErrorWithServerErrorMessage:(NSString *)serverErrorM
}

if ([shortErrorMessage isEqualToString:kInvalidCredentialErrorMessage] ||
[shortErrorMessage isEqualToString:kInvalidPendingToken]) {
[shortErrorMessage isEqualToString:kInvalidPendingToken] ||
[shortErrorMessage isEqualToString:kInvalidLoginCredentials]) {
return [FIRAuthErrorUtils invalidCredentialErrorWithMessage:serverDetailErrorMessage];
}

Expand Down
19 changes: 14 additions & 5 deletions FirebaseAuth/Sources/Public/FirebaseAuth/FIRAuth.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -378,7 +378,9 @@ NS_SWIFT_NAME(Auth)

/** @fn fetchSignInMethodsForEmail:completion:
@brief Fetches the list of all sign-in methods previously used for the provided email address.

This method returns an empty list when [Email Enumeration
Protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection)
is enabled, irrespective of the number of authentication methods available for the given email.
@param email The email address for which to obtain a list of sign-in methods.
@param completion Optionally; a block which is invoked when the list of sign in methods for the
specified email address is ready or an error was encountered. Invoked asynchronously on the
Expand All@@ -393,10 +395,15 @@ NS_SWIFT_NAME(Auth)

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

/** @fn signInWithEmail:password:completion:
@brief Signs in using an email address and password.
@brief Signs in using an email address and password. When [Email Enumeration
Protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection)
is enabled, this method fails with FIRAuthErrorCodeInvalidCredentials in case of an invalid
email/password.

@param email The user's email address.
@param password The user's password.
Expand DownExpand Up@@ -663,8 +670,10 @@ NS_SWIFT_NAME(Auth)
- (void)applyActionCode:(NSString *)code completion:(void (^)(NSError *_Nullable error))completion;

/** @fn sendPasswordResetWithEmail:completion:
@brief Initiates a password reset for the given email address.

@brief Initiates a password reset for the given email address. This method does not throw an
error when there's no user account with the given email address and [Email Enumeration
Protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection)
is enabled.
@param email The email address of the user.
@param completion Optionally; a block which is invoked when the request finishes. Invoked
asynchronously on the main thread in the future.
Expand Down
7 changes: 5 additions & 2 deletions FirebaseAuth/Sources/Public/FirebaseAuth/FIRUser.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -132,7 +132,9 @@ NS_SWIFT_NAME(User)

/** @fn updateEmail:completion:
@brief Updates the email address for the user. On success, the cached user profile data is
updated.
updated. Throws FIRAuthErrorCodeInvalidCredentials error when [Email Enumeration
Protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection)
is enabled.
@remarks May fail if there is already an account with this email address that was created using
email and password authentication.

Expand DownExpand Up@@ -160,7 +162,8 @@ NS_SWIFT_NAME(User)
*/
- (void)updateEmail:(NSString *)email
completion:(nullable void (^)(NSError *_Nullable error))completion
NS_SWIFT_NAME(updateEmail(to:completion:));
NS_SWIFT_NAME(updateEmail(to:completion:))
DEPRECATED_MSG_ATTRIBUTE("Use sendEmailVerificationBeforeUpdatingEmail: instead.");

/** @fn updatePassword:completion:
@brief Updates the password for the user. On success, the cached user profile data is updated.
Expand Down
4 changes: 4 additions & 0 deletions FirebaseAuth/Sources/Utilities/FIRAuthErrorUtils.m
Original file line numberDiff line numberDiff line change
Expand Up@@ -626,6 +626,10 @@
@"The reCAPTCHA SDK is not linked to your app. See "
@"https://cloud.google.com/recaptcha-enterprise/docs/instrument-ios-apps";

static NSString *const kFIRAuthErrorMessageInvalidLoginCredentials =
@"Login credentials invalid. It is possible that the email/password combination does not "
@"exist.";

/** @var FIRAuthErrorDescription
@brief The error descrioption, based on the error code.
@remarks No default case so that we get a compiler warning if a new value was added to the enum.
Expand Down
6 changes: 6 additions & 0 deletions FirebaseAuth/Tests/Unit/FIRAuthTests.m
Original file line numberDiff line numberDiff line change
Expand Up@@ -433,6 +433,8 @@ - (void)testFetchSignInMethodsForEmailSuccess {
});
});
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[FIRAuth auth] fetchSignInMethodsForEmail:kEmail
completion:^(NSArray<NSString *> *_Nullable signInMethods,
NSError *_Nullable error) {
Expand All@@ -442,6 +444,7 @@ - (void)testFetchSignInMethodsForEmailSuccess {
XCTAssertNil(error);
[expectation fulfill];
}];
#pragma clang diagnostic pop
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
Expand All@@ -453,6 +456,8 @@ - (void)testFetchSignInMethodsForEmailFailure {
OCMExpect([_mockBackend createAuthURI:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils tooManyRequestsErrorWithMessage:nil]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[FIRAuth auth] fetchSignInMethodsForEmail:kEmail
completion:^(NSArray<NSString *> *_Nullable signInMethods,
NSError *_Nullable error) {
Expand All@@ -462,6 +467,7 @@ - (void)testFetchSignInMethodsForEmailFailure {
XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
[expectation fulfill];
}];
#pragma clang diagnostic pop
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}
Expand Down
12 changes: 12 additions & 0 deletions FirebaseAuth/Tests/Unit/FIRUserTests.m
Original file line numberDiff line numberDiff line change
Expand Up@@ -769,6 +769,8 @@ - (void)testUpdateEmailSuccess {
callback(mockSetAccountInfoResponse, nil);
});
});
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[user updateEmail:kNewEmail
completion:^(NSError *_Nullable error) {
XCTAssertNil(error);
Expand All@@ -777,6 +779,7 @@ - (void)testUpdateEmailSuccess {
kNewDisplayName);
[expectation fulfill];
}];
#pragma clang diagnostic pop
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
Expand DownExpand Up@@ -829,6 +832,8 @@ - (void)testUpdateEmailWithAuthLinkAccountSuccess {
callback(mockSetAccountInfoResponse, nil);
});
});
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[user updateEmail:kNewEmail
completion:^(NSError *_Nullable error) {
XCTAssertNil(error);
Expand All@@ -838,6 +843,7 @@ - (void)testUpdateEmailWithAuthLinkAccountSuccess {
XCTAssertFalse(user.isAnonymous);
[expectation fulfill];
}];
#pragma clang diagnostic pop
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
Expand All@@ -862,6 +868,8 @@ - (void)testUpdateEmailFailure {
callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils
invalidEmailErrorWithMessage:nil]);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[user
updateEmail:kNewEmail
completion:^(NSError *_Nullable error) {
Expand All@@ -876,6 +884,7 @@ - (void)testUpdateEmailFailure {
user);
[expectation fulfill];
}];
#pragma clang diagnostic pop
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
Expand All@@ -900,6 +909,8 @@ - (void)testUpdateEmailAutoSignOut {
callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils
invalidUserTokenErrorWithMessage:nil]);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[user updateEmail:kNewEmail
completion:^(NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
Expand All@@ -913,6 +924,7 @@ - (void)testUpdateEmailAutoSignOut {
XCTAssertNil([FIRAuth auth].currentUser);
[expectation fulfill];
}];
#pragma clang diagnostic pop
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
Expand Down
7 changes: 0 additions & 7 deletions FirebaseAuth/Tests/Unit/SwiftAPI.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,12 +96,9 @@ class AuthAPI_hOnlyTests: XCTestCase {

func FIRAuth_h(credential: AuthCredential) throws {
let auth = FirebaseAuth.Auth.auth()
let authApp = FirebaseAuth.Auth.auth(app: FirebaseApp.app()!)
let user = auth.currentUser!
auth.updateCurrentUser(user) { _ in
}
authApp.fetchSignInMethods(forEmail: "abc@abc.com") { string, error in
}
auth.signIn(withEmail: "abc@abc.com", password: "password") { result, error in
}
auth.signIn(withEmail: "abc@abc.com", link: "link") { result, error in
Expand DownExpand Up@@ -173,7 +170,6 @@ class AuthAPI_hOnlyTests: XCTestCase {
let auth = FirebaseAuth.Auth.auth()
let user = auth.currentUser!
try await auth.updateCurrentUser(user)
_ = try await auth.fetchSignInMethods(forEmail: "abc@abc.com")
_ = try await auth.signIn(withEmail: "abc@abc.com", password: "password")
_ = try await auth.signIn(withEmail: "abc@abc.com", link: "link")
_ = try await auth.signIn(with: credential)
Expand DownExpand Up@@ -574,8 +570,6 @@ class AuthAPI_hOnlyTests: XCTestCase {
let auth = FirebaseAuth.Auth.auth()
let user = auth.currentUser!
let credential = GoogleAuthProvider.credential(withIDToken: "token", accessToken: "aToken")
user.updateEmail(to: "email") { _ in
}
user.updatePassword(to: "password") { _ in
}
let changeRequest = user.createProfileChangeRequest()
Expand DownExpand Up@@ -648,7 +642,6 @@ class AuthAPI_hOnlyTests: XCTestCase {
let auth = FirebaseAuth.Auth.auth()
let user = auth.currentUser!
let credential = GoogleAuthProvider.credential(withIDToken: "token", accessToken: "aToken")
try await user.updateEmail(to: "email")
try await user.updatePassword(to: "password")
let changeRequest = user.createProfileChangeRequest()
try await user.reload()
Expand Down
close