Skip to content

Expose INVALID_LOGIN_CREDENTIALS as auth/invalid-credential error. #7772

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 6 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/silly-islands-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@firebase/auth': patch
---

Exposed INVALID_LOGIN_CREDENTIALS as auth/invalid-credential error and updated docs for various auth SDK methods.
1 change: 1 addition & 0 deletions common/api-review/auth.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ export const AuthErrorCodes: {
readonly INVALID_EMAIL: "auth/invalid-email";
readonly INVALID_EMULATOR_SCHEME: "auth/invalid-emulator-scheme";
readonly INVALID_IDP_RESPONSE: "auth/invalid-credential";
readonly INVALID_LOGIN_CREDENTIALS: "auth/invalid-credential";
readonly INVALID_MESSAGE_PAYLOAD: "auth/invalid-message-payload";
readonly INVALID_MFA_SESSION: "auth/invalid-multi-factor-session";
readonly INVALID_OAUTH_CLIENT_ID: "auth/invalid-oauth-client-id";
Expand Down
16 changes: 9 additions & 7 deletions docs-devsite/auth.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Project: /docs/reference/js/_project.yaml
Project: /docs/reference/js/_project.yaml
Book: /docs/reference/_book.yaml
page_type: reference

Expand Down Expand Up @@ -28,13 +28,14 @@ Firebase Authentication
| [confirmPasswordReset(auth, oobCode, newPassword)](./auth.md#confirmpasswordreset) | Completes the password reset process, given a confirmation code and new password. |
| [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. |
| [createUserWithEmailAndPassword(auth, email, password)](./auth.md#createuserwithemailandpassword) | Creates a new user account associated with the specified email address and password. |
| [fetchSignInMethodsForEmail(auth, email)](./auth.md#fetchsigninmethodsforemail) | Gets the list of possible sign in methods for the given email address. |
| [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. |
| [getMultiFactorResolver(auth, error)](./auth.md#getmultifactorresolver) | Provides a [MultiFactorResolver](./auth.multifactorresolver.md#multifactorresolver_interface) suitable for completion of a multi-factor flow. |
| [getRedirectResult(auth, resolver)](./auth.md#getredirectresult) | Returns a [UserCredential](./auth.usercredential.md#usercredential_interface) from the redirect-based sign-in flow. |
| [initializeRecaptchaConfig(auth)](./auth.md#initializerecaptchaconfig) | Loads the reCAPTCHA configuration into the <code>Auth</code> instance. |
| [isSignInWithEmailLink(auth, emailLink)](./auth.md#issigninwithemaillink) | Checks if an incoming link is a sign-in with email link suitable for [signInWithEmailLink()](./auth.md#signinwithemaillink)<!-- -->. |
| [onAuthStateChanged(auth, nextOrObserver, error, completed)](./auth.md#onauthstatechanged) | Adds an observer for changes to the user's sign-in state. |
| [onIdTokenChanged(auth, nextOrObserver, error, completed)](./auth.md#onidtokenchanged) | Adds an observer for changes to the signed-in user's ID token. |
| [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. |
| [revokeAccessToken(auth, token)](./auth.md#revokeaccesstoken) | Revokes the given access token. Currently only supports Apple OAuth access tokens. |
| [sendPasswordResetEmail(auth, email, actionCodeSettings)](./auth.md#sendpasswordresetemail) | Sends a password reset email to the given email address. |
| [sendSignInLinkToEmail(auth, email, actionCodeSettings)](./auth.md#sendsigninlinktoemail) | Sends a sign-in email link to the user with the specified email. |
Expand Down Expand Up @@ -397,7 +398,7 @@ Promise&lt;[UserCredential](./auth.usercredential.md#usercredential_interface)<!

## fetchSignInMethodsForEmail()

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

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.

Expand All @@ -412,7 +413,7 @@ export declare function fetchSignInMethodsForEmail(auth: Auth, email: string): P
| Parameter | Type | Description |
| --- | --- | --- |
| auth | [Auth](./auth.auth.md#auth_interface) | The [Auth](./auth.auth.md#auth_interface) instance. |
| email | string | The user's email address. |
| email | string | The user's email address.<!-- -->Deprecated Migrating off of this method is recommended as a security best-practice. |
Copy link
Contributor

@DellaBitta DellaBitta Nov 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing a period here. Based on the other Deprecated things in this document, I think it should be 'Deprecated.'


<b>Returns:</b>

Expand Down Expand Up @@ -622,7 +623,7 @@ Promise&lt;void&gt;

## sendPasswordResetEmail()

Sends a password reset email to the given email address.
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.

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.

Expand Down Expand Up @@ -825,7 +826,7 @@ Promise&lt;[UserCredential](./auth.usercredential.md#usercredential_interface)<!

Asynchronously signs in using an email and password.

Fails with an error if the email address and password do not match.
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.

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)<!-- -->.

Expand Down Expand Up @@ -1633,7 +1634,7 @@ export declare function updateEmail(user: User, newEmail: string): Promise<void>
| Parameter | Type | Description |
| --- | --- | --- |
| user | [User](./auth.user.md#user_interface) | The user. |
| newEmail | string | The new email address. |
| 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. |

<b>Returns:</b>

Expand Down Expand Up @@ -1851,6 +1852,7 @@ AUTH_ERROR_CODES_MAP_DO_NOT_USE_INTERNALLY: {
readonly INVALID_EMAIL: "auth/invalid-email";
readonly INVALID_EMULATOR_SCHEME: "auth/invalid-emulator-scheme";
readonly INVALID_IDP_RESPONSE: "auth/invalid-credential";
readonly INVALID_LOGIN_CREDENTIALS: "auth/invalid-credential";
readonly INVALID_MESSAGE_PAYLOAD: "auth/invalid-message-payload";
readonly INVALID_MFA_SESSION: "auth/invalid-multi-factor-session";
readonly INVALID_OAUTH_CLIENT_ID: "auth/invalid-oauth-client-id";
Expand Down
5 changes: 5 additions & 0 deletions packages/auth/demo/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,11 @@ function onAuthError(error) {
if (error.code === 'auth/user-token-expired') {
alertError('Token expired, please reauthenticate.');
}
if (error.code === 'auth/invalid-credential') {
alertError(
'login credentials invalid. It is possible that the email/password combination does not exist.'
);
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/auth/src/api/authentication/idp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ describe('api/authentication/signInWithIdp', () => {

await expect(signInWithIdp(auth, request)).to.be.rejectedWith(
FirebaseError,
'Firebase: The supplied auth credential is malformed or has expired. (auth/invalid-credential).'
'Firebase: The supplied auth credential is incorrect, malformed or has expired. (auth/invalid-credential).'
);
expect(mock.calls[0].request).to.eql(request);
});
Expand Down
2 changes: 1 addition & 1 deletion packages/auth/src/api/authentication/mfa.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ describe('api/authentication/startSignInPhoneMfa', () => {

await expect(startSignInPhoneMfa(auth, request)).to.be.rejectedWith(
FirebaseError,
'Firebase: The supplied auth credential is malformed or has expired. (auth/invalid-credential).'
'Firebase: The supplied auth credential is incorrect, malformed or has expired. (auth/invalid-credential).'
);
expect(mock.calls[0].request).to.eql(request);
});
Expand Down
2 changes: 1 addition & 1 deletion packages/auth/src/api/authentication/token.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ describe('api/authentication/revokeToken', () => {

await expect(revokeToken(auth, request)).to.be.rejectedWith(
FirebaseError,
'Firebase: The supplied auth credential is malformed or has expired. (auth/invalid-credential).'
'Firebase: The supplied auth credential is incorrect, malformed or has expired. (auth/invalid-credential).'
);
expect(mock.calls[0].request).to.eql(request);
});
Expand Down
10 changes: 7 additions & 3 deletions packages/auth/src/api/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const enum ServerError {
INVALID_ID_TOKEN = 'INVALID_ID_TOKEN',
INVALID_IDP_RESPONSE = 'INVALID_IDP_RESPONSE',
INVALID_IDENTIFIER = 'INVALID_IDENTIFIER',
INVALID_LOGIN_CREDENTIALS = 'INVALID_LOGIN_CREDENTIALS',
INVALID_MESSAGE_PAYLOAD = 'INVALID_MESSAGE_PAYLOAD',
INVALID_MFA_PENDING_CREDENTIAL = 'INVALID_MFA_PENDING_CREDENTIAL',
INVALID_OAUTH_CLIENT_ID = 'INVALID_OAUTH_CLIENT_ID',
Expand Down Expand Up @@ -144,14 +145,17 @@ export const SERVER_ERROR_MAP: Partial<ServerErrorMap<ServerError>> = {
[ServerError.INVALID_PASSWORD]: AuthErrorCode.INVALID_PASSWORD,
// This can only happen if the SDK sends a bad request.
[ServerError.MISSING_PASSWORD]: AuthErrorCode.MISSING_PASSWORD,
// Thrown if Email Enumeration Protection is enabled in the project and the email or password is
// invalid.
[ServerError.INVALID_LOGIN_CREDENTIALS]: AuthErrorCode.INVALID_CREDENTIAL,

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

// Verify assertion for sign in with credential errors:
[ServerError.INVALID_IDP_RESPONSE]: AuthErrorCode.INVALID_IDP_RESPONSE,
[ServerError.INVALID_PENDING_TOKEN]: AuthErrorCode.INVALID_IDP_RESPONSE,
[ServerError.INVALID_IDP_RESPONSE]: AuthErrorCode.INVALID_CREDENTIAL,
[ServerError.INVALID_PENDING_TOKEN]: AuthErrorCode.INVALID_CREDENTIAL,
[ServerError.FEDERATED_USER_ID_ALREADY_LINKED]:
AuthErrorCode.CREDENTIAL_ALREADY_IN_USE,

Expand Down Expand Up @@ -184,7 +188,7 @@ export const SERVER_ERROR_MAP: Partial<ServerErrorMap<ServerError>> = {
// Phone Auth related errors.
[ServerError.INVALID_CODE]: AuthErrorCode.INVALID_CODE,
[ServerError.INVALID_SESSION_INFO]: AuthErrorCode.INVALID_SESSION_INFO,
[ServerError.INVALID_TEMPORARY_PROOF]: AuthErrorCode.INVALID_IDP_RESPONSE,
[ServerError.INVALID_TEMPORARY_PROOF]: AuthErrorCode.INVALID_CREDENTIAL,
[ServerError.MISSING_SESSION_INFO]: AuthErrorCode.MISSING_SESSION_INFO,
[ServerError.SESSION_EXPIRED]: AuthErrorCode.CODE_EXPIRED,

Expand Down
7 changes: 4 additions & 3 deletions packages/auth/src/core/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const enum AuthErrorCode {
INVALID_DYNAMIC_LINK_DOMAIN = 'invalid-dynamic-link-domain',
INVALID_EMAIL = 'invalid-email',
INVALID_EMULATOR_SCHEME = 'invalid-emulator-scheme',
INVALID_IDP_RESPONSE = 'invalid-credential',
INVALID_CREDENTIAL = 'invalid-credential',
INVALID_MESSAGE_PAYLOAD = 'invalid-message-payload',
INVALID_MFA_SESSION = 'invalid-multi-factor-session',
INVALID_OAUTH_CLIENT_ID = 'invalid-oauth-client-id',
Expand Down Expand Up @@ -217,8 +217,8 @@ function _debugErrorMap(): ErrorMap<AuthErrorCode> {
'Your API key is invalid, please check you have copied it correctly.',
[AuthErrorCode.INVALID_CERT_HASH]:
'The SHA-1 certificate hash provided is invalid.',
[AuthErrorCode.INVALID_IDP_RESPONSE]:
'The supplied auth credential is malformed or has expired.',
[AuthErrorCode.INVALID_CREDENTIAL]:
'The supplied auth credential is incorrect, malformed or has expired.',
[AuthErrorCode.INVALID_MESSAGE_PAYLOAD]:
'The email template corresponding to this action contains invalid characters in its message. ' +
'Please fix by going to the Auth email templates section in the Firebase Console.',
Expand Down Expand Up @@ -528,6 +528,7 @@ export const AUTH_ERROR_CODES_MAP_DO_NOT_USE_INTERNALLY = {
INVALID_EMAIL: 'auth/invalid-email',
INVALID_EMULATOR_SCHEME: 'auth/invalid-emulator-scheme',
INVALID_IDP_RESPONSE: 'auth/invalid-credential',
INVALID_LOGIN_CREDENTIALS: 'auth/invalid-credential',

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be "auth/invalid-login-credentials", the missing code reported in #7661?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are reusing the existing error code 'auth/invalid-credential' since it is very similar to the backend error and is already public on the SDK.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason not to create a new error type?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am using firebase 10.6.0 and it is returning auth/invalid-login-credentials when there is an incorrect username or password. Will this change to start returning auth/invalid-credential in later updates?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am using firebase 10.6.0 and it is returning auth/invalid-login-credentials when there is an incorrect username or password. Will this change to start returning auth/invalid-credential in later updates?

That's correct. It is currently converting the server error "INVALID_LOGIN_CREDENTIALS" to lower-case and sending that error code -

We will now populate the error map so the server error maps to "auth/invalid-credential"

Is there a reason not to create a new error type?

This is to avoid creating another error code that is very similar to the existing invalid-credential code and to be consistent across iOS, Android and Web. On Android, we are reusing the https://firebase.google.com/docs/reference/android/com/google/firebase/auth/FirebaseAuthInvalidCredentialsException rather than create a new one, so following a similar approach on Web. Do you see any issue with this approach?

Changing behavior from auth/invalid-login-credentials (a non-exposed error code) to auth/invalid-credential (a public error code) can be a breaking change, but i am not sure, since the previous error code was not public. WDYT @DellaBitta ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like a good reason to me. Thanks for the clarification!

INVALID_MESSAGE_PAYLOAD: 'auth/invalid-message-payload',
INVALID_MFA_SESSION: 'auth/invalid-multi-factor-session',
INVALID_OAUTH_CLIENT_ID: 'auth/invalid-oauth-client-id',
Expand Down
5 changes: 4 additions & 1 deletion packages/auth/src/core/strategies/email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ import { _setActionCodeSettingsOnRequest } from './action_code_settings';
import { getModularInstance } from '@firebase/util';

/**
* Gets the list of possible sign in methods for the given email address.
* 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.
*
* @remarks
* This is useful to differentiate methods of sign-in for the same provider, eg.
Expand All @@ -44,6 +46,7 @@ import { getModularInstance } from '@firebase/util';
* @param auth - The {@link Auth} instance.
* @param email - The user's email address.
*
* Deprecated Migrating off of this method is recommended as a security best-practice.
* @public
*/
export async function fetchSignInMethodsForEmail(
Expand Down
6 changes: 5 additions & 1 deletion packages/auth/src/core/strategies/email_and_password.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ async function recachePasswordPolicy(auth: Auth): Promise<void> {
}

/**
* Sends a password reset email to the given email address.
* Sends a password reset email to 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.
*
* @remarks
* To complete the password reset, call {@link confirmPasswordReset} with the code supplied in
Expand Down Expand Up @@ -303,6 +305,8 @@ export async function createUserWithEmailAndPassword(
*
* @remarks
* 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.
*
* 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
Expand Down
3 changes: 3 additions & 0 deletions packages/auth/src/core/user/account_info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ export async function updateProfile(
* @param user - The user.
* @param newEmail - 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 {@link verifyBeforeUpdateEmail} instead.
*
* @public
*/
export function updateEmail(user: User, newEmail: string): Promise<void> {
Expand Down