Skip to content

Commit 9291d5c

Browse files
committed
Expose INVALID_LOGIN_CREDENTIALS as auth/invalid-credential error.
Update the doc snippets for various SDK methods to explain the behavior when Email Enumeration Protection is enabled. Mark fetchSignInMethodsForEmail and updateEmail as deprecated. Update the demo app to use the error code. Fix error message for the error code and update tests.
1 parent 3533b32 commit 9291d5c

File tree

11 files changed

+43
-17
lines changed

11 files changed

+43
-17
lines changed

.changeset/silly-islands-join.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@firebase/auth': patch
3+
---
4+
5+
Exposed INVALID_LOGIN_CREDENTIALS as auth/invalid-credential error and updated docs for various auth SDK methods.

common/api-review/auth.api.md

+1
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ export const AuthErrorCodes: {
165165
readonly INVALID_EMAIL: "auth/invalid-email";
166166
readonly INVALID_EMULATOR_SCHEME: "auth/invalid-emulator-scheme";
167167
readonly INVALID_IDP_RESPONSE: "auth/invalid-credential";
168+
readonly INVALID_LOGIN_CREDENTIALS: "auth/invalid-credential";
168169
readonly INVALID_MESSAGE_PAYLOAD: "auth/invalid-message-payload";
169170
readonly INVALID_MFA_SESSION: "auth/invalid-multi-factor-session";
170171
readonly INVALID_OAUTH_CLIENT_ID: "auth/invalid-oauth-client-id";

docs-devsite/auth.md

+8-7
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@ Firebase Authentication
2828
| [confirmPasswordReset(auth, oobCode, newPassword)](./auth.md#confirmpasswordreset) | Completes the password reset process, given a confirmation code and new password. |
2929
| [connectAuthEmulator(auth, url, options)](./auth.md#connectauthemulator) | Changes the [Auth](./auth.auth.md#auth_interface) instance to communicate with the Firebase Auth Emulator, instead of production Firebase Auth services. |
3030
| [createUserWithEmailAndPassword(auth, email, password)](./auth.md#createuserwithemailandpassword) | Creates a new user account associated with the specified email address and password. |
31-
| [fetchSignInMethodsForEmail(auth, email)](./auth.md#fetchsigninmethodsforemail) | Gets the list of possible sign in methods for the given email address. |
31+
| [fetchSignInMethodsForEmail(auth, email)](./auth.md#fetchsigninmethodsforemail) | Gets the list of possible sign in methods for the given 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. |
3232
| [getMultiFactorResolver(auth, error)](./auth.md#getmultifactorresolver) | Provides a [MultiFactorResolver](./auth.multifactorresolver.md#multifactorresolver_interface) suitable for completion of a multi-factor flow. |
3333
| [getRedirectResult(auth, resolver)](./auth.md#getredirectresult) | Returns a [UserCredential](./auth.usercredential.md#usercredential_interface) from the redirect-based sign-in flow. |
3434
| [initializeRecaptchaConfig(auth)](./auth.md#initializerecaptchaconfig) | Loads the reCAPTCHA configuration into the <code>Auth</code> instance. |
3535
| [isSignInWithEmailLink(auth, emailLink)](./auth.md#issigninwithemaillink) | Checks if an incoming link is a sign-in with email link suitable for [signInWithEmailLink()](./auth.md#signinwithemaillink)<!-- -->. |
3636
| [onAuthStateChanged(auth, nextOrObserver, error, completed)](./auth.md#onauthstatechanged) | Adds an observer for changes to the user's sign-in state. |
3737
| [onIdTokenChanged(auth, nextOrObserver, error, completed)](./auth.md#onidtokenchanged) | Adds an observer for changes to the signed-in user's ID token. |
38-
| [sendPasswordResetEmail(auth, email, actionCodeSettings)](./auth.md#sendpasswordresetemail) | Sends a password reset email to the given email address. |
38+
| [sendPasswordResetEmail(auth, email, actionCodeSettings)](./auth.md#sendpasswordresetemail) | Sends a password reset email to the given email address. This method does not throw an error when \[Email Enumeration Protection\](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection) is enabled. |
3939
| [sendSignInLinkToEmail(auth, email, actionCodeSettings)](./auth.md#sendsigninlinktoemail) | Sends a sign-in email link to the user with the specified email. |
4040
| [setPersistence(auth, persistence)](./auth.md#setpersistence) | Changes the type of persistence on the [Auth](./auth.auth.md#auth_interface) instance for the currently saved <code>Auth</code> session and applies this type of persistence for future sign-in requests, including sign-in with redirect requests. |
4141
| [signInAnonymously(auth)](./auth.md#signinanonymously) | Asynchronously signs in as an anonymous user. |
@@ -396,7 +396,7 @@ Promise&lt;[UserCredential](./auth.usercredential.md#usercredential_interface)<!
396396

397397
## fetchSignInMethodsForEmail()
398398

399-
Gets the list of possible sign in methods for the given email address.
399+
Gets the list of possible sign in methods for the given 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.
400400

401401
This is useful to differentiate methods of sign-in for the same provider, eg. [EmailAuthProvider](./auth.emailauthprovider.md#emailauthprovider_class) which has 2 methods of sign-in, [SignInMethod](./auth.md#signinmethod)<!-- -->.EMAIL\_PASSWORD and [SignInMethod](./auth.md#signinmethod)<!-- -->.EMAIL\_LINK.
402402

@@ -411,7 +411,7 @@ export declare function fetchSignInMethodsForEmail(auth: Auth, email: string): P
411411
| Parameter | Type | Description |
412412
| --- | --- | --- |
413413
| auth | [Auth](./auth.auth.md#auth_interface) | The [Auth](./auth.auth.md#auth_interface) instance. |
414-
| email | string | The user's email address. |
414+
| email | string | The user's email address.<!-- -->Deprecated Migrating off of this method is recommended as a security best-practice. |
415415

416416
<b>Returns:</b>
417417

@@ -600,7 +600,7 @@ export declare function onIdTokenChanged(auth: Auth, nextOrObserver: NextOrObser
600600

601601
## sendPasswordResetEmail()
602602

603-
Sends a password reset email to the given email address.
603+
Sends a password reset email to the given email address. This method does not throw an error when \[Email Enumeration Protection\](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection) is enabled.
604604

605605
To complete the password reset, call [confirmPasswordReset()](./auth.md#confirmpasswordreset) with the code supplied in the email sent to the user, along with the new password specified by the user.
606606

@@ -803,7 +803,7 @@ Promise&lt;[UserCredential](./auth.usercredential.md#usercredential_interface)<!
803803

804804
Asynchronously signs in using an email and password.
805805

806-
Fails with an error if the email address and password do not match.
806+
Fails with an error if the email address and password do not match. When \[Email Enumeration Protection\](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection) is enabled, this method fails with "auth/invalid-credential" in case of an invalid email/password.
807807

808808
Note: The user's password is NOT the password used to access the user's email account. The email address serves as a unique identifier for the user, and the password is used to access the user's account in your Firebase project. See also: [createUserWithEmailAndPassword()](./auth.md#createuserwithemailandpassword)<!-- -->.
809809

@@ -1611,7 +1611,7 @@ export declare function updateEmail(user: User, newEmail: string): Promise<void>
16111611
| Parameter | Type | Description |
16121612
| --- | --- | --- |
16131613
| user | [User](./auth.user.md#user_interface) | The user. |
1614-
| newEmail | string | The new email address. |
1614+
| newEmail | string | The new email address.<!-- -->Throws "auth/operation-not-allowed" error when \[Email Enumeration Protection\](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection) is enabled. Deprecated - Use [verifyBeforeUpdateEmail()](./auth.md#verifybeforeupdateemail) instead. |
16151615

16161616
<b>Returns:</b>
16171617

@@ -1829,6 +1829,7 @@ AUTH_ERROR_CODES_MAP_DO_NOT_USE_INTERNALLY: {
18291829
readonly INVALID_EMAIL: "auth/invalid-email";
18301830
readonly INVALID_EMULATOR_SCHEME: "auth/invalid-emulator-scheme";
18311831
readonly INVALID_IDP_RESPONSE: "auth/invalid-credential";
1832+
readonly INVALID_LOGIN_CREDENTIALS: "auth/invalid-credential";
18321833
readonly INVALID_MESSAGE_PAYLOAD: "auth/invalid-message-payload";
18331834
readonly INVALID_MFA_SESSION: "auth/invalid-multi-factor-session";
18341835
readonly INVALID_OAUTH_CLIENT_ID: "auth/invalid-oauth-client-id";

packages/auth/demo/src/index.js

+5
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,11 @@ function onAuthError(error) {
316316
if (error.code === 'auth/user-token-expired') {
317317
alertError('Token expired, please reauthenticate.');
318318
}
319+
if (error.code === 'auth/invalid-credential') {
320+
alertError(
321+
'login credentials invalid. It is possible that the email/password combination does not exist.'
322+
);
323+
}
319324
}
320325
}
321326

packages/auth/src/api/authentication/idp.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ describe('api/authentication/signInWithIdp', () => {
8383

8484
await expect(signInWithIdp(auth, request)).to.be.rejectedWith(
8585
FirebaseError,
86-
'Firebase: The supplied auth credential is malformed or has expired. (auth/invalid-credential).'
86+
'Firebase: The supplied auth credential is incorrect, malformed or has expired. (auth/invalid-credential).'
8787
);
8888
expect(mock.calls[0].request).to.eql(request);
8989
});

packages/auth/src/api/authentication/mfa.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ describe('api/authentication/startSignInPhoneMfa', () => {
8585

8686
await expect(startSignInPhoneMfa(auth, request)).to.be.rejectedWith(
8787
FirebaseError,
88-
'Firebase: The supplied auth credential is malformed or has expired. (auth/invalid-credential).'
88+
'Firebase: The supplied auth credential is incorrect, malformed or has expired. (auth/invalid-credential).'
8989
);
9090
expect(mock.calls[0].request).to.eql(request);
9191
});

packages/auth/src/api/errors.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export const enum ServerError {
4444
INVALID_ID_TOKEN = 'INVALID_ID_TOKEN',
4545
INVALID_IDP_RESPONSE = 'INVALID_IDP_RESPONSE',
4646
INVALID_IDENTIFIER = 'INVALID_IDENTIFIER',
47+
INVALID_LOGIN_CREDENTIALS = 'INVALID_LOGIN_CREDENTIALS',
4748
INVALID_MESSAGE_PAYLOAD = 'INVALID_MESSAGE_PAYLOAD',
4849
INVALID_MFA_PENDING_CREDENTIAL = 'INVALID_MFA_PENDING_CREDENTIAL',
4950
INVALID_OAUTH_CLIENT_ID = 'INVALID_OAUTH_CLIENT_ID',
@@ -144,14 +145,17 @@ export const SERVER_ERROR_MAP: Partial<ServerErrorMap<ServerError>> = {
144145
[ServerError.INVALID_PASSWORD]: AuthErrorCode.INVALID_PASSWORD,
145146
// This can only happen if the SDK sends a bad request.
146147
[ServerError.MISSING_PASSWORD]: AuthErrorCode.MISSING_PASSWORD,
148+
// Thrown if Email Enumeration Protection is enabled in the project and the email or password is
149+
// invalid.
150+
[ServerError.INVALID_LOGIN_CREDENTIALS]: AuthErrorCode.INVALID_CREDENTIAL,
147151

148152
// Sign up with email and password errors.
149153
[ServerError.EMAIL_EXISTS]: AuthErrorCode.EMAIL_EXISTS,
150154
[ServerError.PASSWORD_LOGIN_DISABLED]: AuthErrorCode.OPERATION_NOT_ALLOWED,
151155

152156
// Verify assertion for sign in with credential errors:
153-
[ServerError.INVALID_IDP_RESPONSE]: AuthErrorCode.INVALID_IDP_RESPONSE,
154-
[ServerError.INVALID_PENDING_TOKEN]: AuthErrorCode.INVALID_IDP_RESPONSE,
157+
[ServerError.INVALID_IDP_RESPONSE]: AuthErrorCode.INVALID_CREDENTIAL,
158+
[ServerError.INVALID_PENDING_TOKEN]: AuthErrorCode.INVALID_CREDENTIAL,
155159
[ServerError.FEDERATED_USER_ID_ALREADY_LINKED]:
156160
AuthErrorCode.CREDENTIAL_ALREADY_IN_USE,
157161

@@ -184,7 +188,7 @@ export const SERVER_ERROR_MAP: Partial<ServerErrorMap<ServerError>> = {
184188
// Phone Auth related errors.
185189
[ServerError.INVALID_CODE]: AuthErrorCode.INVALID_CODE,
186190
[ServerError.INVALID_SESSION_INFO]: AuthErrorCode.INVALID_SESSION_INFO,
187-
[ServerError.INVALID_TEMPORARY_PROOF]: AuthErrorCode.INVALID_IDP_RESPONSE,
191+
[ServerError.INVALID_TEMPORARY_PROOF]: AuthErrorCode.INVALID_CREDENTIAL,
188192
[ServerError.MISSING_SESSION_INFO]: AuthErrorCode.MISSING_SESSION_INFO,
189193
[ServerError.SESSION_EXPIRED]: AuthErrorCode.CODE_EXPIRED,
190194

packages/auth/src/core/errors.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export const enum AuthErrorCode {
6060
INVALID_DYNAMIC_LINK_DOMAIN = 'invalid-dynamic-link-domain',
6161
INVALID_EMAIL = 'invalid-email',
6262
INVALID_EMULATOR_SCHEME = 'invalid-emulator-scheme',
63-
INVALID_IDP_RESPONSE = 'invalid-credential',
63+
INVALID_CREDENTIAL = 'invalid-credential',
6464
INVALID_MESSAGE_PAYLOAD = 'invalid-message-payload',
6565
INVALID_MFA_SESSION = 'invalid-multi-factor-session',
6666
INVALID_OAUTH_CLIENT_ID = 'invalid-oauth-client-id',
@@ -217,8 +217,8 @@ function _debugErrorMap(): ErrorMap<AuthErrorCode> {
217217
'Your API key is invalid, please check you have copied it correctly.',
218218
[AuthErrorCode.INVALID_CERT_HASH]:
219219
'The SHA-1 certificate hash provided is invalid.',
220-
[AuthErrorCode.INVALID_IDP_RESPONSE]:
221-
'The supplied auth credential is malformed or has expired.',
220+
[AuthErrorCode.INVALID_CREDENTIAL]:
221+
'The supplied auth credential is incorrect, malformed or has expired.',
222222
[AuthErrorCode.INVALID_MESSAGE_PAYLOAD]:
223223
'The email template corresponding to this action contains invalid characters in its message. ' +
224224
'Please fix by going to the Auth email templates section in the Firebase Console.',
@@ -528,6 +528,7 @@ export const AUTH_ERROR_CODES_MAP_DO_NOT_USE_INTERNALLY = {
528528
INVALID_EMAIL: 'auth/invalid-email',
529529
INVALID_EMULATOR_SCHEME: 'auth/invalid-emulator-scheme',
530530
INVALID_IDP_RESPONSE: 'auth/invalid-credential',
531+
INVALID_LOGIN_CREDENTIALS: 'auth/invalid-credential',
531532
INVALID_MESSAGE_PAYLOAD: 'auth/invalid-message-payload',
532533
INVALID_MFA_SESSION: 'auth/invalid-multi-factor-session',
533534
INVALID_OAUTH_CLIENT_ID: 'auth/invalid-oauth-client-id',

packages/auth/src/core/strategies/email.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ import { _setActionCodeSettingsOnRequest } from './action_code_settings';
3333
import { getModularInstance } from '@firebase/util';
3434

3535
/**
36-
* Gets the list of possible sign in methods for the given email address.
36+
* Gets the list of possible sign in methods for the given email address. This method returns an
37+
* empty list when [Email Enumeration Protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection) is enabled, irrespective of the number of
38+
* authentication methods available for the given email.
3739
*
3840
* @remarks
3941
* This is useful to differentiate methods of sign-in for the same provider, eg.
@@ -44,6 +46,7 @@ import { getModularInstance } from '@firebase/util';
4446
* @param auth - The {@link Auth} instance.
4547
* @param email - The user's email address.
4648
*
49+
* Deprecated Migrating off of this method is recommended as a security best-practice.
4750
* @public
4851
*/
4952
export async function fetchSignInMethodsForEmail(

packages/auth/src/core/strategies/email_and_password.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ async function recachePasswordPolicy(auth: Auth): Promise<void> {
6161
}
6262

6363
/**
64-
* Sends a password reset email to the given email address.
64+
* Sends a password reset email to the given email address. This method does not throw an error when
65+
* [Email Enumeration Protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection) is enabled.
6566
*
6667
* @remarks
6768
* To complete the password reset, call {@link confirmPasswordReset} with the code supplied in
@@ -303,6 +304,8 @@ export async function createUserWithEmailAndPassword(
303304
*
304305
* @remarks
305306
* Fails with an error if the email address and password do not match.
307+
* When [Email Enumeration Protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection) is enabled,
308+
* this method fails with "auth/invalid-credential" in case of an invalid email/password.
306309
*
307310
* Note: The user's password is NOT the password used to access the user's email account. The
308311
* email address serves as a unique identifier for the user, and the password is used to access

packages/auth/src/core/user/account_info.ts

+3
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ export async function updateProfile(
8888
* @param user - The user.
8989
* @param newEmail - The new email address.
9090
*
91+
* Throws "auth/operation-not-allowed" error when [Email Enumeration Protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection) is enabled.
92+
* Deprecated - Use {@link verifyBeforeUpdateEmail} instead.
93+
*
9194
* @public
9295
*/
9396
export function updateEmail(user: User, newEmail: string): Promise<void> {

0 commit comments

Comments
 (0)