diff --git a/.eslintrc.js b/.eslintrc.js index 1d4c74bd43..03e844dbf9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -71,6 +71,15 @@ module.exports = { "selector": "variable", "format": ["camelCase", "UPPER_CASE"] }, + { + "selector": "variable", + "modifiers": ["const"], + "format": ["PascalCase", "camelCase", "UPPER_CASE"], + "filter": { + "regex": "ErrorCode$", // Matches only if it ends exactly with ErrorCode + "match": true, + }, + }, { "selector": "parameter", "format": ["camelCase"], diff --git a/etc/firebase-admin.app-check.api.md b/etc/firebase-admin.app-check.api.md index 305104f8b9..3786a8aa88 100644 --- a/etc/firebase-admin.app-check.api.md +++ b/etc/firebase-admin.app-check.api.md @@ -17,7 +17,20 @@ export class AppCheck { } // @public -export type AppCheckErrorCode = 'aborted' | 'invalid-argument' | 'invalid-credential' | 'internal-error' | 'permission-denied' | 'unauthenticated' | 'not-found' | 'app-check-token-expired' | 'unknown-error'; +export const AppCheckErrorCode: { + readonly ABORTED: "aborted"; + readonly INVALID_ARGUMENT: "invalid-argument"; + readonly INVALID_CREDENTIAL: "invalid-credential"; + readonly INTERNAL: "internal-error"; + readonly PERMISSION_DENIED: "permission-denied"; + readonly UNAUTHENTICATED: "unauthenticated"; + readonly NOT_FOUND: "not-found"; + readonly APP_CHECK_TOKEN_EXPIRED: "app-check-token-expired"; + readonly UNKNOWN: "unknown-error"; +}; + +// @public +export type AppCheckErrorCode = typeof AppCheckErrorCode[keyof typeof AppCheckErrorCode]; // @public export interface AppCheckToken { diff --git a/etc/firebase-admin.app.api.md b/etc/firebase-admin.app.api.md index 7a539e3026..7f5e00ee3e 100644 --- a/etc/firebase-admin.app.api.md +++ b/etc/firebase-admin.app.api.md @@ -13,30 +13,22 @@ export interface App { } // @public -export class AppErrorCodes { - // (undocumented) - static APP_DELETED: string; - // (undocumented) - static DUPLICATE_APP: string; - // (undocumented) - static INTERNAL_ERROR: string; - // (undocumented) - static INVALID_APP_NAME: string; - // (undocumented) - static INVALID_APP_OPTIONS: string; - // (undocumented) - static INVALID_ARGUMENT: string; - // (undocumented) - static INVALID_CREDENTIAL: string; - // (undocumented) - static NETWORK_ERROR: string; - // (undocumented) - static NETWORK_TIMEOUT: string; - // (undocumented) - static NO_APP: string; - // (undocumented) - static UNABLE_TO_PARSE_RESPONSE: string; -} +export const AppErrorCode: { + readonly APP_DELETED: "app-deleted"; + readonly DUPLICATE_APP: "duplicate-app"; + readonly INVALID_ARGUMENT: "invalid-argument"; + readonly INTERNAL_ERROR: "internal-error"; + readonly INVALID_APP_NAME: "invalid-app-name"; + readonly INVALID_APP_OPTIONS: "invalid-app-options"; + readonly INVALID_CREDENTIAL: "invalid-credential"; + readonly NETWORK_ERROR: "network-error"; + readonly NETWORK_TIMEOUT: "network-timeout"; + readonly NO_APP: "no-app"; + readonly UNABLE_TO_PARSE_RESPONSE: "unable-to-parse-response"; +}; + +// @public +export type AppErrorCode = typeof AppErrorCode[keyof typeof AppErrorCode]; // @public export function applicationDefault(httpAgent?: Agent): Credential; diff --git a/etc/firebase-admin.auth.api.md b/etc/firebase-admin.auth.api.md index 9cf480f616..08b790af47 100644 --- a/etc/firebase-admin.auth.api.md +++ b/etc/firebase-admin.auth.api.md @@ -56,389 +56,107 @@ export class Auth extends BaseAuth { } // @public -export const AuthClientErrorCode: { - AUTH_BLOCKING_TOKEN_EXPIRED: { - code: string; - message: string; - }; - BILLING_NOT_ENABLED: { - code: string; - message: string; - }; - CLAIMS_TOO_LARGE: { - code: string; - message: string; - }; - CONFIGURATION_EXISTS: { - code: string; - message: string; - }; - CONFIGURATION_NOT_FOUND: { - code: string; - message: string; - }; - ID_TOKEN_EXPIRED: { - code: string; - message: string; - }; - INVALID_ARGUMENT: { - code: string; - message: string; - }; - INVALID_CONFIG: { - code: string; - message: string; - }; - EMAIL_ALREADY_EXISTS: { - code: string; - message: string; - }; - EMAIL_NOT_FOUND: { - code: string; - message: string; - }; - FORBIDDEN_CLAIM: { - code: string; - message: string; - }; - INVALID_ID_TOKEN: { - code: string; - message: string; - }; - ID_TOKEN_REVOKED: { - code: string; - message: string; - }; - INTERNAL_ERROR: { - code: string; - message: string; - }; - INVALID_CLAIMS: { - code: string; - message: string; - }; - INVALID_CONTINUE_URI: { - code: string; - message: string; - }; - INVALID_CREATION_TIME: { - code: string; - message: string; - }; - INVALID_CREDENTIAL: { - code: string; - message: string; - }; - INVALID_DISABLED_FIELD: { - code: string; - message: string; - }; - INVALID_DISPLAY_NAME: { - code: string; - message: string; - }; - INVALID_DYNAMIC_LINK_DOMAIN: { - code: string; - message: string; - }; - INVALID_HOSTING_LINK_DOMAIN: { - code: string; - message: string; - }; - INVALID_EMAIL_VERIFIED: { - code: string; - message: string; - }; - INVALID_EMAIL: { - code: string; - message: string; - }; - INVALID_NEW_EMAIL: { - code: string; - message: string; - }; - INVALID_ENROLLED_FACTORS: { - code: string; - message: string; - }; - INVALID_ENROLLMENT_TIME: { - code: string; - message: string; - }; - INVALID_HASH_ALGORITHM: { - code: string; - message: string; - }; - INVALID_HASH_BLOCK_SIZE: { - code: string; - message: string; - }; - INVALID_HASH_DERIVED_KEY_LENGTH: { - code: string; - message: string; - }; - INVALID_HASH_KEY: { - code: string; - message: string; - }; - INVALID_HASH_MEMORY_COST: { - code: string; - message: string; - }; - INVALID_HASH_PARALLELIZATION: { - code: string; - message: string; - }; - INVALID_HASH_ROUNDS: { - code: string; - message: string; - }; - INVALID_HASH_SALT_SEPARATOR: { - code: string; - message: string; - }; - INVALID_LAST_SIGN_IN_TIME: { - code: string; - message: string; - }; - INVALID_NAME: { - code: string; - message: string; - }; - INVALID_OAUTH_CLIENT_ID: { - code: string; - message: string; - }; - INVALID_PAGE_TOKEN: { - code: string; - message: string; - }; - INVALID_PASSWORD: { - code: string; - message: string; - }; - INVALID_PASSWORD_HASH: { - code: string; - message: string; - }; - INVALID_PASSWORD_SALT: { - code: string; - message: string; - }; - INVALID_PHONE_NUMBER: { - code: string; - message: string; - }; - INVALID_PHOTO_URL: { - code: string; - message: string; - }; - INVALID_PROJECT_ID: { - code: string; - message: string; - }; - INVALID_PROVIDER_DATA: { - code: string; - message: string; - }; - INVALID_PROVIDER_ID: { - code: string; - message: string; - }; - INVALID_PROVIDER_UID: { - code: string; - message: string; - }; - INVALID_OAUTH_RESPONSETYPE: { - code: string; - message: string; - }; - INVALID_SESSION_COOKIE_DURATION: { - code: string; - message: string; - }; - INVALID_TENANT_ID: { - code: string; - message: string; - }; - INVALID_TENANT_TYPE: { - code: string; - message: string; - }; - INVALID_TESTING_PHONE_NUMBER: { - code: string; - message: string; - }; - INVALID_UID: { - code: string; - message: string; - }; - INVALID_USER_IMPORT: { - code: string; - message: string; - }; - INVALID_TOKENS_VALID_AFTER_TIME: { - code: string; - message: string; - }; - MISMATCHING_TENANT_ID: { - code: string; - message: string; - }; - MISSING_ANDROID_PACKAGE_NAME: { - code: string; - message: string; - }; - MISSING_CONFIG: { - code: string; - message: string; - }; - MISSING_CONTINUE_URI: { - code: string; - message: string; - }; - MISSING_DISPLAY_NAME: { - code: string; - message: string; - }; - MISSING_EMAIL: { - code: string; - message: string; - }; - MISSING_IOS_BUNDLE_ID: { - code: string; - message: string; - }; - MISSING_ISSUER: { - code: string; - message: string; - }; - MISSING_HASH_ALGORITHM: { - code: string; - message: string; - }; - MISSING_OAUTH_CLIENT_ID: { - code: string; - message: string; - }; - MISSING_OAUTH_CLIENT_SECRET: { - code: string; - message: string; - }; - MISSING_PROVIDER_ID: { - code: string; - message: string; - }; - MISSING_SAML_RELYING_PARTY_CONFIG: { - code: string; - message: string; - }; - MAXIMUM_TEST_PHONE_NUMBER_EXCEEDED: { - code: string; - message: string; - }; - MAXIMUM_USER_COUNT_EXCEEDED: { - code: string; - message: string; - }; - MISSING_UID: { - code: string; - message: string; - }; - OPERATION_NOT_ALLOWED: { - code: string; - message: string; - }; - PHONE_NUMBER_ALREADY_EXISTS: { - code: string; - message: string; - }; - PROJECT_NOT_FOUND: { - code: string; - message: string; - }; - INSUFFICIENT_PERMISSION: { - code: string; - message: string; - }; - QUOTA_EXCEEDED: { - code: string; - message: string; - }; - SECOND_FACTOR_LIMIT_EXCEEDED: { - code: string; - message: string; - }; - SECOND_FACTOR_UID_ALREADY_EXISTS: { - code: string; - message: string; - }; - SESSION_COOKIE_EXPIRED: { - code: string; - message: string; - }; - SESSION_COOKIE_REVOKED: { - code: string; - message: string; - }; - TENANT_NOT_FOUND: { - code: string; - message: string; - }; - UID_ALREADY_EXISTS: { - code: string; - message: string; - }; - UNAUTHORIZED_DOMAIN: { - code: string; - message: string; - }; - UNSUPPORTED_FIRST_FACTOR: { - code: string; - message: string; - }; - UNSUPPORTED_SECOND_FACTOR: { - code: string; - message: string; - }; - UNSUPPORTED_TENANT_OPERATION: { - code: string; - message: string; - }; - UNVERIFIED_EMAIL: { - code: string; - message: string; - }; - USER_NOT_FOUND: { - code: string; - message: string; - }; - NOT_FOUND: { - code: string; - message: string; - }; - USER_DISABLED: { - code: string; - message: string; - }; - USER_NOT_DISABLED: { - code: string; - message: string; - }; - INVALID_RECAPTCHA_ACTION: { - code: string; - message: string; - }; - INVALID_RECAPTCHA_ENFORCEMENT_STATE: { - code: string; - message: string; - }; - RECAPTCHA_NOT_ENABLED: { - code: string; - message: string; - }; +export const AuthErrorCode: { + readonly AUTH_BLOCKING_TOKEN_EXPIRED: "auth-blocking-token-expired"; + readonly BILLING_NOT_ENABLED: "billing-not-enabled"; + readonly CLAIMS_TOO_LARGE: "claims-too-large"; + readonly CONFIGURATION_EXISTS: "configuration-exists"; + readonly CONFIGURATION_NOT_FOUND: "configuration-not-found"; + readonly ID_TOKEN_EXPIRED: "id-token-expired"; + readonly INVALID_ARGUMENT: "argument-error"; + readonly INVALID_CONFIG: "invalid-config"; + readonly EMAIL_ALREADY_EXISTS: "email-already-exists"; + readonly EMAIL_NOT_FOUND: "email-not-found"; + readonly FORBIDDEN_CLAIM: "reserved-claim"; + readonly INVALID_ID_TOKEN: "invalid-id-token"; + readonly ID_TOKEN_REVOKED: "id-token-revoked"; + readonly INTERNAL_ERROR: "internal-error"; + readonly INVALID_CLAIMS: "invalid-claims"; + readonly INVALID_CONTINUE_URI: "invalid-continue-uri"; + readonly INVALID_CREATION_TIME: "invalid-creation-time"; + readonly INVALID_CREDENTIAL: "invalid-credential"; + readonly INVALID_DISABLED_FIELD: "invalid-disabled-field"; + readonly INVALID_DISPLAY_NAME: "invalid-display-name"; + readonly INVALID_DYNAMIC_LINK_DOMAIN: "invalid-dynamic-link-domain"; + readonly INVALID_HOSTING_LINK_DOMAIN: "invalid-hosting-link-domain"; + readonly INVALID_EMAIL_VERIFIED: "invalid-email-verified"; + readonly INVALID_EMAIL: "invalid-email"; + readonly INVALID_NEW_EMAIL: "invalid-new-email"; + readonly INVALID_ENROLLED_FACTORS: "invalid-enrolled-factors"; + readonly INVALID_ENROLLMENT_TIME: "invalid-enrollment-time"; + readonly INVALID_HASH_ALGORITHM: "invalid-hash-algorithm"; + readonly INVALID_HASH_BLOCK_SIZE: "invalid-hash-block-size"; + readonly INVALID_HASH_DERIVED_KEY_LENGTH: "invalid-hash-derived-key-length"; + readonly INVALID_HASH_KEY: "invalid-hash-key"; + readonly INVALID_HASH_MEMORY_COST: "invalid-hash-memory-cost"; + readonly INVALID_HASH_PARALLELIZATION: "invalid-hash-parallelization"; + readonly INVALID_HASH_ROUNDS: "invalid-hash-rounds"; + readonly INVALID_HASH_SALT_SEPARATOR: "invalid-hash-salt-separator"; + readonly INVALID_LAST_SIGN_IN_TIME: "invalid-last-sign-in-time"; + readonly INVALID_NAME: "invalid-name"; + readonly INVALID_OAUTH_CLIENT_ID: "invalid-oauth-client-id"; + readonly INVALID_PAGE_TOKEN: "invalid-page-token"; + readonly INVALID_PASSWORD: "invalid-password"; + readonly INVALID_PASSWORD_HASH: "invalid-password-hash"; + readonly INVALID_PASSWORD_SALT: "invalid-password-salt"; + readonly INVALID_PHONE_NUMBER: "invalid-phone-number"; + readonly INVALID_PHOTO_URL: "invalid-photo-url"; + readonly INVALID_PROJECT_ID: "invalid-project-id"; + readonly INVALID_PROVIDER_DATA: "invalid-provider-data"; + readonly INVALID_PROVIDER_ID: "invalid-provider-id"; + readonly INVALID_PROVIDER_UID: "invalid-provider-uid"; + readonly INVALID_OAUTH_RESPONSETYPE: "invalid-oauth-responsetype"; + readonly INVALID_SESSION_COOKIE_DURATION: "invalid-session-cookie-duration"; + readonly INVALID_TENANT_ID: "invalid-tenant-id"; + readonly INVALID_TENANT_TYPE: "invalid-tenant-type"; + readonly INVALID_TESTING_PHONE_NUMBER: "invalid-testing-phone-number"; + readonly INVALID_UID: "invalid-uid"; + readonly INVALID_USER_IMPORT: "invalid-user-import"; + readonly INVALID_TOKENS_VALID_AFTER_TIME: "invalid-tokens-valid-after-time"; + readonly MISMATCHING_TENANT_ID: "mismatching-tenant-id"; + readonly MISSING_ANDROID_PACKAGE_NAME: "missing-android-package-name"; + readonly MISSING_CONFIG: "missing-config"; + readonly MISSING_CONTINUE_URI: "missing-continue-uri"; + readonly MISSING_DISPLAY_NAME: "missing-display-name"; + readonly MISSING_EMAIL: "missing-email"; + readonly MISSING_IOS_BUNDLE_ID: "missing-ios-bundle-id"; + readonly MISSING_ISSUER: "missing-issuer"; + readonly MISSING_HASH_ALGORITHM: "missing-hash-algorithm"; + readonly MISSING_OAUTH_CLIENT_ID: "missing-oauth-client-id"; + readonly MISSING_OAUTH_CLIENT_SECRET: "missing-oauth-client-secret"; + readonly MISSING_PROVIDER_ID: "missing-provider-id"; + readonly MISSING_SAML_RELYING_PARTY_CONFIG: "missing-saml-relying-party-config"; + readonly MAXIMUM_TEST_PHONE_NUMBER_EXCEEDED: "test-phone-number-limit-exceeded"; + readonly MAXIMUM_USER_COUNT_EXCEEDED: "maximum-user-count-exceeded"; + readonly MISSING_UID: "missing-uid"; + readonly OPERATION_NOT_ALLOWED: "operation-not-allowed"; + readonly PHONE_NUMBER_ALREADY_EXISTS: "phone-number-already-exists"; + readonly PROJECT_NOT_FOUND: "project-not-found"; + readonly INSUFFICIENT_PERMISSION: "insufficient-permission"; + readonly QUOTA_EXCEEDED: "quota-exceeded"; + readonly SECOND_FACTOR_LIMIT_EXCEEDED: "second-factor-limit-exceeded"; + readonly SECOND_FACTOR_UID_ALREADY_EXISTS: "second-factor-uid-already-exists"; + readonly SESSION_COOKIE_EXPIRED: "session-cookie-expired"; + readonly SESSION_COOKIE_REVOKED: "session-cookie-revoked"; + readonly TENANT_NOT_FOUND: "tenant-not-found"; + readonly UID_ALREADY_EXISTS: "uid-already-exists"; + readonly UNAUTHORIZED_DOMAIN: "unauthorized-continue-uri"; + readonly UNSUPPORTED_FIRST_FACTOR: "unsupported-first-factor"; + readonly UNSUPPORTED_SECOND_FACTOR: "unsupported-second-factor"; + readonly UNSUPPORTED_TENANT_OPERATION: "unsupported-tenant-operation"; + readonly UNVERIFIED_EMAIL: "unverified-email"; + readonly USER_NOT_FOUND: "user-not-found"; + readonly NOT_FOUND: "not-found"; + readonly USER_DISABLED: "user-disabled"; + readonly USER_NOT_DISABLED: "user-not-disabled"; + readonly INVALID_RECAPTCHA_ACTION: "invalid-recaptcha-action"; + readonly INVALID_RECAPTCHA_ENFORCEMENT_STATE: "invalid-recaptcha-enforcement-state"; + readonly RECAPTCHA_NOT_ENABLED: "recaptcha-not-enabled"; }; +// @public +export type AuthErrorCode = typeof AuthErrorCode[keyof typeof AuthErrorCode]; + // @public export type AuthFactorType = 'phone'; diff --git a/etc/firebase-admin.data-connect.api.md b/etc/firebase-admin.data-connect.api.md index 39d10a5cfd..c86afd866b 100644 --- a/etc/firebase-admin.data-connect.api.md +++ b/etc/firebase-admin.data-connect.api.md @@ -39,7 +39,20 @@ export class DataConnect { } // @public -export type DataConnectErrorCode = 'aborted' | 'invalid-argument' | 'invalid-credential' | 'internal-error' | 'permission-denied' | 'unauthenticated' | 'not-found' | 'unknown-error' | 'query-error'; +export const DataConnectErrorCode: { + readonly ABORTED: "aborted"; + readonly INVALID_ARGUMENT: "invalid-argument"; + readonly INVALID_CREDENTIAL: "invalid-credential"; + readonly INTERNAL: "internal-error"; + readonly PERMISSION_DENIED: "permission-denied"; + readonly UNAUTHENTICATED: "unauthenticated"; + readonly NOT_FOUND: "not-found"; + readonly UNKNOWN: "unknown-error"; + readonly QUERY_ERROR: "query-error"; +}; + +// @public +export type DataConnectErrorCode = typeof DataConnectErrorCode[keyof typeof DataConnectErrorCode]; // @public export interface ExecuteGraphqlResponse { diff --git a/etc/firebase-admin.database.api.md b/etc/firebase-admin.database.api.md index 1d28f4a2aa..1b3b1f86dc 100644 --- a/etc/firebase-admin.database.api.md +++ b/etc/firebase-admin.database.api.md @@ -28,10 +28,10 @@ export const enableLogging: typeof rtdb.enableLogging; export { EventType } -// Warning: (ae-forgotten-export) The symbol "FirebaseError" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "PrefixedFirebaseError" needs to be exported by the entry point index.d.ts // // @public -export class FirebaseDatabaseError extends FirebaseError { +export class FirebaseDatabaseError extends PrefixedFirebaseError { // Warning: (ae-forgotten-export) The symbol "ErrorInfo" needs to be exported by the entry point index.d.ts constructor(info: ErrorInfo, message?: string); } diff --git a/etc/firebase-admin.eventarc.api.md b/etc/firebase-admin.eventarc.api.md index e747c46116..8db2134e01 100644 --- a/etc/firebase-admin.eventarc.api.md +++ b/etc/firebase-admin.eventarc.api.md @@ -44,7 +44,13 @@ export class Eventarc { } // @public -export type EventarcErrorCode = 'unknown-error' | 'invalid-argument'; +export const EventarcErrorCode: { + readonly UNKNOWN_ERROR: "unknown-error"; + readonly INVALID_ARGUMENT: "invalid-argument"; +}; + +// @public +export type EventarcErrorCode = typeof EventarcErrorCode[keyof typeof EventarcErrorCode]; // Warning: (ae-forgotten-export) The symbol "PrefixedFirebaseError" needs to be exported by the entry point index.d.ts // diff --git a/etc/firebase-admin.extensions.api.md b/etc/firebase-admin.extensions.api.md index c8c1977fc3..3056b3c345 100644 --- a/etc/firebase-admin.extensions.api.md +++ b/etc/firebase-admin.extensions.api.md @@ -16,7 +16,16 @@ export class Extensions { } // @public -export type ExtensionsErrorCode = 'invalid-argument' | 'not-found' | 'forbidden' | 'internal-error' | 'unknown-error'; +export const ExtensionsErrorCode: { + readonly INVALID_ARGUMENT: "invalid-argument"; + readonly NOT_FOUND: "not-found"; + readonly FORBIDDEN: "forbidden"; + readonly INTERNAL_ERROR: "internal-error"; + readonly UNKNOWN_ERROR: "unknown-error"; +}; + +// @public +export type ExtensionsErrorCode = typeof ExtensionsErrorCode[keyof typeof ExtensionsErrorCode]; // Warning: (ae-forgotten-export) The symbol "PrefixedFirebaseError" needs to be exported by the entry point index.d.ts // diff --git a/etc/firebase-admin.firestore.api.md b/etc/firebase-admin.firestore.api.md index e7fa353032..983462539a 100644 --- a/etc/firebase-admin.firestore.api.md +++ b/etc/firebase-admin.firestore.api.md @@ -100,10 +100,10 @@ export { FieldValue } export { Filter } -// Warning: (ae-forgotten-export) The symbol "FirebaseError" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "PrefixedFirebaseError" needs to be exported by the entry point index.d.ts // // @public -export class FirebaseFirestoreError extends FirebaseError { +export class FirebaseFirestoreError extends PrefixedFirebaseError { // Warning: (ae-forgotten-export) The symbol "ErrorInfo" needs to be exported by the entry point index.d.ts constructor(info: ErrorInfo, message?: string); } diff --git a/etc/firebase-admin.functions.api.md b/etc/firebase-admin.functions.api.md index 6f70b37e2d..6620991ebc 100644 --- a/etc/firebase-admin.functions.api.md +++ b/etc/firebase-admin.functions.api.md @@ -41,7 +41,21 @@ export class Functions { } // @public -export type FunctionsErrorCode = 'aborted' | 'invalid-argument' | 'invalid-credential' | 'internal-error' | 'failed-precondition' | 'permission-denied' | 'unauthenticated' | 'not-found' | 'unknown-error' | 'task-already-exists'; +export const FunctionsErrorCode: { + readonly ABORTED: "aborted"; + readonly INVALID_ARGUMENT: "invalid-argument"; + readonly INVALID_CREDENTIAL: "invalid-credential"; + readonly INTERNAL_ERROR: "internal-error"; + readonly FAILED_PRECONDITION: "failed-precondition"; + readonly PERMISSION_DENIED: "permission-denied"; + readonly UNAUTHENTICATED: "unauthenticated"; + readonly NOT_FOUND: "not-found"; + readonly UNKNOWN_ERROR: "unknown-error"; + readonly TASK_ALREADY_EXISTS: "task-already-exists"; +}; + +// @public +export type FunctionsErrorCode = typeof FunctionsErrorCode[keyof typeof FunctionsErrorCode]; // @public export function getFunctions(app?: App): Functions; diff --git a/etc/firebase-admin.installations.api.md b/etc/firebase-admin.installations.api.md index 2dc62e75ec..a04a8893eb 100644 --- a/etc/firebase-admin.installations.api.md +++ b/etc/firebase-admin.installations.api.md @@ -6,10 +6,10 @@ import { Agent } from 'http'; -// Warning: (ae-forgotten-export) The symbol "FirebaseError" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "PrefixedFirebaseError" needs to be exported by the entry point index.d.ts // // @public -export class FirebaseInstallationsError extends FirebaseError { +export class FirebaseInstallationsError extends PrefixedFirebaseError { // Warning: (ae-forgotten-export) The symbol "ErrorInfo" needs to be exported by the entry point index.d.ts constructor(info: ErrorInfo, message?: string); } @@ -25,24 +25,15 @@ export class Installations { deleteInstallation(fid: string): Promise; } -// @public (undocumented) -export const InstallationsClientErrorCode: { - INVALID_ARGUMENT: { - code: string; - message: string; - }; - INVALID_PROJECT_ID: { - code: string; - message: string; - }; - INVALID_INSTALLATION_ID: { - code: string; - message: string; - }; - API_ERROR: { - code: string; - message: string; - }; +// @public +export const InstallationsErrorCode: { + readonly INVALID_ARGUMENT: "invalid-argument"; + readonly INVALID_PROJECT_ID: "invalid-project-id"; + readonly INVALID_INSTALLATION_ID: "invalid-installation-id"; + readonly API_ERROR: "api-error"; }; +// @public +export type InstallationsErrorCode = typeof InstallationsErrorCode[keyof typeof InstallationsErrorCode]; + ``` diff --git a/etc/firebase-admin.instance-id.api.md b/etc/firebase-admin.instance-id.api.md index 45de619267..52ce830004 100644 --- a/etc/firebase-admin.instance-id.api.md +++ b/etc/firebase-admin.instance-id.api.md @@ -6,10 +6,10 @@ import { Agent } from 'http'; -// Warning: (ae-forgotten-export) The symbol "FirebaseError" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "PrefixedFirebaseError" needs to be exported by the entry point index.d.ts // // @public -export class FirebaseInstanceIdError extends FirebaseError { +export class FirebaseInstanceIdError extends PrefixedFirebaseError { // Warning: (ae-forgotten-export) The symbol "ErrorInfo" needs to be exported by the entry point index.d.ts constructor(info: ErrorInfo, message?: string); } @@ -25,28 +25,16 @@ export class InstanceId { deleteInstanceId(instanceId: string): Promise; } -// @public (undocumented) -export const InstanceIdClientErrorCode: { - INVALID_INSTANCE_ID: { - code: string; - message: string; - }; - INVALID_ARGUMENT: { - code: string; - message: string; - }; - INVALID_PROJECT_ID: { - code: string; - message: string; - }; - INVALID_INSTALLATION_ID: { - code: string; - message: string; - }; - API_ERROR: { - code: string; - message: string; - }; +// @public +export const InstanceIdErrorCode: { + readonly INVALID_ARGUMENT: "invalid-argument"; + readonly INVALID_PROJECT_ID: "invalid-project-id"; + readonly INVALID_INSTALLATION_ID: "invalid-installation-id"; + readonly API_ERROR: "api-error"; + readonly INVALID_INSTANCE_ID: "invalid-instance-id"; }; +// @public +export type InstanceIdErrorCode = typeof InstanceIdErrorCode[keyof typeof InstanceIdErrorCode]; + ``` diff --git a/etc/firebase-admin.machine-learning.api.md b/etc/firebase-admin.machine-learning.api.md index c222e1d6a1..04a390cf9d 100644 --- a/etc/firebase-admin.machine-learning.api.md +++ b/etc/firebase-admin.machine-learning.api.md @@ -53,7 +53,28 @@ export class MachineLearning { } // @public -export type MachineLearningErrorCode = 'already-exists' | 'authentication-error' | 'internal-error' | 'invalid-argument' | 'invalid-server-response' | 'not-found' | 'resource-exhausted' | 'service-unavailable' | 'unknown-error' | 'cancelled' | 'deadline-exceeded' | 'permission-denied' | 'failed-precondition' | 'aborted' | 'out-of-range' | 'data-loss' | 'unauthenticated'; +export const MachineLearningErrorCode: { + readonly ALREADY_EXISTS: "already-exists"; + readonly AUTHENTICATION_ERROR: "authentication-error"; + readonly INTERNAL_ERROR: "internal-error"; + readonly INVALID_ARGUMENT: "invalid-argument"; + readonly INVALID_SERVER_RESPONSE: "invalid-server-response"; + readonly NOT_FOUND: "not-found"; + readonly RESOURCE_EXHAUSTED: "resource-exhausted"; + readonly SERVICE_UNAVAILABLE: "service-unavailable"; + readonly UNKNOWN_ERROR: "unknown-error"; + readonly CANCELLED: "cancelled"; + readonly DEADLINE_EXCEEDED: "deadline-exceeded"; + readonly PERMISSION_DENIED: "permission-denied"; + readonly FAILED_PRECONDITION: "failed-precondition"; + readonly ABORTED: "aborted"; + readonly OUT_OF_RANGE: "out-of-range"; + readonly DATA_LOSS: "data-loss"; + readonly UNAUTHENTICATED: "unauthenticated"; +}; + +// @public +export type MachineLearningErrorCode = typeof MachineLearningErrorCode[keyof typeof MachineLearningErrorCode]; // @public export class Model { diff --git a/etc/firebase-admin.messaging.api.md b/etc/firebase-admin.messaging.api.md index 1d81f2f84c..4c13ff1d27 100644 --- a/etc/firebase-admin.messaging.api.md +++ b/etc/firebase-admin.messaging.api.md @@ -204,85 +204,31 @@ export class Messaging { } // @public -export const MessagingClientErrorCode: { - INVALID_ARGUMENT: { - code: string; - message: string; - }; - INVALID_RECIPIENT: { - code: string; - message: string; - }; - INVALID_PAYLOAD: { - code: string; - message: string; - }; - INVALID_DATA_PAYLOAD_KEY: { - code: string; - message: string; - }; - PAYLOAD_SIZE_LIMIT_EXCEEDED: { - code: string; - message: string; - }; - INVALID_OPTIONS: { - code: string; - message: string; - }; - INVALID_REGISTRATION_TOKEN: { - code: string; - message: string; - }; - REGISTRATION_TOKEN_NOT_REGISTERED: { - code: string; - message: string; - }; - MISMATCHED_CREDENTIAL: { - code: string; - message: string; - }; - INVALID_PACKAGE_NAME: { - code: string; - message: string; - }; - DEVICE_MESSAGE_RATE_EXCEEDED: { - code: string; - message: string; - }; - TOPICS_MESSAGE_RATE_EXCEEDED: { - code: string; - message: string; - }; - MESSAGE_RATE_EXCEEDED: { - code: string; - message: string; - }; - THIRD_PARTY_AUTH_ERROR: { - code: string; - message: string; - }; - TOO_MANY_TOPICS: { - code: string; - message: string; - }; - AUTHENTICATION_ERROR: { - code: string; - message: string; - }; - SERVER_UNAVAILABLE: { - code: string; - message: string; - }; - INTERNAL_ERROR: { - code: string; - message: string; - }; - UNKNOWN_ERROR: { - code: string; - message: string; - }; +export const MessagingErrorCode: { + readonly INVALID_ARGUMENT: "invalid-argument"; + readonly INVALID_RECIPIENT: "invalid-recipient"; + readonly INVALID_PAYLOAD: "invalid-payload"; + readonly INVALID_DATA_PAYLOAD_KEY: "invalid-data-payload-key"; + readonly PAYLOAD_SIZE_LIMIT_EXCEEDED: "payload-size-limit-exceeded"; + readonly INVALID_OPTIONS: "invalid-options"; + readonly INVALID_REGISTRATION_TOKEN: "invalid-registration-token"; + readonly REGISTRATION_TOKEN_NOT_REGISTERED: "registration-token-not-registered"; + readonly MISMATCHED_CREDENTIAL: "mismatched-credential"; + readonly INVALID_PACKAGE_NAME: "invalid-package-name"; + readonly DEVICE_MESSAGE_RATE_EXCEEDED: "device-message-rate-exceeded"; + readonly TOPICS_MESSAGE_RATE_EXCEEDED: "topics-message-rate-exceeded"; + readonly MESSAGE_RATE_EXCEEDED: "message-rate-exceeded"; + readonly THIRD_PARTY_AUTH_ERROR: "third-party-auth-error"; + readonly TOO_MANY_TOPICS: "too-many-topics"; + readonly AUTHENTICATION_ERROR: "authentication-error"; + readonly SERVER_UNAVAILABLE: "server-unavailable"; + readonly INTERNAL_ERROR: "internal-error"; + readonly UNKNOWN_ERROR: "unknown-error"; }; +// @public +export type MessagingErrorCode = typeof MessagingErrorCode[keyof typeof MessagingErrorCode]; + // @public export interface MessagingOptions { // (undocumented) diff --git a/etc/firebase-admin.phone-number-verification.api.md b/etc/firebase-admin.phone-number-verification.api.md index a3256e649b..9195516d62 100644 --- a/etc/firebase-admin.phone-number-verification.api.md +++ b/etc/firebase-admin.phone-number-verification.api.md @@ -25,8 +25,15 @@ export class PhoneNumberVerification { verifyToken(jwt: string): Promise; } -// @public (undocumented) -export type PhoneNumberVerificationErrorCode = 'invalid-argument' | 'invalid-token' | 'expired-token'; +// @public +export const PhoneNumberVerificationErrorCode: { + readonly INVALID_ARGUMENT: "invalid-argument"; + readonly INVALID_TOKEN: "invalid-token"; + readonly EXPIRED_TOKEN: "expired-token"; +}; + +// @public +export type PhoneNumberVerificationErrorCode = typeof PhoneNumberVerificationErrorCode[keyof typeof PhoneNumberVerificationErrorCode]; // @public export interface PhoneNumberVerificationToken { diff --git a/etc/firebase-admin.project-management.api.md b/etc/firebase-admin.project-management.api.md index d48dfc706e..061aaa7c38 100644 --- a/etc/firebase-admin.project-management.api.md +++ b/etc/firebase-admin.project-management.api.md @@ -85,8 +85,21 @@ export class ProjectManagement { shaCertificate(shaHash: string): ShaCertificate; } -// @public (undocumented) -export type ProjectManagementErrorCode = 'already-exists' | 'authentication-error' | 'internal-error' | 'invalid-argument' | 'invalid-project-id' | 'invalid-server-response' | 'not-found' | 'service-unavailable' | 'unknown-error'; +// @public +export const ProjectManagementErrorCode: { + readonly ALREADY_EXISTS: "already-exists"; + readonly AUTHENTICATION_ERROR: "authentication-error"; + readonly INTERNAL_ERROR: "internal-error"; + readonly INVALID_ARGUMENT: "invalid-argument"; + readonly INVALID_PROJECT_ID: "invalid-project-id"; + readonly INVALID_SERVER_RESPONSE: "invalid-server-response"; + readonly NOT_FOUND: "not-found"; + readonly SERVICE_UNAVAILABLE: "service-unavailable"; + readonly UNKNOWN_ERROR: "unknown-error"; +}; + +// @public +export type ProjectManagementErrorCode = typeof ProjectManagementErrorCode[keyof typeof ProjectManagementErrorCode]; // @public export class ShaCertificate { diff --git a/etc/firebase-admin.remote-config.api.md b/etc/firebase-admin.remote-config.api.md index 14d4743ba6..858cbdf8d7 100644 --- a/etc/firebase-admin.remote-config.api.md +++ b/etc/firebase-admin.remote-config.api.md @@ -218,7 +218,22 @@ export interface RemoteConfigCondition { } // @public -export type RemoteConfigErrorCode = 'aborted' | 'already-exists' | 'failed-precondition' | 'internal-error' | 'invalid-argument' | 'not-found' | 'out-of-range' | 'permission-denied' | 'resource-exhausted' | 'unauthenticated' | 'unknown-error'; +export const RemoteConfigErrorCode: { + readonly ABORTED: "aborted"; + readonly ALREADY_EXISTS: "already-exists"; + readonly FAILED_PRECONDITION: "failed-precondition"; + readonly INTERNAL_ERROR: "internal-error"; + readonly INVALID_ARGUMENT: "invalid-argument"; + readonly NOT_FOUND: "not-found"; + readonly OUT_OF_RANGE: "out-of-range"; + readonly PERMISSION_DENIED: "permission-denied"; + readonly RESOURCE_EXHAUSTED: "resource-exhausted"; + readonly UNAUTHENTICATED: "unauthenticated"; + readonly UNKNOWN_ERROR: "unknown-error"; +}; + +// @public +export type RemoteConfigErrorCode = typeof RemoteConfigErrorCode[keyof typeof RemoteConfigErrorCode]; // @public export class RemoteConfigFetchResponse { diff --git a/etc/firebase-admin.security-rules.api.md b/etc/firebase-admin.security-rules.api.md index d78a6ad248..c9569fdde0 100644 --- a/etc/firebase-admin.security-rules.api.md +++ b/etc/firebase-admin.security-rules.api.md @@ -65,6 +65,19 @@ export class SecurityRules { } // @public -export type SecurityRulesErrorCode = 'already-exists' | 'authentication-error' | 'internal-error' | 'invalid-argument' | 'invalid-server-response' | 'not-found' | 'resource-exhausted' | 'service-unavailable' | 'unknown-error'; +export const SecurityRulesErrorCode: { + readonly ALREADY_EXISTS: "already-exists"; + readonly AUTHENTICATION_ERROR: "authentication-error"; + readonly INTERNAL_ERROR: "internal-error"; + readonly INVALID_ARGUMENT: "invalid-argument"; + readonly INVALID_SERVER_RESPONSE: "invalid-server-response"; + readonly NOT_FOUND: "not-found"; + readonly RESOURCE_EXHAUSTED: "resource-exhausted"; + readonly SERVICE_UNAVAILABLE: "service-unavailable"; + readonly UNKNOWN_ERROR: "unknown-error"; +}; + +// @public +export type SecurityRulesErrorCode = typeof SecurityRulesErrorCode[keyof typeof SecurityRulesErrorCode]; ``` diff --git a/src/app-check/app-check-api-client-internal.ts b/src/app-check/app-check-api-client-internal.ts index 246f65dc93..5dd2f17333 100644 --- a/src/app-check/app-check-api-client-internal.ts +++ b/src/app-check/app-check-api-client-internal.ts @@ -20,7 +20,8 @@ import { FirebaseApp } from '../app/firebase-app'; import { HttpRequestConfig, HttpClient, RequestResponseError, AuthorizedHttpClient, RequestResponse } from '../utils/api-request'; -import { PrefixedFirebaseError, ErrorInfo, toHttpResponse } from '../utils/error'; +import { PrefixedFirebaseError, toHttpResponse } from '../utils/error'; +import { FirebaseAppCheckError, AppCheckErrorCode, APP_CHECK_ERROR_CODE_MAPPING } from './error'; import * as utils from '../utils/index'; import * as validator from '../utils/validator'; import { AppCheckToken } from './app-check-api'; @@ -237,42 +238,3 @@ interface AppCheckApiError { message?: string; status?: string; } - -export const APP_CHECK_ERROR_CODE_MAPPING: { [key: string]: AppCheckErrorCode; } = { - ABORTED: 'aborted', - INVALID_ARGUMENT: 'invalid-argument', - INVALID_CREDENTIAL: 'invalid-credential', - INTERNAL: 'internal-error', - PERMISSION_DENIED: 'permission-denied', - UNAUTHENTICATED: 'unauthenticated', - NOT_FOUND: 'not-found', - UNKNOWN: 'unknown-error', -}; - -/** - * App Check client error codes and their default messages. - */ -export type AppCheckErrorCode = - 'aborted' - | 'invalid-argument' - | 'invalid-credential' - | 'internal-error' - | 'permission-denied' - | 'unauthenticated' - | 'not-found' - | 'app-check-token-expired' - | 'unknown-error'; - -/** - * Firebase App Check error type. This extends PrefixedFirebaseError. - */ -export class FirebaseAppCheckError extends PrefixedFirebaseError { - /** - * @param info - The error code info. - * @param message - The error message. If provided, this will override the default message. - */ - constructor(info: ErrorInfo, message?: string) { - super('app-check', info.code, message || info.message, info.httpResponse, info.cause); - - } -} diff --git a/src/app-check/app-check.ts b/src/app-check/app-check.ts index ed5aa57c73..99a2b010d3 100644 --- a/src/app-check/app-check.ts +++ b/src/app-check/app-check.ts @@ -18,7 +18,8 @@ import * as validator from '../utils/validator'; import { App } from '../app'; -import { AppCheckApiClient, FirebaseAppCheckError } from './app-check-api-client-internal'; +import { AppCheckApiClient } from './app-check-api-client-internal'; +import { FirebaseAppCheckError } from './error'; import { appCheckErrorFromCryptoSignerError, AppCheckTokenGenerator, } from './token-generator'; diff --git a/src/app-check/error.ts b/src/app-check/error.ts new file mode 100644 index 0000000000..5b25b29ba4 --- /dev/null +++ b/src/app-check/error.ts @@ -0,0 +1,62 @@ +/*! + * Copyright 2026 Google LLC + * + * 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 { PrefixedFirebaseError, ErrorInfo } from '../utils/error'; + +/** @const {Record} App Check server to client error code mapping. */ +export const APP_CHECK_ERROR_CODE_MAPPING: Record = { + ABORTED: 'aborted', + INVALID_ARGUMENT: 'invalid-argument', + INVALID_CREDENTIAL: 'invalid-credential', + INTERNAL: 'internal-error', + PERMISSION_DENIED: 'permission-denied', + UNAUTHENTICATED: 'unauthenticated', + NOT_FOUND: 'not-found', + UNKNOWN: 'unknown-error', +}; + +/** + * The constant mapping for valid App Check client error codes. + */ +export const AppCheckErrorCode = { + ABORTED: 'aborted', + INVALID_ARGUMENT: 'invalid-argument', + INVALID_CREDENTIAL: 'invalid-credential', + INTERNAL: 'internal-error', + PERMISSION_DENIED: 'permission-denied', + UNAUTHENTICATED: 'unauthenticated', + NOT_FOUND: 'not-found', + APP_CHECK_TOKEN_EXPIRED: 'app-check-token-expired', + UNKNOWN: 'unknown-error', +} as const; + +/** + * The type definition for valid App Check client error codes. + */ +export type AppCheckErrorCode = typeof AppCheckErrorCode[keyof typeof AppCheckErrorCode]; + +/** + * Firebase App Check error type. This extends PrefixedFirebaseError. + */ +export class FirebaseAppCheckError extends PrefixedFirebaseError { + /** + * @param info - The error code info. + * @param message - The error message. If provided, this will override the default message. + */ + constructor(info: ErrorInfo, message?: string) { + super('app-check', info.code, message || info.message, info.httpResponse, info.cause); + } +} diff --git a/src/app-check/index.ts b/src/app-check/index.ts index c505bebafd..4d9605f132 100644 --- a/src/app-check/index.ts +++ b/src/app-check/index.ts @@ -69,4 +69,4 @@ export function getAppCheck(app?: App): AppCheck { return firebaseApp.getOrInitService('appCheck', (app) => new AppCheck(app)); } -export { FirebaseAppCheckError, AppCheckErrorCode } from './app-check-api-client-internal'; \ No newline at end of file +export { FirebaseAppCheckError, AppCheckErrorCode } from './error'; \ No newline at end of file diff --git a/src/app-check/token-generator.ts b/src/app-check/token-generator.ts index b608dd459d..08bf9a7579 100644 --- a/src/app-check/token-generator.ts +++ b/src/app-check/token-generator.ts @@ -22,7 +22,7 @@ import { FirebaseAppCheckError, AppCheckErrorCode, APP_CHECK_ERROR_CODE_MAPPING, -} from './app-check-api-client-internal'; +} from './error'; import { AppCheckTokenOptions } from './app-check-api'; import { RequestResponseError } from '../utils/api-request'; import { toHttpResponse } from '../utils/error'; diff --git a/src/app-check/token-verifier.ts b/src/app-check/token-verifier.ts index 270ba154bf..e1c5d58504 100644 --- a/src/app-check/token-verifier.ts +++ b/src/app-check/token-verifier.ts @@ -16,7 +16,7 @@ import * as validator from '../utils/validator'; import * as util from '../utils/index'; -import { FirebaseAppCheckError } from './app-check-api-client-internal'; +import { FirebaseAppCheckError } from './error'; import { ALGORITHM_RS256, DecodedToken, decodeJwt, JwtError, JwtErrorCode, PublicKeySignatureVerifier, SignatureVerifier diff --git a/src/app/credential-internal.ts b/src/app/credential-internal.ts index a37b44ffaf..fe89fc3360 100644 --- a/src/app/credential-internal.ts +++ b/src/app/credential-internal.ts @@ -20,7 +20,7 @@ import fs = require('fs'); import { Credentials as GoogleAuthCredentials, GoogleAuth, Compute, AnyAuthClient } from 'google-auth-library' import { Agent } from 'http'; import { Credential, GoogleOAuthAccessToken } from './credential'; -import { AppErrorCodes, FirebaseAppError } from '../utils/error'; +import { AppErrorCode, FirebaseAppError } from './error'; import * as util from '../utils/validator'; const SCOPES = [ @@ -95,7 +95,7 @@ export class ApplicationDefaultCredential implements Credential { } else { throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_CREDENTIAL, + code: AppErrorCode.INVALID_CREDENTIAL, message: 'Credentials type should be Compute Engine Credentials.' }); } @@ -183,7 +183,7 @@ class ServiceAccount { } catch (error) { // Throw a nicely formed error message if the file contents cannot be parsed throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_CREDENTIAL, + code: AppErrorCode.INVALID_CREDENTIAL, message: `Failed to parse service account json file: ${(error as Error).message}`, cause: error, }); @@ -193,7 +193,7 @@ class ServiceAccount { constructor(json: object) { if (!util.isNonNullObject(json)) { throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_CREDENTIAL, + code: AppErrorCode.INVALID_CREDENTIAL, message: 'Service account must be an object.' }); } @@ -212,7 +212,7 @@ class ServiceAccount { } if (typeof errorMessage !== 'undefined') { - throw new FirebaseAppError({ code: AppErrorCodes.INVALID_CREDENTIAL, message: errorMessage }); + throw new FirebaseAppError({ code: AppErrorCode.INVALID_CREDENTIAL, message: errorMessage }); } // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -221,7 +221,7 @@ class ServiceAccount { forge.pki.privateKeyFromPem(this.privateKey); } catch (error) { throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_CREDENTIAL, + code: AppErrorCode.INVALID_CREDENTIAL, message: 'Failed to parse private key.', cause: error, }); @@ -296,7 +296,7 @@ class RefreshToken { } catch (error) { // Throw a nicely formed error message if the file contents cannot be parsed throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_CREDENTIAL, + code: AppErrorCode.INVALID_CREDENTIAL, message: 'Failed to parse refresh token file.', cause: error, }); @@ -324,7 +324,7 @@ class RefreshToken { } if (typeof errorMessage !== 'undefined') { - throw new FirebaseAppError({ code: AppErrorCodes.INVALID_CREDENTIAL, message: errorMessage }); + throw new FirebaseAppError({ code: AppErrorCode.INVALID_CREDENTIAL, message: errorMessage }); } } } @@ -394,7 +394,7 @@ class ImpersonatedServiceAccount { } catch (error) { // Throw a nicely formed error message if the file contents cannot be parsed throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_CREDENTIAL, + code: AppErrorCode.INVALID_CREDENTIAL, message: 'Failed to parse impersonated service account file.', cause: error, }); @@ -418,7 +418,7 @@ class ImpersonatedServiceAccount { } if (typeof errorMessage !== 'undefined') { - throw new FirebaseAppError({ code: AppErrorCodes.INVALID_CREDENTIAL, message: errorMessage }); + throw new FirebaseAppError({ code: AppErrorCode.INVALID_CREDENTIAL, message: errorMessage }); } } } @@ -474,7 +474,7 @@ function populateGoogleAuth(keyFile: string | object, httpAgent?: Agent) if (typeof keyFile === 'object') { if (!util.isNonNullObject(keyFile)) { throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_CREDENTIAL, + code: AppErrorCode.INVALID_CREDENTIAL, message: 'Service account must be an object.' }); } @@ -496,12 +496,12 @@ function populateCredential(credentials?: GoogleAuthCredentials): GoogleOAuthAcc if (typeof accessToken !== 'string') throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_CREDENTIAL, + code: AppErrorCode.INVALID_CREDENTIAL, message: 'Failed to parse Google auth credential: access_token must be a non empty string.' }); if (typeof expiryDate !== 'number') throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_CREDENTIAL, + code: AppErrorCode.INVALID_CREDENTIAL, message: 'Failed to parse Google auth credential: Invalid expiry_date.' }); diff --git a/src/app/error.ts b/src/app/error.ts new file mode 100644 index 0000000000..76122c397d --- /dev/null +++ b/src/app/error.ts @@ -0,0 +1,55 @@ +/*! + * Copyright 2026 Google LLC + * + * 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 { ErrorInfo, PrefixedFirebaseError } from '../utils/error'; + +/** + * Firebase App error code structure. This extends PrefixedFirebaseError. + */ +export class FirebaseAppError extends PrefixedFirebaseError { + /** + * @param info - The error code info. + * @param message - The error message. This will override the default message if provided. + */ + constructor(info: ErrorInfo, message?: string) { + // Override default message if custom message provided. + super('app', info.code, message || info.message, info.httpResponse, info.cause); + } +} + +/** + * The constant mapping for valid App client error codes. + */ +export const AppErrorCode = { + APP_DELETED: 'app-deleted', + DUPLICATE_APP: 'duplicate-app', + INVALID_ARGUMENT: 'invalid-argument', + INTERNAL_ERROR: 'internal-error', + INVALID_APP_NAME: 'invalid-app-name', + INVALID_APP_OPTIONS: 'invalid-app-options', + INVALID_CREDENTIAL: 'invalid-credential', + NETWORK_ERROR: 'network-error', + NETWORK_TIMEOUT: 'network-timeout', + NO_APP: 'no-app', + UNABLE_TO_PARSE_RESPONSE: 'unable-to-parse-response', +} as const; + +/** + * The type definition for valid App client error codes. + */ +export type AppErrorCode = typeof AppErrorCode[keyof typeof AppErrorCode]; + + diff --git a/src/app/firebase-app.ts b/src/app/firebase-app.ts index e89087bdbc..bc48772f9f 100644 --- a/src/app/firebase-app.ts +++ b/src/app/firebase-app.ts @@ -21,7 +21,7 @@ import { Credential } from './credential'; import { getApplicationDefault } from './credential-internal'; import * as validator from '../utils/validator'; import { deepCopy } from '../utils/deep-copy'; -import { AppErrorCodes, FirebaseAppError } from '../utils/error'; +import { AppErrorCode, FirebaseAppError } from './error'; const TOKEN_EXPIRY_THRESHOLD_MILLIS = 5 * 60 * 1000; @@ -70,7 +70,7 @@ export class FirebaseAppInternals { typeof result.expires_in !== 'number' || typeof result.access_token !== 'string') { throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_CREDENTIAL, + code: AppErrorCode.INVALID_CREDENTIAL, message: `Invalid access token generated: "${JSON.stringify(result)}". Valid access ` + 'tokens must be an object with the "expires_in" (number) and "access_token" ' + '(string) properties.', @@ -111,7 +111,7 @@ export class FirebaseAppInternals { } throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_CREDENTIAL, + code: AppErrorCode.INVALID_CREDENTIAL, message: errorMessage, cause: error as Error }); @@ -171,7 +171,7 @@ export class FirebaseApp implements App { if (!validator.isNonNullObject(this.options_)) { throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_APP_OPTIONS, + code: AppErrorCode.INVALID_APP_OPTIONS, message: 'Invalid Firebase app options passed as the first argument to initializeApp() for the ' + `app named "${this.name_}". Options must be a non-null object.` }); @@ -186,7 +186,7 @@ export class FirebaseApp implements App { const credential = this.options_.credential; if (typeof credential !== 'object' || credential === null || typeof credential.getAccessToken !== 'function') { throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_APP_OPTIONS, + code: AppErrorCode.INVALID_APP_OPTIONS, message: 'Invalid Firebase app options passed as the first argument to initializeApp() for the ' + `app named "${this.name_}". The "credential" property must be an object which implements ` + 'the Credential interface.' @@ -284,7 +284,7 @@ export class FirebaseApp implements App { private checkDestroyed_(): void { if (this.isDeleted_) { throw new FirebaseAppError({ - code: AppErrorCodes.APP_DELETED, + code: AppErrorCode.APP_DELETED, message: `Firebase app named "${this.name_}" has already been deleted.` }); } diff --git a/src/app/index.ts b/src/app/index.ts index 7c479cfee8..b491c0ad93 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -29,6 +29,7 @@ export { initializeApp, getApp, getApps, deleteApp } from './lifecycle'; export { Credential, ServiceAccount, GoogleOAuthAccessToken } from './credential'; export { applicationDefault, cert, refreshToken } from './credential-factory'; -export { FirebaseAppError, AppErrorCodes, FirebaseError, ErrorInfo, HttpResponse } from '../utils/error'; +export { FirebaseError, ErrorInfo, HttpResponse } from '../utils/error'; +export { FirebaseAppError, AppErrorCode } from './error'; export const SDK_VERSION = getSdkVersion(); diff --git a/src/app/lifecycle.ts b/src/app/lifecycle.ts index e660e84bb8..b2414ab3b6 100644 --- a/src/app/lifecycle.ts +++ b/src/app/lifecycle.ts @@ -18,7 +18,7 @@ import fs = require('fs'); import * as validator from '../utils/validator'; -import { AppErrorCodes, FirebaseAppError } from '../utils/error'; +import { AppErrorCode, FirebaseAppError } from './error'; import { App, AppOptions } from './core'; import { getApplicationDefault } from './credential-internal'; import { FirebaseApp } from './firebase-app'; @@ -52,7 +52,7 @@ export class AppStore { // Ensure the `autoInit` state matches the existing app's. If not, throw. if (currentApp.autoInit() !== autoInit) { throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_APP_OPTIONS, + code: AppErrorCode.INVALID_APP_OPTIONS, message: `A Firebase app named "${appName}" already exists with a different configuration.` }); } @@ -73,7 +73,7 @@ export class AppStore { delete currentAppOptions.credential; if (!fastDeepEqual(options, currentAppOptions)) { throw new FirebaseAppError({ - code: AppErrorCodes.DUPLICATE_APP, + code: AppErrorCode.DUPLICATE_APP, message: `A Firebase app named "${appName}" already exists with a different configuration.` }); @@ -89,7 +89,7 @@ export class AppStore { ? 'The default Firebase app does not exist. ' : `Firebase app named "${appName}" does not exist. `; errorMessage += 'Make sure you call initializeApp() before using any of the Firebase services.'; - throw new FirebaseAppError({ code: AppErrorCodes.NO_APP, message: errorMessage }); + throw new FirebaseAppError({ code: AppErrorCode.NO_APP, message: errorMessage }); } return this.appStore.get(appName)!; @@ -102,7 +102,7 @@ export class AppStore { public deleteApp(app: App): Promise { if (typeof app !== 'object' || app === null || !('options' in app)) { - throw new FirebaseAppError({ code: AppErrorCodes.INVALID_ARGUMENT, message: 'Invalid app argument.' }); + throw new FirebaseAppError({ code: AppErrorCode.INVALID_ARGUMENT, message: 'Invalid app argument.' }); } // Make sure the given app already exists. @@ -152,7 +152,7 @@ function validateAppOptionsSupportDeepEquals( // http.Agent checks. if (typeof requestedOptions.httpAgent !== 'undefined') { throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_APP_OPTIONS, + code: AppErrorCode.INVALID_APP_OPTIONS, message: `Firebase app named "${existingApp.name}" already exists and initializeApp was` + ' invoked with an optional http.Agent. The SDK cannot confirm the equality' + ' of http.Agent objects with the existing app. Please use getApp or getApps to reuse' + @@ -160,7 +160,7 @@ function validateAppOptionsSupportDeepEquals( }); } else if (typeof existingApp.options.httpAgent !== 'undefined') { throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_APP_OPTIONS, + code: AppErrorCode.INVALID_APP_OPTIONS, message: `An existing app named "${existingApp.name}" already exists with a different` + ' options configuration: httpAgent.' }); @@ -169,7 +169,7 @@ function validateAppOptionsSupportDeepEquals( // Credential checks. if (typeof requestedOptions.credential !== 'undefined') { throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_APP_OPTIONS, + code: AppErrorCode.INVALID_APP_OPTIONS, message: `Firebase app named "${existingApp.name}" already exists and initializeApp was` + ' invoked with an optional Credential. The SDK cannot confirm the equality' + ' of Credential objects with the existing app. Please use getApp or getApps' + @@ -179,7 +179,7 @@ function validateAppOptionsSupportDeepEquals( if (existingApp.customCredential()) { throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_APP_OPTIONS, + code: AppErrorCode.INVALID_APP_OPTIONS, message: `An existing app named "${existingApp.name}" already exists with a different` + ' options configuration: Credential.' }); @@ -199,7 +199,7 @@ function validateAppOptionsSupportDeepEquals( function validateAppNameFormat(appName: string): void { if (!validator.isNonEmptyString(appName)) { throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_APP_NAME, + code: AppErrorCode.INVALID_APP_NAME, message: `Invalid Firebase app name "${appName}" provided. App name must be a non-empty string.` }); } @@ -321,7 +321,7 @@ function loadOptionsFromEnvVar(): AppOptions { } catch (error) { // Throw a nicely formed error message if the file contents cannot be parsed throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_APP_OPTIONS, + code: AppErrorCode.INVALID_APP_OPTIONS, message: `Failed to parse app options file: ${(error as Error).message}`, cause: error as Error }); diff --git a/src/auth/action-code-settings-builder.ts b/src/auth/action-code-settings-builder.ts index 5ffd72d048..f10fda20c1 100644 --- a/src/auth/action-code-settings-builder.ts +++ b/src/auth/action-code-settings-builder.ts @@ -15,7 +15,7 @@ */ import * as validator from '../utils/validator'; -import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; +import { authClientErrorCode, FirebaseAuthError } from './error'; /** * This is the interface that defines the required continue/state URL with @@ -147,17 +147,17 @@ export class ActionCodeSettingsBuilder { constructor(actionCodeSettings: ActionCodeSettings) { if (!validator.isNonNullObject(actionCodeSettings)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"ActionCodeSettings" must be a non-null object.', ); } if (typeof actionCodeSettings.url === 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.MISSING_CONTINUE_URI, + authClientErrorCode.MISSING_CONTINUE_URI, ); } else if (!validator.isURL(actionCodeSettings.url)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONTINUE_URI, + authClientErrorCode.INVALID_CONTINUE_URI, ); } this.continueUrl = actionCodeSettings.url; @@ -165,7 +165,7 @@ export class ActionCodeSettingsBuilder { if (typeof actionCodeSettings.handleCodeInApp !== 'undefined' && !validator.isBoolean(actionCodeSettings.handleCodeInApp)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"ActionCodeSettings.handleCodeInApp" must be a boolean.', ); } @@ -174,7 +174,7 @@ export class ActionCodeSettingsBuilder { if (typeof actionCodeSettings.dynamicLinkDomain !== 'undefined' && !validator.isNonEmptyString(actionCodeSettings.dynamicLinkDomain)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_DYNAMIC_LINK_DOMAIN, + authClientErrorCode.INVALID_DYNAMIC_LINK_DOMAIN, ); } this.dynamicLinkDomain = actionCodeSettings.dynamicLinkDomain; @@ -182,7 +182,7 @@ export class ActionCodeSettingsBuilder { if (typeof actionCodeSettings.linkDomain !== 'undefined' && !validator.isNonEmptyString(actionCodeSettings.linkDomain)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HOSTING_LINK_DOMAIN, + authClientErrorCode.INVALID_HOSTING_LINK_DOMAIN, ); } this.linkDomain = actionCodeSettings.linkDomain; @@ -190,16 +190,16 @@ export class ActionCodeSettingsBuilder { if (typeof actionCodeSettings.iOS !== 'undefined') { if (!validator.isNonNullObject(actionCodeSettings.iOS)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"ActionCodeSettings.iOS" must be a valid non-null object.', ); } else if (typeof actionCodeSettings.iOS.bundleId === 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.MISSING_IOS_BUNDLE_ID, + authClientErrorCode.MISSING_IOS_BUNDLE_ID, ); } else if (!validator.isNonEmptyString(actionCodeSettings.iOS.bundleId)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"ActionCodeSettings.iOS.bundleId" must be a valid non-empty string.', ); } @@ -209,28 +209,28 @@ export class ActionCodeSettingsBuilder { if (typeof actionCodeSettings.android !== 'undefined') { if (!validator.isNonNullObject(actionCodeSettings.android)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"ActionCodeSettings.android" must be a valid non-null object.', ); } else if (typeof actionCodeSettings.android.packageName === 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.MISSING_ANDROID_PACKAGE_NAME, + authClientErrorCode.MISSING_ANDROID_PACKAGE_NAME, ); } else if (!validator.isNonEmptyString(actionCodeSettings.android.packageName)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"ActionCodeSettings.android.packageName" must be a valid non-empty string.', ); } else if (typeof actionCodeSettings.android.minimumVersion !== 'undefined' && !validator.isNonEmptyString(actionCodeSettings.android.minimumVersion)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"ActionCodeSettings.android.minimumVersion" must be a valid non-empty string.', ); } else if (typeof actionCodeSettings.android.installApp !== 'undefined' && !validator.isBoolean(actionCodeSettings.android.installApp)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"ActionCodeSettings.android.installApp" must be a valid boolean.', ); } diff --git a/src/auth/auth-api-request.ts b/src/auth/auth-api-request.ts index 5bf360a18a..04eb358a65 100644 --- a/src/auth/auth-api-request.ts +++ b/src/auth/auth-api-request.ts @@ -20,7 +20,8 @@ import * as validator from '../utils/validator'; import { App } from '../app/index'; import { FirebaseApp } from '../app/firebase-app'; import { deepCopy, deepExtend } from '../utils/deep-copy'; -import { AuthClientErrorCode, FirebaseAuthError, toHttpResponse } from '../utils/error'; +import { authClientErrorCode, FirebaseAuthError } from './error'; +import { toHttpResponse } from '../utils/error'; import { ApiSettings, AuthorizedHttpClient, HttpRequestConfig, RequestResponseError, RequestResponse, } from '../utils/api-request'; @@ -171,7 +172,7 @@ class AuthResourceUrlBuilder { .then((projectId) => { if (!validator.isNonEmptyString(projectId)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CREDENTIAL, + authClientErrorCode.INVALID_CREDENTIAL, 'Failed to determine project ID for Auth. Initialize the ' + 'SDK with service account credentials or set project ID as an app option. ' + 'Alternatively set the GOOGLE_CLOUD_PROJECT environment variable.', @@ -265,14 +266,14 @@ function validateAuthFactorInfo(request: AuthFactorInfo): void { if (typeof request.mfaEnrollmentId !== 'undefined' && !validator.isNonEmptyString(request.mfaEnrollmentId)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_UID, + authClientErrorCode.INVALID_UID, 'The second factor "uid" must be a valid non-empty string.', ); } if (typeof request.displayName !== 'undefined' && !validator.isString(request.displayName)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_DISPLAY_NAME, + authClientErrorCode.INVALID_DISPLAY_NAME, `The second factor "displayName" for "${authFactorInfoIdentifier}" must be a valid string.`, ); } @@ -280,7 +281,7 @@ function validateAuthFactorInfo(request: AuthFactorInfo): void { if (typeof request.enrolledAt !== 'undefined' && !validator.isISODateString(request.enrolledAt)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ENROLLMENT_TIME, + authClientErrorCode.INVALID_ENROLLMENT_TIME, `The second factor "enrollmentTime" for "${authFactorInfoIdentifier}" must be a valid ` + 'UTC date string.'); } @@ -289,7 +290,7 @@ function validateAuthFactorInfo(request: AuthFactorInfo): void { // phoneNumber should be a string and a valid phone number. if (!validator.isPhoneNumber(request.phoneInfo)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_PHONE_NUMBER, + authClientErrorCode.INVALID_PHONE_NUMBER, `The second factor "phoneNumber" for "${authFactorInfoIdentifier}" must be a non-empty ` + 'E.164 standard compliant identifier string.'); } @@ -297,7 +298,7 @@ function validateAuthFactorInfo(request: AuthFactorInfo): void { // Invalid second factor. For example, a phone second factor may have been provided without // a phone number. A TOTP based second factor may require a secret key, etc. throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ENROLLED_FACTORS, + authClientErrorCode.INVALID_ENROLLED_FACTORS, 'MFAInfo object provided is invalid.'); } } @@ -325,12 +326,12 @@ function validateProviderUserInfo(request: any): void { } } if (!validator.isNonEmptyString(request.providerId)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID); + throw new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID); } if (typeof request.displayName !== 'undefined' && typeof request.displayName !== 'string') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_DISPLAY_NAME, + authClientErrorCode.INVALID_DISPLAY_NAME, `The provider "displayName" for "${request.providerId}" must be a valid string.`, ); } @@ -338,14 +339,14 @@ function validateProviderUserInfo(request: any): void { // This is called localId on the backend but the developer specifies this as // uid externally. So the error message should use the client facing name. throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_UID, + authClientErrorCode.INVALID_UID, `The provider "uid" for "${request.providerId}" must be a valid non-empty string.`, ); } // email should be a string and a valid email. if (typeof request.email !== 'undefined' && !validator.isEmail(request.email)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_EMAIL, + authClientErrorCode.INVALID_EMAIL, `The provider "email" for "${request.providerId}" must be a valid email string.`, ); } @@ -355,7 +356,7 @@ function validateProviderUserInfo(request: any): void { // This is called photoUrl on the backend but the developer specifies this as // photoURL externally. So the error message should use the client facing name. throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_PHOTO_URL, + authClientErrorCode.INVALID_PHOTO_URL, `The provider "photoURL" for "${request.providerId}" must be a valid URL string.`, ); } @@ -410,79 +411,79 @@ function validateCreateEditRequest(request: any, writeOperationType: WriteOperat } if (typeof request.tenantId !== 'undefined' && !validator.isNonEmptyString(request.tenantId)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_TENANT_ID); + throw new FirebaseAuthError(authClientErrorCode.INVALID_TENANT_ID); } // For any invalid parameter, use the external key name in the error description. // displayName should be a string. if (typeof request.displayName !== 'undefined' && !validator.isString(request.displayName)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_DISPLAY_NAME); + throw new FirebaseAuthError(authClientErrorCode.INVALID_DISPLAY_NAME); } if ((typeof request.localId !== 'undefined' || uploadAccountRequest) && !validator.isUid(request.localId)) { // This is called localId on the backend but the developer specifies this as // uid externally. So the error message should use the client facing name. - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_UID); + throw new FirebaseAuthError(authClientErrorCode.INVALID_UID); } // email should be a string and a valid email. if (typeof request.email !== 'undefined' && !validator.isEmail(request.email)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_EMAIL); + throw new FirebaseAuthError(authClientErrorCode.INVALID_EMAIL); } // phoneNumber should be a string and a valid phone number. if (typeof request.phoneNumber !== 'undefined' && !validator.isPhoneNumber(request.phoneNumber)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PHONE_NUMBER); + throw new FirebaseAuthError(authClientErrorCode.INVALID_PHONE_NUMBER); } // password should be a string and a minimum of 6 chars. if (typeof request.password !== 'undefined' && !validator.isPassword(request.password)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PASSWORD); + throw new FirebaseAuthError(authClientErrorCode.INVALID_PASSWORD); } // rawPassword should be a string and a minimum of 6 chars. if (typeof request.rawPassword !== 'undefined' && !validator.isPassword(request.rawPassword)) { // This is called rawPassword on the backend but the developer specifies this as // password externally. So the error message should use the client facing name. - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PASSWORD); + throw new FirebaseAuthError(authClientErrorCode.INVALID_PASSWORD); } // emailVerified should be a boolean. if (typeof request.emailVerified !== 'undefined' && typeof request.emailVerified !== 'boolean') { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_EMAIL_VERIFIED); + throw new FirebaseAuthError(authClientErrorCode.INVALID_EMAIL_VERIFIED); } // photoUrl should be a URL. if (typeof request.photoUrl !== 'undefined' && !validator.isURL(request.photoUrl)) { // This is called photoUrl on the backend but the developer specifies this as // photoURL externally. So the error message should use the client facing name. - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PHOTO_URL); + throw new FirebaseAuthError(authClientErrorCode.INVALID_PHOTO_URL); } // disabled should be a boolean. if (typeof request.disabled !== 'undefined' && typeof request.disabled !== 'boolean') { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_DISABLED_FIELD); + throw new FirebaseAuthError(authClientErrorCode.INVALID_DISABLED_FIELD); } // validSince should be a number. if (typeof request.validSince !== 'undefined' && !validator.isNumber(request.validSince)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_TOKENS_VALID_AFTER_TIME); + throw new FirebaseAuthError(authClientErrorCode.INVALID_TOKENS_VALID_AFTER_TIME); } // createdAt should be a number. if (typeof request.createdAt !== 'undefined' && !validator.isNumber(request.createdAt)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_CREATION_TIME); + throw new FirebaseAuthError(authClientErrorCode.INVALID_CREATION_TIME); } // lastSignInAt should be a number. if (typeof request.lastLoginAt !== 'undefined' && !validator.isNumber(request.lastLoginAt)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_LAST_SIGN_IN_TIME); + throw new FirebaseAuthError(authClientErrorCode.INVALID_LAST_SIGN_IN_TIME); } // disableUser should be a boolean. if (typeof request.disableUser !== 'undefined' && typeof request.disableUser !== 'boolean') { // This is called disableUser on the backend but the developer specifies this as // disabled externally. So the error message should use the client facing name. - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_DISABLED_FIELD); + throw new FirebaseAuthError(authClientErrorCode.INVALID_DISABLED_FIELD); } // customAttributes should be stringified JSON with no blacklisted claims. // The payload should not exceed 1KB. @@ -494,7 +495,7 @@ function validateCreateEditRequest(request: any, writeOperationType: WriteOperat // JSON parsing error. This should never happen as we stringify the claims internally. // However, we still need to check since setAccountInfo via edit requests could pass // this field. - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_CLAIMS, error.message); + throw new FirebaseAuthError(authClientErrorCode.INVALID_CLAIMS, error.message); } const invalidClaims: string[] = []; // Check for any invalid claims. @@ -506,7 +507,7 @@ function validateCreateEditRequest(request: any, writeOperationType: WriteOperat // Throw an error if an invalid claim is detected. if (invalidClaims.length > 0) { throw new FirebaseAuthError( - AuthClientErrorCode.FORBIDDEN_CLAIM, + authClientErrorCode.FORBIDDEN_CLAIM, invalidClaims.length > 1 ? `Developer claims "${invalidClaims.join('", "')}" are reserved and cannot be specified.` : `Developer claim "${invalidClaims[0]}" is reserved and cannot be specified.`, @@ -515,7 +516,7 @@ function validateCreateEditRequest(request: any, writeOperationType: WriteOperat // Check claims payload does not exceed maxmimum size. if (request.customAttributes.length > MAX_CLAIMS_PAYLOAD_SIZE) { throw new FirebaseAuthError( - AuthClientErrorCode.CLAIMS_TOO_LARGE, + authClientErrorCode.CLAIMS_TOO_LARGE, `Developer claims payload should not exceed ${MAX_CLAIMS_PAYLOAD_SIZE} characters.`, ); } @@ -523,17 +524,17 @@ function validateCreateEditRequest(request: any, writeOperationType: WriteOperat // passwordHash has to be a base64 encoded string. if (typeof request.passwordHash !== 'undefined' && !validator.isString(request.passwordHash)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PASSWORD_HASH); + throw new FirebaseAuthError(authClientErrorCode.INVALID_PASSWORD_HASH); } // salt has to be a base64 encoded string. if (typeof request.salt !== 'undefined' && !validator.isString(request.salt)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PASSWORD_SALT); + throw new FirebaseAuthError(authClientErrorCode.INVALID_PASSWORD_SALT); } // providerUserInfo has to be an array of valid UserInfo requests. if (typeof request.providerUserInfo !== 'undefined' && !validator.isArray(request.providerUserInfo)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_DATA); + throw new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_DATA); } else if (validator.isArray(request.providerUserInfo)) { request.providerUserInfo.forEach((providerUserInfoEntry: any) => { validateProviderUserInfo(providerUserInfoEntry); @@ -556,7 +557,7 @@ function validateCreateEditRequest(request: any, writeOperationType: WriteOperat } if (enrollments) { if (!validator.isArray(enrollments)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_ENROLLED_FACTORS); + throw new FirebaseAuthError(authClientErrorCode.INVALID_ENROLLED_FACTORS); } enrollments.forEach((authFactorInfoEntry: AuthFactorInfo) => { validateAuthFactorInfo(authFactorInfoEntry); @@ -576,13 +577,13 @@ export const FIREBASE_AUTH_CREATE_SESSION_COOKIE = .setRequestValidator((request: any) => { // Validate the ID token is a non-empty string. if (!validator.isNonEmptyString(request.idToken)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_ID_TOKEN); + throw new FirebaseAuthError(authClientErrorCode.INVALID_ID_TOKEN); } // Validate the custom session cookie duration. if (!validator.isNumber(request.validDuration) || request.validDuration < MIN_SESSION_COOKIE_DURATION_SECS || request.validDuration > MAX_SESSION_COOKIE_DURATION_SECS) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_SESSION_COOKIE_DURATION); + throw new FirebaseAuthError(authClientErrorCode.INVALID_SESSION_COOKIE_DURATION); } }) // Set response validator. @@ -590,7 +591,7 @@ export const FIREBASE_AUTH_CREATE_SESSION_COOKIE = // Response should always contain the session cookie. if (!validator.isNonEmptyString(response.data?.sessionCookie)) { throw new FirebaseAuthError({ - ...AuthClientErrorCode.INTERNAL_ERROR, + ...authClientErrorCode.INTERNAL_ERROR, httpResponse: toHttpResponse(response), }); } @@ -616,14 +617,14 @@ export const FIREBASE_AUTH_DOWNLOAD_ACCOUNT = new ApiSettings('/accounts:batchGe // Validate next page token. if (typeof request.nextPageToken !== 'undefined' && !validator.isNonEmptyString(request.nextPageToken)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PAGE_TOKEN); + throw new FirebaseAuthError(authClientErrorCode.INVALID_PAGE_TOKEN); } // Validate max results. if (!validator.isNumber(request.maxResults) || request.maxResults <= 0 || request.maxResults > MAX_DOWNLOAD_ACCOUNT_PAGE_SIZE) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'Required "maxResults" must be a positive integer that does not exceed ' + `${MAX_DOWNLOAD_ACCOUNT_PAGE_SIZE}.`, ); @@ -650,7 +651,7 @@ export const FIREBASE_AUTH_GET_ACCOUNT_INFO = new ApiSettings('/accounts:lookup' .setRequestValidator((request: GetAccountInfoRequest) => { if (!request.localId && !request.email && !request.phoneNumber && !request.federatedUserId) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing user identifier'); } }) @@ -659,7 +660,7 @@ export const FIREBASE_AUTH_GET_ACCOUNT_INFO = new ApiSettings('/accounts:lookup' const data = response.data; if (!data.users || !data.users.length) { throw new FirebaseAuthError({ - ...AuthClientErrorCode.USER_NOT_FOUND, + ...authClientErrorCode.USER_NOT_FOUND, httpResponse: toHttpResponse(response), }); } @@ -676,7 +677,7 @@ export const FIREBASE_AUTH_GET_ACCOUNTS_INFO = new ApiSettings('/accounts:lookup .setRequestValidator((request: GetAccountInfoRequest) => { if (!request.localId && !request.email && !request.phoneNumber && !request.federatedUserId) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing user identifier'); } }); @@ -692,7 +693,7 @@ export const FIREBASE_AUTH_DELETE_ACCOUNT = new ApiSettings('/accounts:delete', .setRequestValidator((request: any) => { if (!request.localId) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing user identifier'); } }); @@ -719,12 +720,12 @@ export const FIREBASE_AUTH_BATCH_DELETE_ACCOUNTS = new ApiSettings('/accounts:ba .setRequestValidator((request: BatchDeleteAccountsRequest) => { if (!request.localIds) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing user identifiers'); } if (typeof request.force === 'undefined' || request.force !== true) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing force=true field'); } }) @@ -734,14 +735,14 @@ export const FIREBASE_AUTH_BATCH_DELETE_ACCOUNTS = new ApiSettings('/accounts:ba errors.forEach((batchDeleteErrorInfo: BatchDeleteErrorInfo) => { if (typeof batchDeleteErrorInfo.index === 'undefined') { throw new FirebaseAuthError({ - ...AuthClientErrorCode.INTERNAL_ERROR, + ...authClientErrorCode.INTERNAL_ERROR, message: 'INTERNAL ASSERT FAILED: Server BatchDeleteAccountResponse is missing an errors.index field', httpResponse: toHttpResponse(response), }); } if (!batchDeleteErrorInfo.localId) { throw new FirebaseAuthError({ - ...AuthClientErrorCode.INTERNAL_ERROR, + ...authClientErrorCode.INTERNAL_ERROR, message: 'INTERNAL ASSERT FAILED: Server BatchDeleteAccountResponse is missing an errors.localId field', httpResponse: toHttpResponse(response), }); @@ -761,13 +762,13 @@ export const FIREBASE_AUTH_SET_ACCOUNT_INFO = new ApiSettings('/accounts:update' // localId is a required parameter. if (typeof request.localId === 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing user identifier'); } // Throw error when tenantId is passed in POST body. if (typeof request.tenantId !== 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"tenantId" is an invalid "UpdateRequest" property.'); } validateCreateEditRequest(request, WriteOperationType.Update); @@ -778,7 +779,7 @@ export const FIREBASE_AUTH_SET_ACCOUNT_INFO = new ApiSettings('/accounts:update' // If the localId is not returned, then the request failed. if (!data?.localId) { throw new FirebaseAuthError({ - ...AuthClientErrorCode.USER_NOT_FOUND, + ...authClientErrorCode.USER_NOT_FOUND, httpResponse: toHttpResponse(response), }); } @@ -796,21 +797,21 @@ export const FIREBASE_AUTH_SIGN_UP_NEW_USER = new ApiSettings('/accounts', 'POST // signupNewUser does not support customAttributes. if (typeof request.customAttributes !== 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"customAttributes" cannot be set when creating a new user.', ); } // signupNewUser does not support validSince. if (typeof request.validSince !== 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"validSince" cannot be set when creating a new user.', ); } // Throw error when tenantId is passed in POST body. if (typeof request.tenantId !== 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"tenantId" is an invalid "CreateRequest" property.'); } validateCreateEditRequest(request, WriteOperationType.Create); @@ -821,7 +822,7 @@ export const FIREBASE_AUTH_SIGN_UP_NEW_USER = new ApiSettings('/accounts', 'POST // If the localId is not returned, then the request failed. if (!data?.localId) { throw new FirebaseAuthError({ - ...AuthClientErrorCode.INTERNAL_ERROR, + ...authClientErrorCode.INTERNAL_ERROR, message: 'INTERNAL ASSERT FAILED: Unable to create new user', httpResponse: toHttpResponse(response), }); @@ -833,17 +834,17 @@ const FIREBASE_AUTH_GET_OOB_CODE = new ApiSettings('/accounts:sendOobCode', 'POS .setRequestValidator((request: any) => { if (!validator.isEmail(request.email)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_EMAIL, + authClientErrorCode.INVALID_EMAIL, ); } if (typeof request.newEmail !== 'undefined' && !validator.isEmail(request.newEmail)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_NEW_EMAIL, + authClientErrorCode.INVALID_NEW_EMAIL, ); } if (EMAIL_ACTION_REQUEST_TYPES.indexOf(request.requestType) === -1) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, `"${request.requestType}" is not a supported email action request type.`, ); } @@ -854,7 +855,7 @@ const FIREBASE_AUTH_GET_OOB_CODE = new ApiSettings('/accounts:sendOobCode', 'POS // If the oobLink is not returned, then the request failed. if (!data?.oobLink) { throw new FirebaseAuthError({ - ...AuthClientErrorCode.INTERNAL_ERROR, + ...authClientErrorCode.INTERNAL_ERROR, message: 'INTERNAL ASSERT FAILED: Unable to create the email action link', httpResponse: toHttpResponse(response), }); @@ -873,7 +874,7 @@ const GET_OAUTH_IDP_CONFIG = new ApiSettings('/oauthIdpConfigs/{providerId}', 'G // Response should always contain the OIDC provider resource name. if (!validator.isNonEmptyString(data?.name)) { throw new FirebaseAuthError({ - ...AuthClientErrorCode.INTERNAL_ERROR, + ...authClientErrorCode.INTERNAL_ERROR, message: 'INTERNAL ASSERT FAILED: Unable to get OIDC configuration', httpResponse: toHttpResponse(response), }); @@ -899,7 +900,7 @@ const CREATE_OAUTH_IDP_CONFIG = new ApiSettings('/oauthIdpConfigs?oauthIdpConfig // Response should always contain the OIDC provider resource name. if (!validator.isNonEmptyString(data?.name)) { throw new FirebaseAuthError({ - ...AuthClientErrorCode.INTERNAL_ERROR, + ...authClientErrorCode.INTERNAL_ERROR, message: 'INTERNAL ASSERT FAILED: Unable to create new OIDC configuration', httpResponse: toHttpResponse(response), }); @@ -918,7 +919,7 @@ const UPDATE_OAUTH_IDP_CONFIG = new ApiSettings('/oauthIdpConfigs/{providerId}?u // Response should always contain the configuration resource name. if (!validator.isNonEmptyString(data?.name)) { throw new FirebaseAuthError({ - ...AuthClientErrorCode.INTERNAL_ERROR, + ...authClientErrorCode.INTERNAL_ERROR, message: 'INTERNAL ASSERT FAILED: Unable to update OIDC configuration', httpResponse: toHttpResponse(response), }); @@ -936,14 +937,14 @@ const LIST_OAUTH_IDP_CONFIGS = new ApiSettings('/oauthIdpConfigs', 'GET') // Validate next page token. if (typeof request.pageToken !== 'undefined' && !validator.isNonEmptyString(request.pageToken)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PAGE_TOKEN); + throw new FirebaseAuthError(authClientErrorCode.INVALID_PAGE_TOKEN); } // Validate max results. if (!validator.isNumber(request.pageSize) || request.pageSize <= 0 || request.pageSize > MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'Required "maxResults" must be a positive integer that does not exceed ' + `${MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE}.`, ); @@ -962,7 +963,7 @@ const GET_INBOUND_SAML_CONFIG = new ApiSettings('/inboundSamlConfigs/{providerId // Response should always contain the SAML provider resource name. if (!validator.isNonEmptyString(data?.name)) { throw new FirebaseAuthError({ - ...AuthClientErrorCode.INTERNAL_ERROR, + ...authClientErrorCode.INTERNAL_ERROR, message: 'INTERNAL ASSERT FAILED: Unable to get SAML configuration', httpResponse: toHttpResponse(response), }); @@ -988,7 +989,7 @@ const CREATE_INBOUND_SAML_CONFIG = new ApiSettings('/inboundSamlConfigs?inboundS // Response should always contain the SAML provider resource name. if (!validator.isNonEmptyString(data?.name)) { throw new FirebaseAuthError({ - ...AuthClientErrorCode.INTERNAL_ERROR, + ...authClientErrorCode.INTERNAL_ERROR, message: 'INTERNAL ASSERT FAILED: Unable to create new SAML configuration', httpResponse: toHttpResponse(response), }); @@ -1007,7 +1008,7 @@ const UPDATE_INBOUND_SAML_CONFIG = new ApiSettings('/inboundSamlConfigs/{provide // Response should always contain the configuration resource name. if (!validator.isNonEmptyString(data?.name)) { throw new FirebaseAuthError({ - ...AuthClientErrorCode.INTERNAL_ERROR, + ...authClientErrorCode.INTERNAL_ERROR, message: 'INTERNAL ASSERT FAILED: Unable to update SAML configuration', httpResponse: toHttpResponse(response), }); @@ -1025,14 +1026,14 @@ const LIST_INBOUND_SAML_CONFIGS = new ApiSettings('/inboundSamlConfigs', 'GET') // Validate next page token. if (typeof request.pageToken !== 'undefined' && !validator.isNonEmptyString(request.pageToken)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PAGE_TOKEN); + throw new FirebaseAuthError(authClientErrorCode.INVALID_PAGE_TOKEN); } // Validate max results. if (!validator.isNumber(request.pageSize) || request.pageSize <= 0 || request.pageSize > MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'Required "maxResults" must be a positive integer that does not exceed ' + `${MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE}.`, ); @@ -1060,7 +1061,7 @@ export abstract class AbstractAuthRequestHandler { private static addUidToRequest(id: UidIdentifier, request: GetAccountInfoRequest): GetAccountInfoRequest { if (!validator.isUid(id.uid)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_UID); + throw new FirebaseAuthError(authClientErrorCode.INVALID_UID); } request.localId ? request.localId.push(id.uid) : request.localId = [id.uid]; return request; @@ -1068,7 +1069,7 @@ export abstract class AbstractAuthRequestHandler { private static addEmailToRequest(id: EmailIdentifier, request: GetAccountInfoRequest): GetAccountInfoRequest { if (!validator.isEmail(id.email)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_EMAIL); + throw new FirebaseAuthError(authClientErrorCode.INVALID_EMAIL); } request.email ? request.email.push(id.email) : request.email = [id.email]; return request; @@ -1076,7 +1077,7 @@ export abstract class AbstractAuthRequestHandler { private static addPhoneToRequest(id: PhoneIdentifier, request: GetAccountInfoRequest): GetAccountInfoRequest { if (!validator.isPhoneNumber(id.phoneNumber)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PHONE_NUMBER); + throw new FirebaseAuthError(authClientErrorCode.INVALID_PHONE_NUMBER); } request.phoneNumber ? request.phoneNumber.push(id.phoneNumber) : request.phoneNumber = [id.phoneNumber]; return request; @@ -1084,10 +1085,10 @@ export abstract class AbstractAuthRequestHandler { private static addProviderToRequest(id: ProviderIdentifier, request: GetAccountInfoRequest): GetAccountInfoRequest { if (!validator.isNonEmptyString(id.providerId)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID); + throw new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID); } if (!validator.isNonEmptyString(id.providerUid)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_UID); + throw new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_UID); } const federatedUserId = { providerId: id.providerId, @@ -1106,7 +1107,7 @@ export abstract class AbstractAuthRequestHandler { constructor(protected readonly app: App) { if (typeof app !== 'object' || app === null || !('options' in app)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'First argument passed to admin.auth() must be a valid Firebase app instance.', ); } @@ -1142,7 +1143,7 @@ export abstract class AbstractAuthRequestHandler { */ public getAccountInfoByUid(uid: string): Promise { if (!validator.isUid(uid)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_UID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_UID)); } const request = { @@ -1159,7 +1160,7 @@ export abstract class AbstractAuthRequestHandler { */ public getAccountInfoByEmail(email: string): Promise { if (!validator.isEmail(email)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_EMAIL)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_EMAIL)); } const request = { @@ -1176,7 +1177,7 @@ export abstract class AbstractAuthRequestHandler { */ public getAccountInfoByPhoneNumber(phoneNumber: string): Promise { if (!validator.isPhoneNumber(phoneNumber)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PHONE_NUMBER)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_PHONE_NUMBER)); } const request = { @@ -1187,7 +1188,7 @@ export abstract class AbstractAuthRequestHandler { public getAccountInfoByFederatedUid(providerId: string, rawId: string): Promise { if (!validator.isNonEmptyString(providerId) || !validator.isNonEmptyString(rawId)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID); + throw new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID); } const request = { @@ -1213,7 +1214,7 @@ export abstract class AbstractAuthRequestHandler { return Promise.resolve({ users: [] }); } else if (identifiers.length > MAX_GET_ACCOUNTS_BATCH_SIZE) { throw new FirebaseAuthError( - AuthClientErrorCode.MAXIMUM_USER_COUNT_EXCEEDED, + authClientErrorCode.MAXIMUM_USER_COUNT_EXCEEDED, '`identifiers` parameter must have <= ' + MAX_GET_ACCOUNTS_BATCH_SIZE + ' entries.'); } @@ -1230,7 +1231,7 @@ export abstract class AbstractAuthRequestHandler { request = AbstractAuthRequestHandler.addProviderToRequest(id, request); } else { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'Unrecognized identifier: ' + id); } } @@ -1300,7 +1301,7 @@ export abstract class AbstractAuthRequestHandler { // Fail quickly if more users than allowed are to be imported. if (validator.isArray(users) && users.length > MAX_UPLOAD_ACCOUNT_BATCH_SIZE) { throw new FirebaseAuthError( - AuthClientErrorCode.MAXIMUM_USER_COUNT_EXCEEDED, + authClientErrorCode.MAXIMUM_USER_COUNT_EXCEEDED, `A maximum of ${MAX_UPLOAD_ACCOUNT_BATCH_SIZE} users can be imported at once.`, ); } @@ -1326,7 +1327,7 @@ export abstract class AbstractAuthRequestHandler { */ public deleteAccount(uid: string): Promise { if (!validator.isUid(uid)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_UID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_UID)); } const request = { @@ -1340,7 +1341,7 @@ export abstract class AbstractAuthRequestHandler { return Promise.resolve({}); } else if (uids.length > MAX_DELETE_ACCOUNTS_BATCH_SIZE) { throw new FirebaseAuthError( - AuthClientErrorCode.MAXIMUM_USER_COUNT_EXCEEDED, + authClientErrorCode.MAXIMUM_USER_COUNT_EXCEEDED, '`uids` parameter must have <= ' + MAX_DELETE_ACCOUNTS_BATCH_SIZE + ' entries.'); } @@ -1351,7 +1352,7 @@ export abstract class AbstractAuthRequestHandler { uids.forEach((uid) => { if (!validator.isUid(uid)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_UID); + throw new FirebaseAuthError(authClientErrorCode.INVALID_UID); } request.localIds!.push(uid); }); @@ -1370,11 +1371,11 @@ export abstract class AbstractAuthRequestHandler { public setCustomUserClaims(uid: string, customUserClaims: object | null): Promise { // Validate user UID. if (!validator.isUid(uid)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_UID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_UID)); } else if (!validator.isObject(customUserClaims)) { return Promise.reject( new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'CustomUserClaims argument must be an object or null.', ), ); @@ -1404,11 +1405,11 @@ export abstract class AbstractAuthRequestHandler { */ public updateExistingAccount(uid: string, properties: UpdateRequest): Promise { if (!validator.isUid(uid)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_UID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_UID)); } else if (!validator.isNonNullObject(properties)) { return Promise.reject( new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'Properties argument must be a non-null object.', ), ); @@ -1417,25 +1418,25 @@ export abstract class AbstractAuthRequestHandler { // validateProviderUserInfo. It may be possible to refactor a bit. if (!validator.isNonEmptyString(properties.providerToLink.providerId)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'providerToLink.providerId of properties argument must be a non-empty string.'); } if (!validator.isNonEmptyString(properties.providerToLink.uid)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'providerToLink.uid of properties argument must be a non-empty string.'); } } else if (typeof properties.providersToUnlink !== 'undefined') { if (!validator.isArray(properties.providersToUnlink)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'providersToUnlink of properties argument must be an array of strings.'); } properties.providersToUnlink.forEach((providerId) => { if (!validator.isNonEmptyString(providerId)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'providersToUnlink of properties argument must be an array of strings.'); } }); @@ -1548,7 +1549,7 @@ export abstract class AbstractAuthRequestHandler { public revokeRefreshTokens(uid: string): Promise { // Validate user UID. if (!validator.isUid(uid)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_UID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_UID)); } const request: any = { localId: uid, @@ -1572,7 +1573,7 @@ export abstract class AbstractAuthRequestHandler { if (!validator.isNonNullObject(properties)) { return Promise.reject( new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'Properties argument must be a non-null object.', ), ); @@ -1605,11 +1606,11 @@ export abstract class AbstractAuthRequestHandler { // They will automatically be provisioned server side. if ('enrollmentTime' in multiFactorInfo) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"enrollmentTime" is not supported when adding second factors via "createUser()"'); } else if ('uid' in multiFactorInfo) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"uid" is not supported when adding second factors via "createUser()"'); } mfaInfo.push(convertMultiFactorInfoToServerFormat(multiFactorInfo)); @@ -1656,7 +1657,7 @@ export abstract class AbstractAuthRequestHandler { if (typeof actionCodeSettings === 'undefined' && requestType === 'EMAIL_SIGNIN') { return Promise.reject( new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, "`actionCodeSettings` is required when `requestType` === 'EMAIL_SIGNIN'", ), ); @@ -1672,7 +1673,7 @@ export abstract class AbstractAuthRequestHandler { if (requestType === 'VERIFY_AND_CHANGE_EMAIL' && typeof newEmail === 'undefined') { return Promise.reject( new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, "`newEmail` is required when `requestType` === 'VERIFY_AND_CHANGE_EMAIL'", ), ); @@ -1692,7 +1693,7 @@ export abstract class AbstractAuthRequestHandler { */ public getOAuthIdpConfig(providerId: string): Promise { if (!OIDCConfig.isProviderId(providerId)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID)); } return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), GET_OAUTH_IDP_CONFIG, {}, { providerId }); } @@ -1738,7 +1739,7 @@ export abstract class AbstractAuthRequestHandler { */ public deleteOAuthIdpConfig(providerId: string): Promise { if (!OIDCConfig.isProviderId(providerId)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID)); } return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), DELETE_OAUTH_IDP_CONFIG, {}, { providerId }) .then(() => { @@ -1767,7 +1768,7 @@ export abstract class AbstractAuthRequestHandler { .then((response: any) => { if (!OIDCConfig.getProviderIdFromResourceName(response.name)) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new OIDC provider configuration'); } return response as OIDCConfigServerResponse; @@ -1785,7 +1786,7 @@ export abstract class AbstractAuthRequestHandler { public updateOAuthIdpConfig( providerId: string, options: OIDCUpdateAuthProviderRequest): Promise { if (!OIDCConfig.isProviderId(providerId)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID)); } // Construct backend request. let request: OIDCConfigServerRequest; @@ -1800,7 +1801,7 @@ export abstract class AbstractAuthRequestHandler { .then((response: any) => { if (!OIDCConfig.getProviderIdFromResourceName(response.name)) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to update OIDC provider configuration'); } return response as OIDCConfigServerResponse; @@ -1815,7 +1816,7 @@ export abstract class AbstractAuthRequestHandler { */ public getInboundSamlConfig(providerId: string): Promise { if (!SAMLConfig.isProviderId(providerId)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID)); } return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), GET_INBOUND_SAML_CONFIG, {}, { providerId }); } @@ -1861,7 +1862,7 @@ export abstract class AbstractAuthRequestHandler { */ public deleteInboundSamlConfig(providerId: string): Promise { if (!SAMLConfig.isProviderId(providerId)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID)); } return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), DELETE_INBOUND_SAML_CONFIG, {}, { providerId }) .then(() => { @@ -1890,7 +1891,7 @@ export abstract class AbstractAuthRequestHandler { .then((response: any) => { if (!SAMLConfig.getProviderIdFromResourceName(response.name)) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new SAML provider configuration'); } return response as SAMLConfigServerResponse; @@ -1908,7 +1909,7 @@ export abstract class AbstractAuthRequestHandler { public updateInboundSamlConfig( providerId: string, options: SAMLUpdateAuthProviderRequest): Promise { if (!SAMLConfig.isProviderId(providerId)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID)); } // Construct backend request. let request: SAMLConfigServerRequest; @@ -1923,7 +1924,7 @@ export abstract class AbstractAuthRequestHandler { .then((response: any) => { if (!SAMLConfig.getProviderIdFromResourceName(response.name)) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to update SAML provider configuration'); } return response as SAMLConfigServerResponse; @@ -1974,7 +1975,7 @@ export abstract class AbstractAuthRequestHandler { if (!errorCode) { // Fallback for unexpected server error responses without a parseable error code. throw new FirebaseAuthError({ - ...AuthClientErrorCode.INTERNAL_ERROR, + ...authClientErrorCode.INTERNAL_ERROR, message: 'An internal error occurred while attempting to extract the errorcode from the error.', cause: err, httpResponse: toHttpResponse(err.response), @@ -2024,7 +2025,7 @@ const GET_PROJECT_CONFIG = new ApiSettings('/config', 'GET') // Response should always contain at least the config name. if (!validator.isNonEmptyString(data?.name)) { throw new FirebaseAuthError({ - ...AuthClientErrorCode.INTERNAL_ERROR, + ...authClientErrorCode.INTERNAL_ERROR, message: 'INTERNAL ASSERT FAILED: Unable to get project config', httpResponse: toHttpResponse(response), }); @@ -2038,7 +2039,7 @@ const UPDATE_PROJECT_CONFIG = new ApiSettings('/config?updateMask={updateMask}', // Response should always contain at least the config name. if (!validator.isNonEmptyString(data?.name)) { throw new FirebaseAuthError({ - ...AuthClientErrorCode.INTERNAL_ERROR, + ...authClientErrorCode.INTERNAL_ERROR, message: 'INTERNAL ASSERT FAILED: Unable to update project config', httpResponse: toHttpResponse(response), }); @@ -2053,7 +2054,7 @@ const GET_TENANT = new ApiSettings('/tenants/{tenantId}', 'GET') // Response should always contain at least the tenant name. if (!validator.isNonEmptyString(data?.name)) { throw new FirebaseAuthError({ - ...AuthClientErrorCode.INTERNAL_ERROR, + ...authClientErrorCode.INTERNAL_ERROR, message: 'INTERNAL ASSERT FAILED: Unable to get tenant', httpResponse: toHttpResponse(response), }); @@ -2072,7 +2073,7 @@ const UPDATE_TENANT = new ApiSettings('/tenants/{tenantId}?updateMask={updateMas if (!validator.isNonEmptyString(data?.name) || !Tenant.getTenantIdFromResourceName(data?.name)) { throw new FirebaseAuthError({ - ...AuthClientErrorCode.INTERNAL_ERROR, + ...authClientErrorCode.INTERNAL_ERROR, message: 'INTERNAL ASSERT FAILED: Unable to update tenant', httpResponse: toHttpResponse(response), }); @@ -2086,14 +2087,14 @@ const LIST_TENANTS = new ApiSettings('/tenants', 'GET') // Validate next page token. if (typeof request.pageToken !== 'undefined' && !validator.isNonEmptyString(request.pageToken)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PAGE_TOKEN); + throw new FirebaseAuthError(authClientErrorCode.INVALID_PAGE_TOKEN); } // Validate max results. if (!validator.isNumber(request.pageSize) || request.pageSize <= 0 || request.pageSize > MAX_LIST_TENANT_PAGE_SIZE) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'Required "maxResults" must be a positive non-zero number that does not exceed ' + `the allowed ${MAX_LIST_TENANT_PAGE_SIZE}.`, ); @@ -2109,7 +2110,7 @@ const CREATE_TENANT = new ApiSettings('/tenants', 'POST') if (!validator.isNonEmptyString(data?.name) || !Tenant.getTenantIdFromResourceName(data?.name)) { throw new FirebaseAuthError({ - ...AuthClientErrorCode.INTERNAL_ERROR, + ...authClientErrorCode.INTERNAL_ERROR, message: 'INTERNAL ASSERT FAILED: Unable to create tenant', httpResponse: toHttpResponse(response), }); @@ -2188,7 +2189,7 @@ export class AuthRequestHandler extends AbstractAuthRequestHandler { */ public getTenant(tenantId: string): Promise { if (!validator.isNonEmptyString(tenantId)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_TENANT_ID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_TENANT_ID)); } return this.invokeRequestHandler(this.authResourceUrlBuilder, GET_TENANT, {}, { tenantId }) .then((response: any) => { @@ -2238,7 +2239,7 @@ export class AuthRequestHandler extends AbstractAuthRequestHandler { */ public deleteTenant(tenantId: string): Promise { if (!validator.isNonEmptyString(tenantId)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_TENANT_ID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_TENANT_ID)); } return this.invokeRequestHandler(this.authResourceUrlBuilder, DELETE_TENANT, undefined, { tenantId }) .then(() => { @@ -2274,7 +2275,7 @@ export class AuthRequestHandler extends AbstractAuthRequestHandler { */ public updateTenant(tenantId: string, tenantOptions: UpdateTenantRequest): Promise { if (!validator.isNonEmptyString(tenantId)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_TENANT_ID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_TENANT_ID)); } try { // Construct backend request. @@ -2348,7 +2349,7 @@ export class TenantAwareAuthRequestHandler extends AbstractAuthRequestHandler { if (validator.isNonEmptyString(user.tenantId) && user.tenantId !== this.tenantId) { throw new FirebaseAuthError( - AuthClientErrorCode.MISMATCHING_TENANT_ID, + authClientErrorCode.MISMATCHING_TENANT_ID, `UserRecord of index "${index}" has mismatching tenant ID "${user.tenantId}"`); } }); diff --git a/src/auth/auth-config.ts b/src/auth/auth-config.ts index 1dd5565a88..3310a8441e 100644 --- a/src/auth/auth-config.ts +++ b/src/auth/auth-config.ts @@ -16,7 +16,7 @@ import * as validator from '../utils/validator'; import { deepCopy } from '../utils/deep-copy'; -import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; +import { authClientErrorCode, FirebaseAuthError } from './error'; /** * Interface representing base properties of a user-enrolled second factor for a @@ -605,7 +605,7 @@ export class MultiFactorAuthConfig implements MultiFactorConfig { }; if (!validator.isNonNullObject(options)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"MultiFactorConfig" must be a non-null object.', ); } @@ -613,7 +613,7 @@ export class MultiFactorAuthConfig implements MultiFactorConfig { for (const key in options) { if (!(key in validKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${key}" is not a valid MultiFactorConfig parameter.`, ); } @@ -623,7 +623,7 @@ export class MultiFactorAuthConfig implements MultiFactorConfig { options.state !== 'ENABLED' && options.state !== 'DISABLED') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"MultiFactorConfig.state" must be either "ENABLED" or "DISABLED".', ); } @@ -631,7 +631,7 @@ export class MultiFactorAuthConfig implements MultiFactorConfig { if (typeof options.factorIds !== 'undefined') { if (!validator.isArray(options.factorIds)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"MultiFactorConfig.factorIds" must be an array of valid "AuthFactorTypes".', ); } @@ -640,7 +640,7 @@ export class MultiFactorAuthConfig implements MultiFactorConfig { options.factorIds.forEach((factorId) => { if (typeof AUTH_FACTOR_CLIENT_TO_SERVER_TYPE[factorId] === 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${factorId}" is not a valid "AuthFactorType".`, ); } @@ -650,7 +650,7 @@ export class MultiFactorAuthConfig implements MultiFactorConfig { if (typeof options.providerConfigs !== 'undefined') { if (!validator.isArray(options.providerConfigs)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"MultiFactorConfig.providerConfigs" must be an array of valid "MultiFactorProviderConfig."', ); } @@ -658,7 +658,7 @@ export class MultiFactorAuthConfig implements MultiFactorConfig { options.providerConfigs.forEach((multiFactorProviderConfig) => { if (typeof multiFactorProviderConfig === 'undefined' || !validator.isObject(multiFactorProviderConfig)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${multiFactorProviderConfig}" is not a valid "MultiFactorProviderConfig" type.` ) } @@ -669,7 +669,7 @@ export class MultiFactorAuthConfig implements MultiFactorConfig { for (const key in multiFactorProviderConfig) { if (!(key in validProviderConfigKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${key}" is not a valid ProviderConfig parameter.`, ); } @@ -678,14 +678,14 @@ export class MultiFactorAuthConfig implements MultiFactorConfig { (multiFactorProviderConfig.state !== 'ENABLED' && multiFactorProviderConfig.state !== 'DISABLED')) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"MultiFactorConfig.providerConfigs.state" must be either "ENABLED" or "DISABLED".', ) } // Since TOTP is the only provider config available right now, not defining it will lead into an error if (typeof multiFactorProviderConfig.totpProviderConfig === 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"MultiFactorConfig.providerConfigs.totpProviderConfig" must be defined.' ) } @@ -695,7 +695,7 @@ export class MultiFactorAuthConfig implements MultiFactorConfig { for (const key in multiFactorProviderConfig.totpProviderConfig) { if (!(key in validTotpProviderConfigKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${key}" is not a valid TotpProviderConfig parameter.`, ); } @@ -704,7 +704,7 @@ export class MultiFactorAuthConfig implements MultiFactorConfig { if (typeof adjIntervals !== 'undefined' && (!Number.isInteger(adjIntervals) || adjIntervals < 0 || adjIntervals > 10)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"MultiFactorConfig.providerConfigs.totpProviderConfig.adjacentIntervals" must' + ' be a valid number between 0 and 10 (both inclusive).' ) @@ -724,7 +724,7 @@ export class MultiFactorAuthConfig implements MultiFactorConfig { constructor(response: MultiFactorAuthServerConfig) { if (typeof response.state === 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Invalid multi-factor configuration response'); } this.state = response.state; @@ -744,7 +744,7 @@ export class MultiFactorAuthConfig implements MultiFactorConfig { (typeof providerConfig.totpProviderConfig.adjacentIntervals !== 'undefined' && typeof providerConfig.totpProviderConfig.adjacentIntervals !== 'number')) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Invalid multi-factor configuration response'); } this.providerConfigs.push(providerConfig); @@ -773,18 +773,18 @@ export function validateTestPhoneNumbers( ): void { if (!validator.isObject(testPhoneNumbers)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"testPhoneNumbers" must be a map of phone number / code pairs.', ); } if (Object.keys(testPhoneNumbers).length > MAXIMUM_TEST_PHONE_NUMBERS) { - throw new FirebaseAuthError(AuthClientErrorCode.MAXIMUM_TEST_PHONE_NUMBER_EXCEEDED); + throw new FirebaseAuthError(authClientErrorCode.MAXIMUM_TEST_PHONE_NUMBER_EXCEEDED); } for (const phoneNumber in testPhoneNumbers) { // Validate phone number. if (!validator.isPhoneNumber(phoneNumber)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_TESTING_PHONE_NUMBER, + authClientErrorCode.INVALID_TESTING_PHONE_NUMBER, `"${phoneNumber}" is not a valid E.164 standard compliant phone number.` ); } @@ -793,7 +793,7 @@ export function validateTestPhoneNumbers( if (!validator.isString(testPhoneNumbers[phoneNumber]) || !/^[\d]{6}$/.test(testPhoneNumbers[phoneNumber])) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_TESTING_PHONE_NUMBER, + authClientErrorCode.INVALID_TESTING_PHONE_NUMBER, `"${testPhoneNumbers[phoneNumber]}" is not a valid 6 digit code string.` ); } @@ -860,7 +860,7 @@ export class EmailSignInConfig implements EmailSignInProviderConfig { }; if (!validator.isNonNullObject(options)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"EmailSignInConfig" must be a non-null object.', ); } @@ -868,7 +868,7 @@ export class EmailSignInConfig implements EmailSignInProviderConfig { for (const key in options) { if (!(key in validKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, `"${key}" is not a valid EmailSignInConfig parameter.`, ); } @@ -877,14 +877,14 @@ export class EmailSignInConfig implements EmailSignInProviderConfig { if (typeof options.enabled !== 'undefined' && !validator.isBoolean(options.enabled)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"EmailSignInConfig.enabled" must be a boolean.', ); } if (typeof options.passwordRequired !== 'undefined' && !validator.isBoolean(options.passwordRequired)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"EmailSignInConfig.passwordRequired" must be a boolean.', ); } @@ -900,7 +900,7 @@ export class EmailSignInConfig implements EmailSignInProviderConfig { constructor(response: {[key: string]: any}) { if (typeof response.allowPasswordSignup === 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Invalid email sign-in configuration response'); } this.enabled = response.allowPasswordSignup; @@ -1165,7 +1165,7 @@ export class SAMLConfig implements SAMLAuthProviderConfig { }; if (!validator.isNonNullObject(options)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"SAMLAuthProviderConfig" must be a valid non-null object.', ); } @@ -1173,7 +1173,7 @@ export class SAMLConfig implements SAMLAuthProviderConfig { for (const key in options) { if (!(key in validKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${key}" is not a valid SAML config parameter.`, ); } @@ -1182,57 +1182,57 @@ export class SAMLConfig implements SAMLAuthProviderConfig { if (validator.isNonEmptyString(options.providerId)) { if (options.providerId.indexOf('saml.') !== 0) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_PROVIDER_ID, + authClientErrorCode.INVALID_PROVIDER_ID, '"SAMLAuthProviderConfig.providerId" must be a valid non-empty string prefixed with "saml.".', ); } } else if (!ignoreMissingFields) { // providerId is required and not provided correctly. throw new FirebaseAuthError( - !options.providerId ? AuthClientErrorCode.MISSING_PROVIDER_ID : AuthClientErrorCode.INVALID_PROVIDER_ID, + !options.providerId ? authClientErrorCode.MISSING_PROVIDER_ID : authClientErrorCode.INVALID_PROVIDER_ID, '"SAMLAuthProviderConfig.providerId" must be a valid non-empty string prefixed with "saml.".', ); } if (!(ignoreMissingFields && typeof options.idpEntityId === 'undefined') && !validator.isNonEmptyString(options.idpEntityId)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"SAMLAuthProviderConfig.idpEntityId" must be a valid non-empty string.', ); } if (!(ignoreMissingFields && typeof options.ssoURL === 'undefined') && !validator.isURL(options.ssoURL)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"SAMLAuthProviderConfig.ssoURL" must be a valid URL string.', ); } if (!(ignoreMissingFields && typeof options.rpEntityId === 'undefined') && !validator.isNonEmptyString(options.rpEntityId)) { throw new FirebaseAuthError( - !options.rpEntityId ? AuthClientErrorCode.MISSING_SAML_RELYING_PARTY_CONFIG : - AuthClientErrorCode.INVALID_CONFIG, + !options.rpEntityId ? authClientErrorCode.MISSING_SAML_RELYING_PARTY_CONFIG : + authClientErrorCode.INVALID_CONFIG, '"SAMLAuthProviderConfig.rpEntityId" must be a valid non-empty string.', ); } if (!(ignoreMissingFields && typeof options.callbackURL === 'undefined') && !validator.isURL(options.callbackURL)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"SAMLAuthProviderConfig.callbackURL" must be a valid URL string.', ); } if (!(ignoreMissingFields && typeof options.x509Certificates === 'undefined') && !validator.isArray(options.x509Certificates)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"SAMLAuthProviderConfig.x509Certificates" must be a valid array of X509 certificate strings.', ); } (options.x509Certificates || []).forEach((cert: string) => { if (!validator.isNonEmptyString(cert)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"SAMLAuthProviderConfig.x509Certificates" must be a valid array of X509 certificate strings.', ); } @@ -1240,21 +1240,21 @@ export class SAMLConfig implements SAMLAuthProviderConfig { if (typeof (options as any).enableRequestSigning !== 'undefined' && !validator.isBoolean((options as any).enableRequestSigning)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"SAMLAuthProviderConfig.enableRequestSigning" must be a boolean.', ); } if (typeof options.enabled !== 'undefined' && !validator.isBoolean(options.enabled)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"SAMLAuthProviderConfig.enabled" must be a boolean.', ); } if (typeof options.displayName !== 'undefined' && !validator.isString(options.displayName)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"SAMLAuthProviderConfig.displayName" must be a valid string.', ); } @@ -1277,14 +1277,14 @@ export class SAMLConfig implements SAMLAuthProviderConfig { !(validator.isString(response.name) && SAMLConfig.getProviderIdFromResourceName(response.name))) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Invalid SAML configuration response'); } const providerId = SAMLConfig.getProviderIdFromResourceName(response.name); if (!providerId) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Invalid SAML configuration response'); } this.providerId = providerId; @@ -1418,7 +1418,7 @@ export class OIDCConfig implements OIDCAuthProviderConfig { }; if (!validator.isNonNullObject(options)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"OIDCAuthProviderConfig" must be a valid non-null object.', ); } @@ -1426,7 +1426,7 @@ export class OIDCConfig implements OIDCAuthProviderConfig { for (const key in options) { if (!(key in validKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${key}" is not a valid OIDC config parameter.`, ); } @@ -1435,48 +1435,48 @@ export class OIDCConfig implements OIDCAuthProviderConfig { if (validator.isNonEmptyString(options.providerId)) { if (options.providerId.indexOf('oidc.') !== 0) { throw new FirebaseAuthError( - !options.providerId ? AuthClientErrorCode.MISSING_PROVIDER_ID : AuthClientErrorCode.INVALID_PROVIDER_ID, + !options.providerId ? authClientErrorCode.MISSING_PROVIDER_ID : authClientErrorCode.INVALID_PROVIDER_ID, '"OIDCAuthProviderConfig.providerId" must be a valid non-empty string prefixed with "oidc.".', ); } } else if (!ignoreMissingFields) { throw new FirebaseAuthError( - !options.providerId ? AuthClientErrorCode.MISSING_PROVIDER_ID : AuthClientErrorCode.INVALID_PROVIDER_ID, + !options.providerId ? authClientErrorCode.MISSING_PROVIDER_ID : authClientErrorCode.INVALID_PROVIDER_ID, '"OIDCAuthProviderConfig.providerId" must be a valid non-empty string prefixed with "oidc.".', ); } if (!(ignoreMissingFields && typeof options.clientId === 'undefined') && !validator.isNonEmptyString(options.clientId)) { throw new FirebaseAuthError( - !options.clientId ? AuthClientErrorCode.MISSING_OAUTH_CLIENT_ID : AuthClientErrorCode.INVALID_OAUTH_CLIENT_ID, + !options.clientId ? authClientErrorCode.MISSING_OAUTH_CLIENT_ID : authClientErrorCode.INVALID_OAUTH_CLIENT_ID, '"OIDCAuthProviderConfig.clientId" must be a valid non-empty string.', ); } if (!(ignoreMissingFields && typeof options.issuer === 'undefined') && !validator.isURL(options.issuer)) { throw new FirebaseAuthError( - !options.issuer ? AuthClientErrorCode.MISSING_ISSUER : AuthClientErrorCode.INVALID_CONFIG, + !options.issuer ? authClientErrorCode.MISSING_ISSUER : authClientErrorCode.INVALID_CONFIG, '"OIDCAuthProviderConfig.issuer" must be a valid URL string.', ); } if (typeof options.enabled !== 'undefined' && !validator.isBoolean(options.enabled)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"OIDCAuthProviderConfig.enabled" must be a boolean.', ); } if (typeof options.displayName !== 'undefined' && !validator.isString(options.displayName)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"OIDCAuthProviderConfig.displayName" must be a valid string.', ); } if (typeof options.clientSecret !== 'undefined' && !validator.isNonEmptyString(options.clientSecret)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"OIDCAuthProviderConfig.clientSecret" must be a valid string.', ); } @@ -1484,7 +1484,7 @@ export class OIDCConfig implements OIDCAuthProviderConfig { Object.keys(options.responseType).forEach((key) => { if (!(key in validResponseTypes)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${key}" is not a valid OAuthResponseType parameter.`, ); } @@ -1493,7 +1493,7 @@ export class OIDCConfig implements OIDCAuthProviderConfig { const idToken = options.responseType.idToken; if (typeof idToken !== 'undefined' && !validator.isBoolean(idToken)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"OIDCAuthProviderConfig.responseType.idToken" must be a boolean.', ); } @@ -1501,14 +1501,14 @@ export class OIDCConfig implements OIDCAuthProviderConfig { if (typeof code !== 'undefined') { if (!validator.isBoolean(code)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"OIDCAuthProviderConfig.responseType.code" must be a boolean.', ); } // If code flow is enabled, client secret must be provided. if (code && typeof options.clientSecret === 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.MISSING_OAUTH_CLIENT_SECRET, + authClientErrorCode.MISSING_OAUTH_CLIENT_SECRET, 'The OAuth configuration client secret is required to enable OIDC code flow.', ); } @@ -1519,7 +1519,7 @@ export class OIDCConfig implements OIDCAuthProviderConfig { // Only one of OAuth response types can be set to true. if (allKeys > 1 && enabledCount !== 1) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_OAUTH_RESPONSETYPE, + authClientErrorCode.INVALID_OAUTH_RESPONSETYPE, 'Only exactly one OAuth responseType should be set to true.', ); } @@ -1540,14 +1540,14 @@ export class OIDCConfig implements OIDCAuthProviderConfig { !(validator.isString(response.name) && OIDCConfig.getProviderIdFromResourceName(response.name))) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Invalid OIDC configuration response'); } const providerId = OIDCConfig.getProviderIdFromResourceName(response.name); if (!providerId) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Invalid SAML configuration response'); } this.providerId = providerId; @@ -1649,7 +1649,7 @@ export class SmsRegionsAuthConfig { public static validate(options: SmsRegionConfig): void { if (!validator.isNonNullObject(options)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"SmsRegionConfig" must be a non-null object.', ); } @@ -1662,7 +1662,7 @@ export class SmsRegionsAuthConfig { for (const key in options) { if (!(key in validKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${key}" is not a valid SmsRegionConfig parameter.`, ); } @@ -1671,7 +1671,7 @@ export class SmsRegionsAuthConfig { // validate mutual exclusiveness of allowByDefault and allowlistOnly if (typeof options.allowByDefault !== 'undefined' && typeof options.allowlistOnly !== 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, 'SmsRegionConfig cannot have both "allowByDefault" and "allowlistOnly" parameters.', ); } @@ -1683,7 +1683,7 @@ export class SmsRegionsAuthConfig { for (const key in options.allowByDefault) { if (!(key in allowByDefaultValidKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${key}" is not a valid SmsRegionConfig.allowByDefault parameter.`, ); } @@ -1692,7 +1692,7 @@ export class SmsRegionsAuthConfig { if (typeof options.allowByDefault.disallowedRegions !== 'undefined' && !validator.isArray(options.allowByDefault.disallowedRegions)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"SmsRegionConfig.allowByDefault.disallowedRegions" must be a valid string array.', ); } @@ -1705,7 +1705,7 @@ export class SmsRegionsAuthConfig { for (const key in options.allowlistOnly) { if (!(key in allowListOnlyValidKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${key}" is not a valid SmsRegionConfig.allowlistOnly parameter.`, ); } @@ -1715,7 +1715,7 @@ export class SmsRegionsAuthConfig { if (typeof options.allowlistOnly.allowedRegions !== 'undefined' && !validator.isArray(options.allowlistOnly.allowedRegions)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"SmsRegionConfig.allowlistOnly.allowedRegions" must be a valid string array.', ); } @@ -1941,7 +1941,7 @@ export class RecaptchaAuthConfig implements RecaptchaConfig { if (!validator.isNonNullObject(options)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"RecaptchaConfig" must be a non-null object.', ); } @@ -1949,7 +1949,7 @@ export class RecaptchaAuthConfig implements RecaptchaConfig { for (const key in options) { if (!(key in validKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${key}" is not a valid RecaptchaConfig parameter.`, ); } @@ -1959,7 +1959,7 @@ export class RecaptchaAuthConfig implements RecaptchaConfig { if (typeof options.emailPasswordEnforcementState !== 'undefined') { if (!validator.isNonEmptyString(options.emailPasswordEnforcementState)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"RecaptchaConfig.emailPasswordEnforcementState" must be a valid non-empty string.', ); } @@ -1968,7 +1968,7 @@ export class RecaptchaAuthConfig implements RecaptchaConfig { options.emailPasswordEnforcementState !== 'AUDIT' && options.emailPasswordEnforcementState !== 'ENFORCE') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"RecaptchaConfig.emailPasswordEnforcementState" must be either "OFF", "AUDIT" or "ENFORCE".', ); } @@ -1977,7 +1977,7 @@ export class RecaptchaAuthConfig implements RecaptchaConfig { if (typeof options.phoneEnforcementState !== 'undefined') { if (!validator.isNonEmptyString(options.phoneEnforcementState)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"RecaptchaConfig.phoneEnforcementState" must be a valid non-empty string.', ); } @@ -1986,7 +1986,7 @@ export class RecaptchaAuthConfig implements RecaptchaConfig { options.phoneEnforcementState !== 'AUDIT' && options.phoneEnforcementState !== 'ENFORCE') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"RecaptchaConfig.phoneEnforcementState" must be either "OFF", "AUDIT" or "ENFORCE".', ); } @@ -1996,7 +1996,7 @@ export class RecaptchaAuthConfig implements RecaptchaConfig { // Validate array if (!validator.isArray(options.managedRules)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"RecaptchaConfig.managedRules" must be an array of valid "RecaptchaManagedRule".', ); } @@ -2009,7 +2009,7 @@ export class RecaptchaAuthConfig implements RecaptchaConfig { if (typeof options.useAccountDefender !== 'undefined') { if (!validator.isBoolean(options.useAccountDefender)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"RecaptchaConfig.useAccountDefender" must be a boolean value".', ); } @@ -2018,7 +2018,7 @@ export class RecaptchaAuthConfig implements RecaptchaConfig { if (typeof options.useSmsBotScore !== 'undefined') { if (!validator.isBoolean(options.useSmsBotScore)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"RecaptchaConfig.useSmsBotScore" must be a boolean value".', ); } @@ -2027,7 +2027,7 @@ export class RecaptchaAuthConfig implements RecaptchaConfig { if (typeof options.useSmsTollFraudProtection !== 'undefined') { if (!validator.isBoolean(options.useSmsTollFraudProtection)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"RecaptchaConfig.useSmsTollFraudProtection" must be a boolean value".', ); } @@ -2037,7 +2037,7 @@ export class RecaptchaAuthConfig implements RecaptchaConfig { // Validate array if (!validator.isArray(options.smsTollFraudManagedRules)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"RecaptchaConfig.smsTollFraudManagedRules" must be an array of valid "RecaptchaTollFraudManagedRule".', ); } @@ -2059,7 +2059,7 @@ export class RecaptchaAuthConfig implements RecaptchaConfig { } if (!validator.isNonNullObject(options)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"RecaptchaManagedRule" must be a non-null object.', ); } @@ -2067,7 +2067,7 @@ export class RecaptchaAuthConfig implements RecaptchaConfig { for (const key in options) { if (!(key in validKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${key}" is not a valid RecaptchaManagedRule parameter.`, ); } @@ -2077,7 +2077,7 @@ export class RecaptchaAuthConfig implements RecaptchaConfig { if (typeof options.action !== 'undefined' && options.action !== 'BLOCK') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"RecaptchaManagedRule.action" must be "BLOCK".', ); } @@ -2094,7 +2094,7 @@ export class RecaptchaAuthConfig implements RecaptchaConfig { } if (!validator.isNonNullObject(options)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"RecaptchaTollFraudManagedRule" must be a non-null object.', ); } @@ -2102,7 +2102,7 @@ export class RecaptchaAuthConfig implements RecaptchaConfig { for (const key in options) { if (!(key in validKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${key}" is not a valid RecaptchaTollFraudManagedRule parameter.`, ); } @@ -2112,7 +2112,7 @@ export class RecaptchaAuthConfig implements RecaptchaConfig { if (typeof options.action !== 'undefined' && options.action !== 'BLOCK') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"RecaptchaTollFraudManagedRule.action" must be "BLOCK".', ); } @@ -2162,7 +2162,7 @@ export class MobileLinksAuthConfig { public static validate(options: MobileLinksConfig): void { if (!validator.isNonNullObject(options)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"MobileLinksConfig" must be a non-null object.', ); } @@ -2174,7 +2174,7 @@ export class MobileLinksAuthConfig { for (const key in options) { if (!(key in validKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${key}" is not a valid "MobileLinksConfig" parameter.`, ); } @@ -2184,7 +2184,7 @@ export class MobileLinksAuthConfig { && options.domain !== 'HOSTING_DOMAIN' && options.domain !== 'FIREBASE_DYNAMIC_LINK_DOMAIN') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"MobileLinksConfig.domain" must be either "HOSTING_DOMAIN" or "FIREBASE_DYNAMIC_LINK_DOMAIN".', ); } @@ -2314,7 +2314,7 @@ export class PasswordPolicyAuthConfig implements PasswordPolicyConfig { }; if (!validator.isNonNullObject(options)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"PasswordPolicyConfig" must be a non-null object.', ); } @@ -2322,7 +2322,7 @@ export class PasswordPolicyAuthConfig implements PasswordPolicyConfig { for (const key in options) { if (!(key in validKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${key}" is not a valid PasswordPolicyConfig parameter.`, ); } @@ -2332,7 +2332,7 @@ export class PasswordPolicyAuthConfig implements PasswordPolicyConfig { !(options.enforcementState === 'ENFORCE' || options.enforcementState === 'OFF')) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"PasswordPolicyConfig.enforcementState" must be either "ENFORCE" or "OFF".', ); } @@ -2340,7 +2340,7 @@ export class PasswordPolicyAuthConfig implements PasswordPolicyConfig { if (typeof options.forceUpgradeOnSignin !== 'undefined') { if (!validator.isBoolean(options.forceUpgradeOnSignin)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"PasswordPolicyConfig.forceUpgradeOnSignin" must be a boolean.', ); } @@ -2349,7 +2349,7 @@ export class PasswordPolicyAuthConfig implements PasswordPolicyConfig { if (typeof options.constraints !== 'undefined') { if (options.enforcementState === 'ENFORCE' && !validator.isNonNullObject(options.constraints)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"PasswordPolicyConfig.constraints" must be a non-empty object.', ); } @@ -2367,7 +2367,7 @@ export class PasswordPolicyAuthConfig implements PasswordPolicyConfig { for (const key in options.constraints) { if (!(key in validCharKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${key}" is not a valid PasswordPolicyConfig.constraints parameter.`, ); } @@ -2375,21 +2375,21 @@ export class PasswordPolicyAuthConfig implements PasswordPolicyConfig { if (typeof options.constraints.requireUppercase !== 'undefined' && !validator.isBoolean(options.constraints.requireUppercase)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"PasswordPolicyConfig.constraints.requireUppercase" must be a boolean.', ); } if (typeof options.constraints.requireLowercase !== 'undefined' && !validator.isBoolean(options.constraints.requireLowercase)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"PasswordPolicyConfig.constraints.requireLowercase" must be a boolean.', ); } if (typeof options.constraints.requireNonAlphanumeric !== 'undefined' && !validator.isBoolean(options.constraints.requireNonAlphanumeric)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"PasswordPolicyConfig.constraints.requireNonAlphanumeric"' + ' must be a boolean.', ); @@ -2397,7 +2397,7 @@ export class PasswordPolicyAuthConfig implements PasswordPolicyConfig { if (typeof options.constraints.requireNumeric !== 'undefined' && !validator.isBoolean(options.constraints.requireNumeric)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"PasswordPolicyConfig.constraints.requireNumeric" must be a boolean.', ); } @@ -2405,14 +2405,14 @@ export class PasswordPolicyAuthConfig implements PasswordPolicyConfig { options.constraints.minLength = 6; } else if (!validator.isNumber(options.constraints.minLength)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"PasswordPolicyConfig.constraints.minLength" must be a number.', ); } else { if (!(options.constraints.minLength >= 6 && options.constraints.minLength <= 30)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"PasswordPolicyConfig.constraints.minLength"' + ' must be an integer between 6 and 30, inclusive.', ); @@ -2422,14 +2422,14 @@ export class PasswordPolicyAuthConfig implements PasswordPolicyConfig { options.constraints.maxLength = 4096; } else if (!validator.isNumber(options.constraints.maxLength)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"PasswordPolicyConfig.constraints.maxLength" must be a number.', ); } else { if (!(options.constraints.maxLength >= options.constraints.minLength && options.constraints.maxLength <= 4096)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"PasswordPolicyConfig.constraints.maxLength"' + ' must be greater than or equal to minLength and at max 4096.', ); @@ -2438,7 +2438,7 @@ export class PasswordPolicyAuthConfig implements PasswordPolicyConfig { } else { if (options.enforcementState === 'ENFORCE') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"PasswordPolicyConfig.constraints" must be defined.', ); } @@ -2456,7 +2456,7 @@ export class PasswordPolicyAuthConfig implements PasswordPolicyConfig { constructor(response: PasswordPolicyAuthServerConfig) { if (typeof response.passwordPolicyEnforcementState === 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Invalid password policy configuration response'); } this.enforcementState = response.passwordPolicyEnforcementState; @@ -2525,7 +2525,7 @@ export class EmailPrivacyAuthConfig { public static validate(options: EmailPrivacyConfig): void { if (!validator.isNonNullObject(options)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"EmailPrivacyConfig" must be a non-null object.', ); } @@ -2537,7 +2537,7 @@ export class EmailPrivacyAuthConfig { for (const key in options) { if (!(key in validKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, `"${key}" is not a valid "EmailPrivacyConfig" parameter.`, ); } @@ -2546,7 +2546,7 @@ export class EmailPrivacyAuthConfig { if (typeof options.enableImprovedEmailPrivacy !== 'undefined' && !validator.isBoolean(options.enableImprovedEmailPrivacy)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"EmailPrivacyConfig.enableImprovedEmailPrivacy" must be a valid boolean value.', ); } diff --git a/src/auth/base-auth.ts b/src/auth/base-auth.ts index 25e1a3db2b..1d71210fe1 100644 --- a/src/auth/base-auth.ts +++ b/src/auth/base-auth.ts @@ -15,7 +15,8 @@ */ import { App, FirebaseArrayIndexError } from '../app'; -import { AuthClientErrorCode, ErrorInfo, FirebaseAuthError } from '../utils/error'; +import { authClientErrorCode, FirebaseAuthError } from './error'; +import { ErrorInfo } from '../utils/error'; import { deepCopy } from '../utils/deep-copy'; import * as validator from '../utils/validator'; @@ -217,7 +218,7 @@ export abstract class BaseAuth { if (checkRevoked || isEmulator) { return this.verifyDecodedJWTNotRevokedOrDisabled( decodedIdToken, - AuthClientErrorCode.ID_TOKEN_REVOKED); + authClientErrorCode.ID_TOKEN_REVOKED); } return decodedIdToken; }); @@ -331,7 +332,7 @@ export abstract class BaseAuth { public getUsers(identifiers: UserIdentifier[]): Promise { if (!validator.isArray(identifiers)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, '`identifiers` parameter must be an array'); + authClientErrorCode.INVALID_ARGUMENT, '`identifiers` parameter must be an array'); } return this.authRequestHandler .getAccountInfoByIdentifiers(identifiers) @@ -355,7 +356,7 @@ export abstract class BaseAuth { return !!matchingUserInfo && id.providerUid === matchingUserInfo.uid; } else { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'Unhandled identifier type'); } }); @@ -427,7 +428,7 @@ export abstract class BaseAuth { if (error.code === 'auth/user-not-found') { // Something must have happened after creating the user and then retrieving it. throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'Unable to create the user record provided.'); } throw error; @@ -477,7 +478,7 @@ export abstract class BaseAuth { public deleteUsers(uids: string[]): Promise { if (!validator.isArray(uids)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, '`uids` parameter must be an array'); + authClientErrorCode.INVALID_ARGUMENT, '`uids` parameter must be an array'); } return this.authRequestHandler.deleteAccounts(uids, /*force=*/true) .then((batchDeleteAccountsResponse) => { @@ -496,7 +497,7 @@ export abstract class BaseAuth { result.errors = batchDeleteAccountsResponse.errors.map((batchDeleteErrorInfo) => { if (batchDeleteErrorInfo.index === undefined) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'Corrupt BatchDeleteAccountsResponse detected'); } @@ -504,7 +505,7 @@ export abstract class BaseAuth { // We unconditionally set force=true, so the 'NOT_DISABLED' error // should not be possible. const code = msg && msg.startsWith('NOT_DISABLED') ? - AuthClientErrorCode.USER_NOT_DISABLED : AuthClientErrorCode.INTERNAL_ERROR; + authClientErrorCode.USER_NOT_DISABLED : authClientErrorCode.INTERNAL_ERROR; return new FirebaseAuthError(code, batchDeleteErrorInfo.message); }; @@ -544,7 +545,7 @@ export abstract class BaseAuth { if (properties.providerToLink.providerId === 'email') { if (typeof properties.email !== 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, "Both UpdateRequest.email and UpdateRequest.providerToLink.providerId='email' were set. To " + 'link to the email/password provider, only specify the UpdateRequest.email field.'); } @@ -553,7 +554,7 @@ export abstract class BaseAuth { } else if (properties.providerToLink.providerId === 'phone') { if (typeof properties.phoneNumber !== 'undefined') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, "Both UpdateRequest.phoneNumber and UpdateRequest.providerToLink.providerId='phone' were set. To " + 'link to a phone provider, only specify the UpdateRequest.phoneNumber field.'); } @@ -569,7 +570,7 @@ export abstract class BaseAuth { // to relax this restriction and just unlink it. if (properties.phoneNumber === null) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, "Both UpdateRequest.phoneNumber=null and UpdateRequest.providersToUnlink=['phone'] were set. To " + 'unlink from a phone provider, only specify the UpdateRequest.phoneNumber=null field.'); } @@ -683,7 +684,7 @@ export abstract class BaseAuth { // Return rejected promise if expiresIn is not available. if (!validator.isNonNullObject(sessionCookieOptions) || !validator.isNumber(sessionCookieOptions.expiresIn)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_SESSION_COOKIE_DURATION)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_SESSION_COOKIE_DURATION)); } return this.authRequestHandler.createSessionCookie( idToken, sessionCookieOptions.expiresIn); @@ -723,7 +724,7 @@ export abstract class BaseAuth { if (checkRevoked || isEmulator) { return this.verifyDecodedJWTNotRevokedOrDisabled( decodedIdToken, - AuthClientErrorCode.SESSION_COOKIE_REVOKED); + authClientErrorCode.SESSION_COOKIE_REVOKED); } return decodedIdToken; }); @@ -966,7 +967,7 @@ export abstract class BaseAuth { } return Promise.reject( new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"AuthProviderConfigFilter.type" must be either "saml" or "oidc"')); } @@ -997,7 +998,7 @@ export abstract class BaseAuth { return new SAMLConfig(response); }); } - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID)); } /** @@ -1019,7 +1020,7 @@ export abstract class BaseAuth { } else if (SAMLConfig.isProviderId(providerId)) { return this.authRequestHandler.deleteInboundSamlConfig(providerId); } - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID)); } /** @@ -1041,7 +1042,7 @@ export abstract class BaseAuth { providerId: string, updatedConfig: UpdateAuthProviderRequest): Promise { if (!validator.isNonNullObject(updatedConfig)) { return Promise.reject(new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, 'Request is missing "UpdateAuthProviderRequest" configuration.', )); } @@ -1056,7 +1057,7 @@ export abstract class BaseAuth { return new SAMLConfig(response); }); } - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID)); } /** @@ -1073,7 +1074,7 @@ export abstract class BaseAuth { public createProviderConfig(config: AuthProviderConfig): Promise { if (!validator.isNonNullObject(config)) { return Promise.reject(new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, 'Request is missing "AuthProviderConfig" configuration.', )); } @@ -1088,7 +1089,7 @@ export abstract class BaseAuth { return new SAMLConfig(response); }); } - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID)); } /** @alpha */ @@ -1121,7 +1122,7 @@ export abstract class BaseAuth { .then((user: UserRecord) => { if (user.disabled) { throw new FirebaseAuthError( - AuthClientErrorCode.USER_DISABLED, + authClientErrorCode.USER_DISABLED, 'The user record is disabled.'); } // If no tokens valid after time available, token is not revoked. diff --git a/src/auth/error.ts b/src/auth/error.ts new file mode 100644 index 0000000000..3d29020ee2 --- /dev/null +++ b/src/auth/error.ts @@ -0,0 +1,695 @@ +/*! + * Copyright 2026 Google LLC + * + * 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 { PrefixedFirebaseError, toHttpResponse, ErrorInfo } from '../utils/error'; +import { RequestResponseError } from '../utils/api-request'; +import { deepCopy } from '../utils/deep-copy'; + +/** + * The constant mapping for valid Auth client error codes. + */ +export const AuthErrorCode = { + AUTH_BLOCKING_TOKEN_EXPIRED: 'auth-blocking-token-expired', + BILLING_NOT_ENABLED: 'billing-not-enabled', + CLAIMS_TOO_LARGE: 'claims-too-large', + CONFIGURATION_EXISTS: 'configuration-exists', + CONFIGURATION_NOT_FOUND: 'configuration-not-found', + ID_TOKEN_EXPIRED: 'id-token-expired', + INVALID_ARGUMENT: 'argument-error', + INVALID_CONFIG: 'invalid-config', + EMAIL_ALREADY_EXISTS: 'email-already-exists', + EMAIL_NOT_FOUND: 'email-not-found', + FORBIDDEN_CLAIM: 'reserved-claim', + INVALID_ID_TOKEN: 'invalid-id-token', + ID_TOKEN_REVOKED: 'id-token-revoked', + INTERNAL_ERROR: 'internal-error', + INVALID_CLAIMS: 'invalid-claims', + INVALID_CONTINUE_URI: 'invalid-continue-uri', + INVALID_CREATION_TIME: 'invalid-creation-time', + INVALID_CREDENTIAL: 'invalid-credential', + INVALID_DISABLED_FIELD: 'invalid-disabled-field', + INVALID_DISPLAY_NAME: 'invalid-display-name', + INVALID_DYNAMIC_LINK_DOMAIN: 'invalid-dynamic-link-domain', + INVALID_HOSTING_LINK_DOMAIN: 'invalid-hosting-link-domain', + INVALID_EMAIL_VERIFIED: 'invalid-email-verified', + INVALID_EMAIL: 'invalid-email', + INVALID_NEW_EMAIL: 'invalid-new-email', + INVALID_ENROLLED_FACTORS: 'invalid-enrolled-factors', + INVALID_ENROLLMENT_TIME: 'invalid-enrollment-time', + INVALID_HASH_ALGORITHM: 'invalid-hash-algorithm', + INVALID_HASH_BLOCK_SIZE: 'invalid-hash-block-size', + INVALID_HASH_DERIVED_KEY_LENGTH: 'invalid-hash-derived-key-length', + INVALID_HASH_KEY: 'invalid-hash-key', + INVALID_HASH_MEMORY_COST: 'invalid-hash-memory-cost', + INVALID_HASH_PARALLELIZATION: 'invalid-hash-parallelization', + INVALID_HASH_ROUNDS: 'invalid-hash-rounds', + INVALID_HASH_SALT_SEPARATOR: 'invalid-hash-salt-separator', + INVALID_LAST_SIGN_IN_TIME: 'invalid-last-sign-in-time', + INVALID_NAME: 'invalid-name', + INVALID_OAUTH_CLIENT_ID: 'invalid-oauth-client-id', + INVALID_PAGE_TOKEN: 'invalid-page-token', + INVALID_PASSWORD: 'invalid-password', + INVALID_PASSWORD_HASH: 'invalid-password-hash', + INVALID_PASSWORD_SALT: 'invalid-password-salt', + INVALID_PHONE_NUMBER: 'invalid-phone-number', + INVALID_PHOTO_URL: 'invalid-photo-url', + INVALID_PROJECT_ID: 'invalid-project-id', + INVALID_PROVIDER_DATA: 'invalid-provider-data', + INVALID_PROVIDER_ID: 'invalid-provider-id', + INVALID_PROVIDER_UID: 'invalid-provider-uid', + INVALID_OAUTH_RESPONSETYPE: 'invalid-oauth-responsetype', + INVALID_SESSION_COOKIE_DURATION: 'invalid-session-cookie-duration', + INVALID_TENANT_ID: 'invalid-tenant-id', + INVALID_TENANT_TYPE: 'invalid-tenant-type', + INVALID_TESTING_PHONE_NUMBER: 'invalid-testing-phone-number', + INVALID_UID: 'invalid-uid', + INVALID_USER_IMPORT: 'invalid-user-import', + INVALID_TOKENS_VALID_AFTER_TIME: 'invalid-tokens-valid-after-time', + MISMATCHING_TENANT_ID: 'mismatching-tenant-id', + MISSING_ANDROID_PACKAGE_NAME: 'missing-android-package-name', + MISSING_CONFIG: 'missing-config', + MISSING_CONTINUE_URI: 'missing-continue-uri', + MISSING_DISPLAY_NAME: 'missing-display-name', + MISSING_EMAIL: 'missing-email', + MISSING_IOS_BUNDLE_ID: 'missing-ios-bundle-id', + MISSING_ISSUER: 'missing-issuer', + MISSING_HASH_ALGORITHM: 'missing-hash-algorithm', + MISSING_OAUTH_CLIENT_ID: 'missing-oauth-client-id', + MISSING_OAUTH_CLIENT_SECRET: 'missing-oauth-client-secret', + MISSING_PROVIDER_ID: 'missing-provider-id', + MISSING_SAML_RELYING_PARTY_CONFIG: 'missing-saml-relying-party-config', + MAXIMUM_TEST_PHONE_NUMBER_EXCEEDED: 'test-phone-number-limit-exceeded', + MAXIMUM_USER_COUNT_EXCEEDED: 'maximum-user-count-exceeded', + MISSING_UID: 'missing-uid', + OPERATION_NOT_ALLOWED: 'operation-not-allowed', + PHONE_NUMBER_ALREADY_EXISTS: 'phone-number-already-exists', + PROJECT_NOT_FOUND: 'project-not-found', + INSUFFICIENT_PERMISSION: 'insufficient-permission', + QUOTA_EXCEEDED: 'quota-exceeded', + SECOND_FACTOR_LIMIT_EXCEEDED: 'second-factor-limit-exceeded', + SECOND_FACTOR_UID_ALREADY_EXISTS: 'second-factor-uid-already-exists', + SESSION_COOKIE_EXPIRED: 'session-cookie-expired', + SESSION_COOKIE_REVOKED: 'session-cookie-revoked', + TENANT_NOT_FOUND: 'tenant-not-found', + UID_ALREADY_EXISTS: 'uid-already-exists', + UNAUTHORIZED_DOMAIN: 'unauthorized-continue-uri', + UNSUPPORTED_FIRST_FACTOR: 'unsupported-first-factor', + UNSUPPORTED_SECOND_FACTOR: 'unsupported-second-factor', + UNSUPPORTED_TENANT_OPERATION: 'unsupported-tenant-operation', + UNVERIFIED_EMAIL: 'unverified-email', + USER_NOT_FOUND: 'user-not-found', + NOT_FOUND: 'not-found', + USER_DISABLED: 'user-disabled', + USER_NOT_DISABLED: 'user-not-disabled', + INVALID_RECAPTCHA_ACTION: 'invalid-recaptcha-action', + INVALID_RECAPTCHA_ENFORCEMENT_STATE: 'invalid-recaptcha-enforcement-state', + RECAPTCHA_NOT_ENABLED: 'recaptcha-not-enabled', +} as const; + +/** + * The type definition for valid Auth client error codes. + */ +export type AuthErrorCode = typeof AuthErrorCode[keyof typeof AuthErrorCode]; + +/** + * Internal Auth client error code mapping used to construct ErrorInfo. + */ +export const authClientErrorCode: { readonly [K in keyof typeof AuthErrorCode]: ErrorInfo } = { + AUTH_BLOCKING_TOKEN_EXPIRED: { + code: AuthErrorCode.AUTH_BLOCKING_TOKEN_EXPIRED, + message: 'The provided Firebase Auth Blocking token is expired.', + }, + BILLING_NOT_ENABLED: { + code: AuthErrorCode.BILLING_NOT_ENABLED, + message: 'Feature requires billing to be enabled.', + }, + CLAIMS_TOO_LARGE: { + code: AuthErrorCode.CLAIMS_TOO_LARGE, + message: 'Developer claims maximum payload size exceeded.', + }, + CONFIGURATION_EXISTS: { + code: AuthErrorCode.CONFIGURATION_EXISTS, + message: 'A configuration already exists with the provided identifier.', + }, + CONFIGURATION_NOT_FOUND: { + code: AuthErrorCode.CONFIGURATION_NOT_FOUND, + message: 'There is no configuration corresponding to the provided identifier.', + }, + ID_TOKEN_EXPIRED: { + code: AuthErrorCode.ID_TOKEN_EXPIRED, + message: 'The provided Firebase ID token is expired.', + }, + INVALID_ARGUMENT: { + code: AuthErrorCode.INVALID_ARGUMENT, + message: 'Invalid argument provided.', + }, + INVALID_CONFIG: { + code: AuthErrorCode.INVALID_CONFIG, + message: 'The provided configuration is invalid.', + }, + EMAIL_ALREADY_EXISTS: { + code: AuthErrorCode.EMAIL_ALREADY_EXISTS, + message: 'The email address is already in use by another account.', + }, + EMAIL_NOT_FOUND: { + code: AuthErrorCode.EMAIL_NOT_FOUND, + message: 'There is no user record corresponding to the provided email.', + }, + FORBIDDEN_CLAIM: { + code: AuthErrorCode.FORBIDDEN_CLAIM, + message: 'The specified developer claim is reserved and cannot be specified.', + }, + INVALID_ID_TOKEN: { + code: AuthErrorCode.INVALID_ID_TOKEN, + message: 'The provided ID token is not a valid Firebase ID token.', + }, + ID_TOKEN_REVOKED: { + code: AuthErrorCode.ID_TOKEN_REVOKED, + message: 'The Firebase ID token has been revoked.', + }, + INTERNAL_ERROR: { + code: AuthErrorCode.INTERNAL_ERROR, + message: 'An internal error has occurred.', + }, + INVALID_CLAIMS: { + code: AuthErrorCode.INVALID_CLAIMS, + message: 'The provided custom claim attributes are invalid.', + }, + INVALID_CONTINUE_URI: { + code: AuthErrorCode.INVALID_CONTINUE_URI, + message: 'The continue URL must be a valid URL string.', + }, + INVALID_CREATION_TIME: { + code: AuthErrorCode.INVALID_CREATION_TIME, + message: 'The creation time must be a valid UTC date string.', + }, + INVALID_CREDENTIAL: { + code: AuthErrorCode.INVALID_CREDENTIAL, + message: 'Invalid credential object provided.', + }, + INVALID_DISABLED_FIELD: { + code: AuthErrorCode.INVALID_DISABLED_FIELD, + message: 'The disabled field must be a boolean.', + }, + INVALID_DISPLAY_NAME: { + code: AuthErrorCode.INVALID_DISPLAY_NAME, + message: 'The displayName field must be a valid string.', + }, + INVALID_DYNAMIC_LINK_DOMAIN: { + code: AuthErrorCode.INVALID_DYNAMIC_LINK_DOMAIN, + message: 'The provided dynamic link domain is not configured or authorized for the current project.', + }, + INVALID_HOSTING_LINK_DOMAIN: { + code: AuthErrorCode.INVALID_HOSTING_LINK_DOMAIN, + message: 'The provided hosting link domain is not configured in Firebase Hosting or ' + + 'is not owned by the current project.', + }, + INVALID_EMAIL_VERIFIED: { + code: AuthErrorCode.INVALID_EMAIL_VERIFIED, + message: 'The emailVerified field must be a boolean.', + }, + INVALID_EMAIL: { + code: AuthErrorCode.INVALID_EMAIL, + message: 'The email address is improperly formatted.', + }, + INVALID_NEW_EMAIL: { + code: AuthErrorCode.INVALID_NEW_EMAIL, + message: 'The new email address is improperly formatted.', + }, + INVALID_ENROLLED_FACTORS: { + code: AuthErrorCode.INVALID_ENROLLED_FACTORS, + message: 'The enrolled factors must be a valid array of MultiFactorInfo objects.', + }, + INVALID_ENROLLMENT_TIME: { + code: AuthErrorCode.INVALID_ENROLLMENT_TIME, + message: 'The second factor enrollment time must be a valid UTC date string.', + }, + INVALID_HASH_ALGORITHM: { + code: AuthErrorCode.INVALID_HASH_ALGORITHM, + message: 'The hash algorithm must match one of the strings in the list of supported algorithms.', + }, + INVALID_HASH_BLOCK_SIZE: { + code: AuthErrorCode.INVALID_HASH_BLOCK_SIZE, + message: 'The hash block size must be a valid number.', + }, + INVALID_HASH_DERIVED_KEY_LENGTH: { + code: AuthErrorCode.INVALID_HASH_DERIVED_KEY_LENGTH, + message: 'The hash derived key length must be a valid number.', + }, + INVALID_HASH_KEY: { + code: AuthErrorCode.INVALID_HASH_KEY, + message: 'The hash key must a valid byte buffer.', + }, + INVALID_HASH_MEMORY_COST: { + code: AuthErrorCode.INVALID_HASH_MEMORY_COST, + message: 'The hash memory cost must be a valid number.', + }, + INVALID_HASH_PARALLELIZATION: { + code: AuthErrorCode.INVALID_HASH_PARALLELIZATION, + message: 'The hash parallelization must be a valid number.', + }, + INVALID_HASH_ROUNDS: { + code: AuthErrorCode.INVALID_HASH_ROUNDS, + message: 'The hash rounds must be a valid number.', + }, + INVALID_HASH_SALT_SEPARATOR: { + code: AuthErrorCode.INVALID_HASH_SALT_SEPARATOR, + message: 'The hashing algorithm salt separator field must be a valid byte buffer.', + }, + INVALID_LAST_SIGN_IN_TIME: { + code: AuthErrorCode.INVALID_LAST_SIGN_IN_TIME, + message: 'The last sign-in time must be a valid UTC date string.', + }, + INVALID_NAME: { + code: AuthErrorCode.INVALID_NAME, + message: 'The resource name provided is invalid.', + }, + INVALID_OAUTH_CLIENT_ID: { + code: AuthErrorCode.INVALID_OAUTH_CLIENT_ID, + message: 'The provided OAuth client ID is invalid.', + }, + INVALID_PAGE_TOKEN: { + code: AuthErrorCode.INVALID_PAGE_TOKEN, + message: 'The page token must be a valid non-empty string.', + }, + INVALID_PASSWORD: { + code: AuthErrorCode.INVALID_PASSWORD, + message: 'The password must be a string with at least 6 characters.', + }, + INVALID_PASSWORD_HASH: { + code: AuthErrorCode.INVALID_PASSWORD_HASH, + message: 'The password hash must be a valid byte buffer.', + }, + INVALID_PASSWORD_SALT: { + code: AuthErrorCode.INVALID_PASSWORD_SALT, + message: 'The password salt must be a valid byte buffer.', + }, + INVALID_PHONE_NUMBER: { + code: AuthErrorCode.INVALID_PHONE_NUMBER, + message: 'The phone number must be a non-empty E.164 standard compliant identifier string.', + }, + INVALID_PHOTO_URL: { + code: AuthErrorCode.INVALID_PHOTO_URL, + message: 'The photoURL field must be a valid URL.', + }, + INVALID_PROJECT_ID: { + code: AuthErrorCode.INVALID_PROJECT_ID, + message: 'Invalid parent project. Either parent project doesn\'t exist or didn\'t enable multi-tenancy.', + }, + INVALID_PROVIDER_DATA: { + code: AuthErrorCode.INVALID_PROVIDER_DATA, + message: 'The providerData must be a valid array of UserInfo objects.', + }, + INVALID_PROVIDER_ID: { + code: AuthErrorCode.INVALID_PROVIDER_ID, + message: 'The providerId must be a valid supported provider identifier string.', + }, + INVALID_PROVIDER_UID: { + code: AuthErrorCode.INVALID_PROVIDER_UID, + message: 'The providerUid must be a valid provider uid string.', + }, + INVALID_OAUTH_RESPONSETYPE: { + code: AuthErrorCode.INVALID_OAUTH_RESPONSETYPE, + message: 'Only exactly one OAuth responseType should be set to true.', + }, + INVALID_SESSION_COOKIE_DURATION: { + code: AuthErrorCode.INVALID_SESSION_COOKIE_DURATION, + message: 'The session cookie duration must be a valid number in milliseconds between 5 minutes and 2 weeks.', + }, + INVALID_TENANT_ID: { + code: AuthErrorCode.INVALID_TENANT_ID, + message: 'The tenant ID must be a valid non-empty string.', + }, + INVALID_TENANT_TYPE: { + code: AuthErrorCode.INVALID_TENANT_TYPE, + message: 'Tenant type must be either "full_service" or "lightweight".', + }, + INVALID_TESTING_PHONE_NUMBER: { + code: AuthErrorCode.INVALID_TESTING_PHONE_NUMBER, + message: 'Invalid testing phone number or invalid test code provided.', + }, + INVALID_UID: { + code: AuthErrorCode.INVALID_UID, + message: 'The uid must be a non-empty string with at most 128 characters.', + }, + INVALID_USER_IMPORT: { + code: AuthErrorCode.INVALID_USER_IMPORT, + message: 'The user record to import is invalid.', + }, + INVALID_TOKENS_VALID_AFTER_TIME: { + code: AuthErrorCode.INVALID_TOKENS_VALID_AFTER_TIME, + message: 'The tokensValidAfterTime must be a valid UTC number in seconds.', + }, + MISMATCHING_TENANT_ID: { + code: AuthErrorCode.MISMATCHING_TENANT_ID, + message: 'User tenant ID does not match with the current TenantAwareAuth tenant ID.', + }, + MISSING_ANDROID_PACKAGE_NAME: { + code: AuthErrorCode.MISSING_ANDROID_PACKAGE_NAME, + message: 'An Android Package Name must be provided if the Android App is required to be installed.', + }, + MISSING_CONFIG: { + code: AuthErrorCode.MISSING_CONFIG, + message: 'The provided configuration is missing required attributes.', + }, + MISSING_CONTINUE_URI: { + code: AuthErrorCode.MISSING_CONTINUE_URI, + message: 'A valid continue URL must be provided in the request.', + }, + MISSING_DISPLAY_NAME: { + code: AuthErrorCode.MISSING_DISPLAY_NAME, + message: 'The resource being created or edited is missing a valid display name.', + }, + MISSING_EMAIL: { + code: AuthErrorCode.MISSING_EMAIL, + message: 'The email is required for the specified action. For example, a multi-factor ' + + 'user requires a verified email.', + }, + MISSING_IOS_BUNDLE_ID: { + code: AuthErrorCode.MISSING_IOS_BUNDLE_ID, + message: 'The request is missing an iOS Bundle ID.', + }, + MISSING_ISSUER: { + code: AuthErrorCode.MISSING_ISSUER, + message: 'The OAuth/OIDC configuration issuer must not be empty.', + }, + MISSING_HASH_ALGORITHM: { + code: AuthErrorCode.MISSING_HASH_ALGORITHM, + message: 'Importing users with password hashes requires that the hashing algorithm and its parameters be provided.', + }, + MISSING_OAUTH_CLIENT_ID: { + code: AuthErrorCode.MISSING_OAUTH_CLIENT_ID, + message: 'The OAuth/OIDC configuration client ID must not be empty.', + }, + MISSING_OAUTH_CLIENT_SECRET: { + code: AuthErrorCode.MISSING_OAUTH_CLIENT_SECRET, + message: 'The OAuth configuration client secret is required to enable OIDC code flow.', + }, + MISSING_PROVIDER_ID: { + code: AuthErrorCode.MISSING_PROVIDER_ID, + message: 'A valid provider ID must be provided in the request.', + }, + MISSING_SAML_RELYING_PARTY_CONFIG: { + code: AuthErrorCode.MISSING_SAML_RELYING_PARTY_CONFIG, + message: 'The SAML configuration provided is missing a relying party configuration.', + }, + MAXIMUM_TEST_PHONE_NUMBER_EXCEEDED: { + code: AuthErrorCode.MAXIMUM_TEST_PHONE_NUMBER_EXCEEDED, + message: 'The maximum allowed number of test phone number / code pairs has been exceeded.', + }, + MAXIMUM_USER_COUNT_EXCEEDED: { + code: AuthErrorCode.MAXIMUM_USER_COUNT_EXCEEDED, + message: 'The maximum allowed number of users to import has been exceeded.', + }, + MISSING_UID: { + code: AuthErrorCode.MISSING_UID, + message: 'A uid identifier is required for the current operation.', + }, + OPERATION_NOT_ALLOWED: { + code: AuthErrorCode.OPERATION_NOT_ALLOWED, + message: 'The given sign-in provider is disabled for this Firebase project. Enable it in the ' + + 'Firebase console, under the sign-in method tab of the Auth section.', + }, + PHONE_NUMBER_ALREADY_EXISTS: { + code: AuthErrorCode.PHONE_NUMBER_ALREADY_EXISTS, + message: 'The user with the provided phone number already exists.', + }, + PROJECT_NOT_FOUND: { + code: AuthErrorCode.PROJECT_NOT_FOUND, + message: 'No Firebase project was found for the provided credential.', + }, + INSUFFICIENT_PERMISSION: { + code: AuthErrorCode.INSUFFICIENT_PERMISSION, + message: 'Credential implementation provided to initializeApp() via the "credential" property has insufficient permission to access the requested resource. See https://firebase.google.com/docs/admin/setup for details on how to authenticate this SDK with appropriate permissions.', + }, + QUOTA_EXCEEDED: { + code: AuthErrorCode.QUOTA_EXCEEDED, + message: 'The project quota for the specified operation has been exceeded.', + }, + SECOND_FACTOR_LIMIT_EXCEEDED: { + code: AuthErrorCode.SECOND_FACTOR_LIMIT_EXCEEDED, + message: 'The maximum number of allowed second factors on a user has been exceeded.', + }, + SECOND_FACTOR_UID_ALREADY_EXISTS: { + code: AuthErrorCode.SECOND_FACTOR_UID_ALREADY_EXISTS, + message: 'The specified second factor "uid" already exists.', + }, + SESSION_COOKIE_EXPIRED: { + code: AuthErrorCode.SESSION_COOKIE_EXPIRED, + message: 'The Firebase session cookie is expired.', + }, + SESSION_COOKIE_REVOKED: { + code: AuthErrorCode.SESSION_COOKIE_REVOKED, + message: 'The Firebase session cookie has been revoked.', + }, + TENANT_NOT_FOUND: { + code: AuthErrorCode.TENANT_NOT_FOUND, + message: 'There is no tenant corresponding to the provided identifier.', + }, + UID_ALREADY_EXISTS: { + code: AuthErrorCode.UID_ALREADY_EXISTS, + message: 'The user with the provided uid already exists.', + }, + UNAUTHORIZED_DOMAIN: { + code: AuthErrorCode.UNAUTHORIZED_DOMAIN, + message: 'The domain of the continue URL is not whitelisted. Whitelist the domain in the Firebase console.', + }, + UNSUPPORTED_FIRST_FACTOR: { + code: AuthErrorCode.UNSUPPORTED_FIRST_FACTOR, + message: 'A multi-factor user requires a supported first factor.', + }, + UNSUPPORTED_SECOND_FACTOR: { + code: AuthErrorCode.UNSUPPORTED_SECOND_FACTOR, + message: 'The request specified an unsupported type of second factor.', + }, + UNSUPPORTED_TENANT_OPERATION: { + code: AuthErrorCode.UNSUPPORTED_TENANT_OPERATION, + message: 'This operation is not supported in a multi-tenant context.', + }, + UNVERIFIED_EMAIL: { + code: AuthErrorCode.UNVERIFIED_EMAIL, + message: 'A verified email is required for the specified action. For example, a ' + + 'multi-factor user requires a verified email.', + }, + USER_NOT_FOUND: { + code: AuthErrorCode.USER_NOT_FOUND, + message: 'There is no user record corresponding to the provided identifier.', + }, + NOT_FOUND: { + code: AuthErrorCode.NOT_FOUND, + message: 'The requested resource was not found.', + }, + USER_DISABLED: { + code: AuthErrorCode.USER_DISABLED, + message: 'The user record is disabled.', + }, + USER_NOT_DISABLED: { + code: AuthErrorCode.USER_NOT_DISABLED, + message: 'The user must be disabled in order to bulk delete it (or you must pass force=true).', + }, + INVALID_RECAPTCHA_ACTION: { + code: AuthErrorCode.INVALID_RECAPTCHA_ACTION, + message: 'reCAPTCHA action must be "BLOCK".', + }, + INVALID_RECAPTCHA_ENFORCEMENT_STATE: { + code: AuthErrorCode.INVALID_RECAPTCHA_ENFORCEMENT_STATE, + message: 'reCAPTCHA enforcement state must be either "OFF", "AUDIT" or "ENFORCE".', + }, + RECAPTCHA_NOT_ENABLED: { + code: AuthErrorCode.RECAPTCHA_NOT_ENABLED, + message: 'reCAPTCHA enterprise is not enabled.', + }, +}; + +/** @const {Record} Auth server to client enum error codes. */ +const AUTH_SERVER_TO_CLIENT_CODE: Record = { + // Feature being configured or used requires a billing account. + BILLING_NOT_ENABLED: 'BILLING_NOT_ENABLED', + // Claims payload is too large. + CLAIMS_TOO_LARGE: 'CLAIMS_TOO_LARGE', + // Configuration being added already exists. + CONFIGURATION_EXISTS: 'CONFIGURATION_EXISTS', + // Configuration not found. + CONFIGURATION_NOT_FOUND: 'CONFIGURATION_NOT_FOUND', + // Provided credential has insufficient permissions. + INSUFFICIENT_PERMISSION: 'INSUFFICIENT_PERMISSION', + // Provided configuration has invalid fields. + INVALID_CONFIG: 'INVALID_CONFIG', + // Provided configuration identifier is invalid. + INVALID_CONFIG_ID: 'INVALID_PROVIDER_ID', + // ActionCodeSettings missing continue URL. + INVALID_CONTINUE_URI: 'INVALID_CONTINUE_URI', + // Dynamic link domain in provided ActionCodeSettings is not authorized. + INVALID_DYNAMIC_LINK_DOMAIN: 'INVALID_DYNAMIC_LINK_DOMAIN', + // Hosting link domain in provided ActionCodeSettings is not owned by the current project. + INVALID_HOSTING_LINK_DOMAIN: 'INVALID_HOSTING_LINK_DOMAIN', + // uploadAccount provides an email that already exists. + DUPLICATE_EMAIL: 'EMAIL_ALREADY_EXISTS', + // uploadAccount provides a localId that already exists. + DUPLICATE_LOCAL_ID: 'UID_ALREADY_EXISTS', + // Request specified a multi-factor enrollment ID that already exists. + DUPLICATE_MFA_ENROLLMENT_ID: 'SECOND_FACTOR_UID_ALREADY_EXISTS', + // setAccountInfo email already exists. + EMAIL_EXISTS: 'EMAIL_ALREADY_EXISTS', + // /accounts:sendOobCode for password reset when user is not found. + EMAIL_NOT_FOUND: 'EMAIL_NOT_FOUND', + // Reserved claim name. + FORBIDDEN_CLAIM: 'FORBIDDEN_CLAIM', + // Invalid claims provided. + INVALID_CLAIMS: 'INVALID_CLAIMS', + // Invalid session cookie duration. + INVALID_DURATION: 'INVALID_SESSION_COOKIE_DURATION', + // Invalid email provided. + INVALID_EMAIL: 'INVALID_EMAIL', + // Invalid new email provided. + INVALID_NEW_EMAIL: 'INVALID_NEW_EMAIL', + // Invalid tenant display name. This can be thrown on CreateTenant and UpdateTenant. + INVALID_DISPLAY_NAME: 'INVALID_DISPLAY_NAME', + // Invalid ID token provided. + INVALID_ID_TOKEN: 'INVALID_ID_TOKEN', + // Invalid tenant/parent resource name. + INVALID_NAME: 'INVALID_NAME', + // OIDC configuration has an invalid OAuth client ID. + INVALID_OAUTH_CLIENT_ID: 'INVALID_OAUTH_CLIENT_ID', + // Invalid page token. + INVALID_PAGE_SELECTION: 'INVALID_PAGE_TOKEN', + // Invalid phone number. + INVALID_PHONE_NUMBER: 'INVALID_PHONE_NUMBER', + // Invalid agent project. Either agent project doesn't exist or didn't enable multi-tenancy. + INVALID_PROJECT_ID: 'INVALID_PROJECT_ID', + // Invalid provider ID. + INVALID_PROVIDER_ID: 'INVALID_PROVIDER_ID', + // Invalid service account. + INVALID_SERVICE_ACCOUNT: 'INVALID_CREDENTIAL', + // Invalid testing phone number. + INVALID_TESTING_PHONE_NUMBER: 'INVALID_TESTING_PHONE_NUMBER', + // Invalid tenant type. + INVALID_TENANT_TYPE: 'INVALID_TENANT_TYPE', + // Missing Android package name. + MISSING_ANDROID_PACKAGE_NAME: 'MISSING_ANDROID_PACKAGE_NAME', + // Missing configuration. + MISSING_CONFIG: 'MISSING_CONFIG', + // Missing configuration identifier. + MISSING_CONFIG_ID: 'MISSING_PROVIDER_ID', + // Missing tenant display name: This can be thrown on CreateTenant and UpdateTenant. + MISSING_DISPLAY_NAME: 'MISSING_DISPLAY_NAME', + // Email is required for the specified action. For example a multi-factor user requires + // a verified email. + MISSING_EMAIL: 'MISSING_EMAIL', + // Missing iOS bundle ID. + MISSING_IOS_BUNDLE_ID: 'MISSING_IOS_BUNDLE_ID', + // Missing OIDC issuer. + MISSING_ISSUER: 'MISSING_ISSUER', + // No localId provided (deleteAccount missing localId). + MISSING_LOCAL_ID: 'MISSING_UID', + // OIDC configuration is missing an OAuth client ID. + MISSING_OAUTH_CLIENT_ID: 'MISSING_OAUTH_CLIENT_ID', + // Missing provider ID. + MISSING_PROVIDER_ID: 'MISSING_PROVIDER_ID', + // Missing SAML RP config. + MISSING_SAML_RELYING_PARTY_CONFIG: 'MISSING_SAML_RELYING_PARTY_CONFIG', + // Empty user list in uploadAccount. + MISSING_USER_ACCOUNT: 'MISSING_UID', + // Password auth disabled in console. + OPERATION_NOT_ALLOWED: 'OPERATION_NOT_ALLOWED', + // Provided credential has insufficient permissions. + PERMISSION_DENIED: 'INSUFFICIENT_PERMISSION', + // Phone number already exists. + PHONE_NUMBER_EXISTS: 'PHONE_NUMBER_ALREADY_EXISTS', + // Project not found. + PROJECT_NOT_FOUND: 'PROJECT_NOT_FOUND', + // In multi-tenancy context: project creation quota exceeded. + QUOTA_EXCEEDED: 'QUOTA_EXCEEDED', + // Currently only 5 second factors can be set on the same user. + SECOND_FACTOR_LIMIT_EXCEEDED: 'SECOND_FACTOR_LIMIT_EXCEEDED', + // Tenant not found. + TENANT_NOT_FOUND: 'TENANT_NOT_FOUND', + // Tenant ID mismatch. + TENANT_ID_MISMATCH: 'MISMATCHING_TENANT_ID', + // Token expired error. + TOKEN_EXPIRED: 'ID_TOKEN_EXPIRED', + // Continue URL provided in ActionCodeSettings has a domain that is not whitelisted. + UNAUTHORIZED_DOMAIN: 'UNAUTHORIZED_DOMAIN', + // A multi-factor user requires a supported first factor. + UNSUPPORTED_FIRST_FACTOR: 'UNSUPPORTED_FIRST_FACTOR', + // The request specified an unsupported type of second factor. + UNSUPPORTED_SECOND_FACTOR: 'UNSUPPORTED_SECOND_FACTOR', + // Operation is not supported in a multi-tenant context. + UNSUPPORTED_TENANT_OPERATION: 'UNSUPPORTED_TENANT_OPERATION', + // A verified email is required for the specified action. For example a multi-factor user + // requires a verified email. + UNVERIFIED_EMAIL: 'UNVERIFIED_EMAIL', + // User on which action is to be performed is not found. + USER_NOT_FOUND: 'USER_NOT_FOUND', + // User record is disabled. + USER_DISABLED: 'USER_DISABLED', + // Password provided is too weak. + WEAK_PASSWORD: 'INVALID_PASSWORD', + // Unrecognized reCAPTCHA action. + INVALID_RECAPTCHA_ACTION: 'INVALID_RECAPTCHA_ACTION', + // Unrecognized reCAPTCHA enforcement state. + INVALID_RECAPTCHA_ENFORCEMENT_STATE: 'INVALID_RECAPTCHA_ENFORCEMENT_STATE', + // reCAPTCHA is not enabled for account defender. + RECAPTCHA_NOT_ENABLED: 'RECAPTCHA_NOT_ENABLED' +}; + +/** + * Firebase Auth error code structure. This extends PrefixedFirebaseError. + */ +export class FirebaseAuthError extends PrefixedFirebaseError { + /** + * Creates the developer-facing error corresponding to the backend error code. + * + * @param serverErrorCode - The server error code. + * @param [message] The error message. The default message is used + * if not provided. + * @param [serverError] The error's raw server response. + * @returns The corresponding developer-facing error. + * @internal + */ + public static fromServerError( + serverErrorCode: string, + message?: string, + serverError?: RequestResponseError, + ): FirebaseAuthError { + // serverErrorCode could contain additional details: + // ERROR_CODE : Detailed message which can also contain colons + const colonSeparator = (serverErrorCode || '').indexOf(':'); + let customMessage = null; + if (colonSeparator !== -1) { + customMessage = serverErrorCode.substring(colonSeparator + 1).trim(); + serverErrorCode = serverErrorCode.substring(0, colonSeparator).trim(); + } + // If not found, default to internal error. + const clientCodeKey = AUTH_SERVER_TO_CLIENT_CODE[serverErrorCode] || 'INTERNAL_ERROR'; + const error: ErrorInfo = deepCopy((authClientErrorCode as any)[clientCodeKey]); + // Server detailed message should have highest priority. + error.message = customMessage || message || error.message; + error.cause = serverError; + error.httpResponse = serverError?.response ? toHttpResponse(serverError.response) : undefined; + return new FirebaseAuthError(error); + } + + /** + * @param info - The error code info. + * @param message - The error message. This will override the default message if provided. + */ + constructor(info: ErrorInfo, message?: string) { + // Override default message if custom message provided. + super('auth', info.code, message || info.message, info.httpResponse, info.cause); + + } +} diff --git a/src/auth/index.ts b/src/auth/index.ts index 4650b25e27..ccef8194d2 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -170,5 +170,5 @@ export { export { FirebaseAuthError, - AuthClientErrorCode, -} from '../utils/error'; + AuthErrorCode, +} from './error'; diff --git a/src/auth/project-config.ts b/src/auth/project-config.ts index 1695ec3a0d..4028dbb515 100644 --- a/src/auth/project-config.ts +++ b/src/auth/project-config.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import * as validator from '../utils/validator'; -import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; +import { authClientErrorCode, FirebaseAuthError } from './error'; import { SmsRegionsAuthConfig, SmsRegionConfig, @@ -150,7 +150,7 @@ export class ProjectConfig { private static validate(request: UpdateProjectConfigRequest): void { if (!validator.isNonNullObject(request)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"UpdateProjectConfigRequest" must be a valid non-null object.', ); } @@ -166,7 +166,7 @@ export class ProjectConfig { for (const key in request) { if (!(key in validKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, `"${key}" is not a valid UpdateProjectConfigRequest parameter.`, ); } diff --git a/src/auth/tenant-manager.ts b/src/auth/tenant-manager.ts index 19da5b4c83..654d9241f1 100644 --- a/src/auth/tenant-manager.ts +++ b/src/auth/tenant-manager.ts @@ -17,7 +17,7 @@ import * as validator from '../utils/validator'; import { App } from '../app'; import * as utils from '../utils/index'; -import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; +import { authClientErrorCode, FirebaseAuthError } from './error'; import { BaseAuth, createFirebaseTokenGenerator, SessionCookieOptions } from './base-auth'; import { Tenant, TenantServerResponse, CreateTenantRequest, UpdateTenantRequest } from './tenant'; @@ -93,7 +93,7 @@ export class TenantAwareAuth extends BaseAuth { .then((decodedClaims) => { // Validate tenant ID. if (decodedClaims.firebase.tenant !== this.tenantId) { - throw new FirebaseAuthError(AuthClientErrorCode.MISMATCHING_TENANT_ID); + throw new FirebaseAuthError(authClientErrorCode.MISMATCHING_TENANT_ID); } return decodedClaims; }); @@ -106,11 +106,11 @@ export class TenantAwareAuth extends BaseAuth { idToken: string, sessionCookieOptions: SessionCookieOptions): Promise { // Validate arguments before processing. if (!validator.isNonEmptyString(idToken)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_ID_TOKEN)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_ID_TOKEN)); } if (!validator.isNonNullObject(sessionCookieOptions) || !validator.isNumber(sessionCookieOptions.expiresIn)) { - return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_SESSION_COOKIE_DURATION)); + return Promise.reject(new FirebaseAuthError(authClientErrorCode.INVALID_SESSION_COOKIE_DURATION)); } // This will verify the ID token and then match the tenant ID before creating the session cookie. return this.verifyIdToken(idToken) @@ -127,7 +127,7 @@ export class TenantAwareAuth extends BaseAuth { return super.verifySessionCookie(sessionCookie, checkRevoked) .then((decodedClaims) => { if (decodedClaims.firebase.tenant !== this.tenantId) { - throw new FirebaseAuthError(AuthClientErrorCode.MISMATCHING_TENANT_ID); + throw new FirebaseAuthError(authClientErrorCode.MISMATCHING_TENANT_ID); } return decodedClaims; }); @@ -171,7 +171,7 @@ export class TenantManager { */ public authForTenant(tenantId: string): TenantAwareAuth { if (!validator.isNonEmptyString(tenantId)) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_TENANT_ID); + throw new FirebaseAuthError(authClientErrorCode.INVALID_TENANT_ID); } if (typeof this.tenantsMap[tenantId] === 'undefined') { this.tenantsMap[tenantId] = new TenantAwareAuth(this.app, tenantId); diff --git a/src/auth/tenant.ts b/src/auth/tenant.ts index 19812c02e6..ea46eed609 100644 --- a/src/auth/tenant.ts +++ b/src/auth/tenant.ts @@ -16,7 +16,7 @@ import * as validator from '../utils/validator'; import { deepCopy } from '../utils/deep-copy'; -import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; +import { authClientErrorCode, FirebaseAuthError } from './error'; import { EmailSignInConfig, EmailSignInConfigServerRequest, MultiFactorAuthServerConfig, @@ -258,7 +258,7 @@ export class Tenant { const label = createRequest ? 'CreateTenantRequest' : 'UpdateTenantRequest'; if (!validator.isNonNullObject(request)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, `"${label}" must be a valid non-null object.`, ); } @@ -266,7 +266,7 @@ export class Tenant { for (const key in request) { if (!(key in validKeys)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, `"${key}" is not a valid ${label} parameter.`, ); } @@ -275,7 +275,7 @@ export class Tenant { if (typeof request.displayName !== 'undefined' && !validator.isNonEmptyString(request.displayName)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, `"${label}.displayName" must be a valid non-empty string.`, ); } @@ -291,7 +291,7 @@ export class Tenant { } else if (request.testPhoneNumbers === null && createRequest) { // null allowed only for update operations. throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, `"${label}.testPhoneNumbers" must be a non-null object.`, ); } @@ -330,7 +330,7 @@ export class Tenant { const tenantId = Tenant.getTenantIdFromResourceName(response.name); if (!tenantId) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Invalid tenant response', ); } diff --git a/src/auth/token-generator.ts b/src/auth/token-generator.ts index 3230730104..4a41a50158 100644 --- a/src/auth/token-generator.ts +++ b/src/auth/token-generator.ts @@ -15,7 +15,8 @@ * limitations under the License. */ -import { AuthClientErrorCode, ErrorInfo, FirebaseAuthError } from '../utils/error'; +import { authClientErrorCode, FirebaseAuthError } from './error'; +import { ErrorInfo } from '../utils/error'; import { RequestResponseError } from '../utils/api-request'; import { CryptoSigner, CryptoSignerError, CryptoSignerErrorCode } from '../utils/crypto-signer'; @@ -99,13 +100,13 @@ export class FirebaseTokenGenerator { constructor(signer: CryptoSigner, public readonly tenantId?: string) { if (!validator.isNonNullObject(signer)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CREDENTIAL, + authClientErrorCode.INVALID_CREDENTIAL, 'INTERNAL ASSERT: Must provide a CryptoSigner to use FirebaseTokenGenerator.', ); } if (typeof this.tenantId !== 'undefined' && !validator.isNonEmptyString(this.tenantId)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '`tenantId` argument must be a non-empty string.'); } this.signer = signer; @@ -131,7 +132,7 @@ export class FirebaseTokenGenerator { } if (errorMessage) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, errorMessage); + throw new FirebaseAuthError(authClientErrorCode.INVALID_ARGUMENT, errorMessage); } const claims: {[key: string]: any} = {}; @@ -141,7 +142,7 @@ export class FirebaseTokenGenerator { if (Object.prototype.hasOwnProperty.call(developerClaims, key)) { if (BLACKLISTED_CLAIMS.indexOf(key) !== -1) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, `Developer claim "${key}" is reserved and cannot be specified.`, ); } @@ -222,22 +223,22 @@ export function handleCryptoSignerError(err: Error): Error { return FirebaseAuthError.fromServerError(errorCode, errorMsg, httpError); } - return new FirebaseAuthError(AuthClientErrorCode.INTERNAL_ERROR, + return new FirebaseAuthError(authClientErrorCode.INTERNAL_ERROR, 'Error returned from server: ' + errorResponse + '. Additionally, an ' + 'internal error occurred while attempting to extract the ' + 'errorcode from the error.' ); } - return new FirebaseAuthError(mapToAuthClientErrorCode(err.code), err.message); + return new FirebaseAuthError(mapToAuthErrorInfo(err.code), err.message); } -function mapToAuthClientErrorCode(code: string): ErrorInfo { +function mapToAuthErrorInfo(code: string): ErrorInfo { switch (code) { case CryptoSignerErrorCode.INVALID_CREDENTIAL: - return AuthClientErrorCode.INVALID_CREDENTIAL; + return authClientErrorCode.INVALID_CREDENTIAL; case CryptoSignerErrorCode.INVALID_ARGUMENT: - return AuthClientErrorCode.INVALID_ARGUMENT; + return authClientErrorCode.INVALID_ARGUMENT; default: - return AuthClientErrorCode.INTERNAL_ERROR; + return authClientErrorCode.INTERNAL_ERROR; } } diff --git a/src/auth/token-verifier.ts b/src/auth/token-verifier.ts index 64da8ac516..112b82fc32 100644 --- a/src/auth/token-verifier.ts +++ b/src/auth/token-verifier.ts @@ -14,7 +14,8 @@ * limitations under the License. */ -import { AuthClientErrorCode, FirebaseAuthError, ErrorInfo } from '../utils/error'; +import { authClientErrorCode, FirebaseAuthError } from './error'; +import { ErrorInfo } from '../utils/error'; import * as util from '../utils/index'; import * as validator from '../utils/validator'; import { @@ -274,7 +275,7 @@ export const ID_TOKEN_INFO: FirebaseTokenInfo = { verifyApiName: 'verifyIdToken()', jwtName: 'Firebase ID token', shortName: 'ID token', - expiredErrorCode: AuthClientErrorCode.ID_TOKEN_EXPIRED, + expiredErrorCode: authClientErrorCode.ID_TOKEN_EXPIRED, }; /** @@ -287,7 +288,7 @@ export const AUTH_BLOCKING_TOKEN_INFO: FirebaseTokenInfo = { verifyApiName: '_verifyAuthBlockingToken()', jwtName: 'Firebase Auth Blocking token', shortName: 'Auth Blocking token', - expiredErrorCode: AuthClientErrorCode.AUTH_BLOCKING_TOKEN_EXPIRED, + expiredErrorCode: authClientErrorCode.AUTH_BLOCKING_TOKEN_EXPIRED, }; /** @@ -300,7 +301,7 @@ export const SESSION_COOKIE_INFO: FirebaseTokenInfo = { verifyApiName: 'verifySessionCookie()', jwtName: 'Firebase session cookie', shortName: 'session cookie', - expiredErrorCode: AuthClientErrorCode.SESSION_COOKIE_EXPIRED, + expiredErrorCode: authClientErrorCode.SESSION_COOKIE_EXPIRED, }; /** @@ -336,42 +337,42 @@ export class FirebaseTokenVerifier { if (!validator.isURL(clientCertUrl)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'The provided public client certificate URL is an invalid URL.', ); } else if (!validator.isURL(issuer)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'The provided JWT issuer is an invalid URL.', ); } else if (!validator.isNonNullObject(tokenInfo)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'The provided JWT information is not an object or null.', ); } else if (!validator.isURL(tokenInfo.url)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'The provided JWT verification documentation URL is invalid.', ); } else if (!validator.isNonEmptyString(tokenInfo.verifyApiName)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'The JWT verify API name must be a non-empty string.', ); } else if (!validator.isNonEmptyString(tokenInfo.jwtName)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'The JWT public full name must be a non-empty string.', ); } else if (!validator.isNonEmptyString(tokenInfo.shortName)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'The JWT public short name must be a non-empty string.', ); } else if (!validator.isNonNullObject(tokenInfo.expiredErrorCode) || !('code' in tokenInfo.expiredErrorCode)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'The JWT expiration error code must be a non-null ErrorInfo object.', ); } @@ -393,7 +394,7 @@ export class FirebaseTokenVerifier { public verifyJWT(jwtToken: string, isEmulator = false): Promise { if (!validator.isString(jwtToken)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, `First argument to ${this.tokenInfo.verifyApiName} must be a ${this.tokenInfo.jwtName} string.`, ); } @@ -417,7 +418,7 @@ export class FirebaseTokenVerifier { audience: string | undefined): Promise { if (!validator.isString(jwtToken)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, `First argument to ${this.tokenInfo.verifyApiName} must be a ${this.tokenInfo.jwtName} string.`, ); } @@ -441,7 +442,7 @@ export class FirebaseTokenVerifier { .then((projectId) => { if (!validator.isNonEmptyString(projectId)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_CREDENTIAL, + authClientErrorCode.INVALID_CREDENTIAL, 'Must initialize app with a cert credential or set your Firebase project ID as the ' + `GOOGLE_CLOUD_PROJECT environment variable to call ${this.tokenInfo.verifyApiName}.`, ); @@ -472,10 +473,10 @@ export class FirebaseTokenVerifier { const errorMessage = `Decoding ${this.tokenInfo.jwtName} failed. Make sure you passed ` + `the entire string JWT which represents ${this.shortNameArticle} ` + `${this.tokenInfo.shortName}.` + verifyJwtTokenDocsMessage; - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, + throw new FirebaseAuthError(authClientErrorCode.INVALID_ARGUMENT, errorMessage); } - throw new FirebaseAuthError(AuthClientErrorCode.INTERNAL_ERROR, err.message); + throw new FirebaseAuthError(authClientErrorCode.INTERNAL_ERROR, err.message); }); } @@ -544,7 +545,7 @@ export class FirebaseTokenVerifier { } } if (errorMessage) { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, errorMessage); + throw new FirebaseAuthError(authClientErrorCode.INVALID_ARGUMENT, errorMessage); } } @@ -573,14 +574,14 @@ export class FirebaseTokenVerifier { return new FirebaseAuthError(this.tokenInfo.expiredErrorCode, errorMessage); } else if (error.code === JwtErrorCode.INVALID_SIGNATURE) { const errorMessage = `${this.tokenInfo.jwtName} has invalid signature.` + verifyJwtTokenDocsMessage; - return new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, errorMessage); + return new FirebaseAuthError(authClientErrorCode.INVALID_ARGUMENT, errorMessage); } else if (error.code === JwtErrorCode.NO_MATCHING_KID) { const errorMessage = `${this.tokenInfo.jwtName} has "kid" claim which does not ` + `correspond to a known public key. Most likely the ${this.tokenInfo.shortName} ` + 'is expired, so get a fresh token from your client app and try again.'; - return new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, errorMessage); + return new FirebaseAuthError(authClientErrorCode.INVALID_ARGUMENT, errorMessage); } - return new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, error.message); + return new FirebaseAuthError(authClientErrorCode.INVALID_ARGUMENT, error.message); } } diff --git a/src/auth/user-import-builder.ts b/src/auth/user-import-builder.ts index f396066b49..4554187b10 100644 --- a/src/auth/user-import-builder.ts +++ b/src/auth/user-import-builder.ts @@ -18,7 +18,7 @@ import { FirebaseArrayIndexError } from '../app/index'; import { deepCopy, deepExtend } from '../utils/deep-copy'; import * as utils from '../utils'; import * as validator from '../utils/validator'; -import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; +import { authClientErrorCode, FirebaseAuthError } from './error'; import { UpdateMultiFactorInfoRequest, UpdatePhoneMultiFactorInfoRequest, MultiFactorUpdateSettings } from './auth-config'; @@ -329,7 +329,7 @@ export function convertMultiFactorInfoToServerFormat(multiFactorInfo: UpdateMult enrolledAt = new Date(multiFactorInfo.enrollmentTime).toISOString(); } else { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ENROLLMENT_TIME, + authClientErrorCode.INVALID_ENROLLMENT_TIME, `The second factor "enrollmentTime" for "${multiFactorInfo.uid}" must be a valid ` + 'UTC date string.'); } @@ -353,7 +353,7 @@ export function convertMultiFactorInfoToServerFormat(multiFactorInfo: UpdateMult } else { // Unsupported second factor. throw new FirebaseAuthError( - AuthClientErrorCode.UNSUPPORTED_SECOND_FACTOR, + authClientErrorCode.UNSUPPORTED_SECOND_FACTOR, `Unsupported second factor "${JSON.stringify(multiFactorInfo)}" provided.`); } } @@ -401,7 +401,7 @@ function populateUploadAccountUser( if (typeof user.passwordHash !== 'undefined') { if (!validator.isBuffer(user.passwordHash)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_PASSWORD_HASH, + authClientErrorCode.INVALID_PASSWORD_HASH, ); } result.passwordHash = utils.toWebSafeBase64(user.passwordHash); @@ -409,7 +409,7 @@ function populateUploadAccountUser( if (typeof user.passwordSalt !== 'undefined') { if (!validator.isBuffer(user.passwordSalt)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_PASSWORD_SALT, + authClientErrorCode.INVALID_PASSWORD_SALT, ); } result.salt = utils.toWebSafeBase64(user.passwordSalt); @@ -527,7 +527,7 @@ export class UserImportBuilder { // Map backend request index to original developer provided array index. index: this.indexMap[failedUpload.index], error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_USER_IMPORT, + authClientErrorCode.INVALID_USER_IMPORT, failedUpload.message, ), }); @@ -555,20 +555,20 @@ export class UserImportBuilder { } if (!validator.isNonNullObject(options)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"UserImportOptions" are required when importing users with passwords.', ); } if (!validator.isNonNullObject(options.hash)) { throw new FirebaseAuthError( - AuthClientErrorCode.MISSING_HASH_ALGORITHM, + authClientErrorCode.MISSING_HASH_ALGORITHM, '"hash.algorithm" is missing from the provided "UserImportOptions".', ); } if (typeof options.hash.algorithm === 'undefined' || !validator.isNonEmptyString(options.hash.algorithm)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_ALGORITHM, + authClientErrorCode.INVALID_HASH_ALGORITHM, '"hash.algorithm" must be a string matching the list of supported algorithms.', ); } @@ -581,7 +581,7 @@ export class UserImportBuilder { case 'HMAC_MD5': if (!validator.isBuffer(options.hash.key)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_KEY, + authClientErrorCode.INVALID_HASH_KEY, 'A non-empty "hash.key" byte buffer must be provided for ' + `hash algorithm ${options.hash.algorithm}.`, ); @@ -601,7 +601,7 @@ export class UserImportBuilder { const minRounds = options.hash.algorithm === 'MD5' ? 0 : 1; if (isNaN(rounds) || rounds < minRounds || rounds > 8192) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_ROUNDS, + authClientErrorCode.INVALID_HASH_ROUNDS, `A valid "hash.rounds" number between ${minRounds} and 8192 must be provided for ` + `hash algorithm ${options.hash.algorithm}.`, ); @@ -617,7 +617,7 @@ export class UserImportBuilder { rounds = getNumberField(options.hash, 'rounds'); if (isNaN(rounds) || rounds < 0 || rounds > 120000) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_ROUNDS, + authClientErrorCode.INVALID_HASH_ROUNDS, 'A valid "hash.rounds" number between 0 and 120000 must be provided for ' + `hash algorithm ${options.hash.algorithm}.`, ); @@ -631,7 +631,7 @@ export class UserImportBuilder { case 'SCRYPT': { if (!validator.isBuffer(options.hash.key)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_KEY, + authClientErrorCode.INVALID_HASH_KEY, 'A "hash.key" byte buffer must be provided for ' + `hash algorithm ${options.hash.algorithm}.`, ); @@ -639,7 +639,7 @@ export class UserImportBuilder { rounds = getNumberField(options.hash, 'rounds'); if (isNaN(rounds) || rounds <= 0 || rounds > 8) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_ROUNDS, + authClientErrorCode.INVALID_HASH_ROUNDS, 'A valid "hash.rounds" number between 1 and 8 must be provided for ' + `hash algorithm ${options.hash.algorithm}.`, ); @@ -647,7 +647,7 @@ export class UserImportBuilder { const memoryCost = getNumberField(options.hash, 'memoryCost'); if (isNaN(memoryCost) || memoryCost <= 0 || memoryCost > 14) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_MEMORY_COST, + authClientErrorCode.INVALID_HASH_MEMORY_COST, 'A valid "hash.memoryCost" number between 1 and 14 must be provided for ' + `hash algorithm ${options.hash.algorithm}.`, ); @@ -655,7 +655,7 @@ export class UserImportBuilder { if (typeof options.hash.saltSeparator !== 'undefined' && !validator.isBuffer(options.hash.saltSeparator)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_SALT_SEPARATOR, + authClientErrorCode.INVALID_HASH_SALT_SEPARATOR, '"hash.saltSeparator" must be a byte buffer.', ); } @@ -678,7 +678,7 @@ export class UserImportBuilder { const cpuMemCost = getNumberField(options.hash, 'memoryCost'); if (isNaN(cpuMemCost)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_MEMORY_COST, + authClientErrorCode.INVALID_HASH_MEMORY_COST, 'A valid "hash.memoryCost" number must be provided for ' + `hash algorithm ${options.hash.algorithm}.`, ); @@ -686,7 +686,7 @@ export class UserImportBuilder { const parallelization = getNumberField(options.hash, 'parallelization'); if (isNaN(parallelization)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_PARALLELIZATION, + authClientErrorCode.INVALID_HASH_PARALLELIZATION, 'A valid "hash.parallelization" number must be provided for ' + `hash algorithm ${options.hash.algorithm}.`, ); @@ -694,7 +694,7 @@ export class UserImportBuilder { const blockSize = getNumberField(options.hash, 'blockSize'); if (isNaN(blockSize)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_BLOCK_SIZE, + authClientErrorCode.INVALID_HASH_BLOCK_SIZE, 'A valid "hash.blockSize" number must be provided for ' + `hash algorithm ${options.hash.algorithm}.`, ); @@ -702,7 +702,7 @@ export class UserImportBuilder { const dkLen = getNumberField(options.hash, 'derivedKeyLength'); if (isNaN(dkLen)) { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_DERIVED_KEY_LENGTH, + authClientErrorCode.INVALID_HASH_DERIVED_KEY_LENGTH, 'A valid "hash.derivedKeyLength" number must be provided for ' + `hash algorithm ${options.hash.algorithm}.`, ); @@ -718,7 +718,7 @@ export class UserImportBuilder { } default: throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_ALGORITHM, + authClientErrorCode.INVALID_HASH_ALGORITHM, `Unsupported hash algorithm provider "${options.hash.algorithm}".`, ); } diff --git a/src/auth/user-record.ts b/src/auth/user-record.ts index 2310968849..1807ffd01c 100644 --- a/src/auth/user-record.ts +++ b/src/auth/user-record.ts @@ -18,7 +18,7 @@ import { deepCopy } from '../utils/deep-copy'; import { isNonNullObject } from '../utils/validator'; import * as utils from '../utils'; -import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error'; +import { authClientErrorCode, FirebaseAuthError } from './error'; /** * 'REDACTED', encoded as a base64 string. @@ -188,7 +188,7 @@ export abstract class MultiFactorInfo { const factorId = response && this.getFactorId(response); if (!factorId || !response || !response.mfaEnrollmentId) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Invalid multi-factor info response'); } utils.addReadonlyGetter(this, 'uid', response.mfaEnrollmentId); @@ -330,7 +330,7 @@ export class MultiFactorSettings { const parsedEnrolledFactors: MultiFactorInfo[] = []; if (!isNonNullObject(response)) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Invalid multi-factor response'); } else if (response.mfaInfo) { response.mfaInfo.forEach((factorResponse) => { @@ -457,7 +457,7 @@ export class UserInfo { // Provider user id and provider id are required. if (!response.rawId || !response.providerId) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Invalid user info response'); } @@ -592,7 +592,7 @@ export class UserRecord { // The Firebase user id is required. if (!response.localId) { throw new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Invalid user response'); } diff --git a/src/data-connect/data-connect-api-client-internal.ts b/src/data-connect/data-connect-api-client-internal.ts index 37df364207..cc96f3b4fd 100644 --- a/src/data-connect/data-connect-api-client-internal.ts +++ b/src/data-connect/data-connect-api-client-internal.ts @@ -20,7 +20,8 @@ import { FirebaseApp } from '../app/firebase-app'; import { HttpRequestConfig, HttpClient, RequestResponseError, AuthorizedHttpClient } from '../utils/api-request'; -import { PrefixedFirebaseError, ErrorInfo, toHttpResponse } from '../utils/error'; +import { PrefixedFirebaseError, toHttpResponse } from '../utils/error'; +import { FirebaseDataConnectError, DataConnectErrorCode, DATA_CONNECT_ERROR_CODE_MAPPING } from './error'; import * as utils from '../utils/index'; import * as validator from '../utils/validator'; import { ConnectorConfig, ExecuteGraphqlResponse, GraphqlOptions, OperationOptions } from './data-connect-api'; @@ -678,43 +679,3 @@ interface ServerError { message?: string; status?: string; } - -export const DATA_CONNECT_ERROR_CODE_MAPPING: { [key: string]: DataConnectErrorCode } = { - ABORTED: 'aborted', - INVALID_ARGUMENT: 'invalid-argument', - INVALID_CREDENTIAL: 'invalid-credential', - INTERNAL: 'internal-error', - PERMISSION_DENIED: 'permission-denied', - UNAUTHENTICATED: 'unauthenticated', - NOT_FOUND: 'not-found', - UNKNOWN: 'unknown-error', - QUERY_ERROR: 'query-error', -}; - -/** - * Data Connect client error codes and their default messages. - */ -export type DataConnectErrorCode = - 'aborted' - | 'invalid-argument' - | 'invalid-credential' - | 'internal-error' - | 'permission-denied' - | 'unauthenticated' - | 'not-found' - | 'unknown-error' - | 'query-error'; - -/** - * Firebase Data Connect error type. This extends PrefixedFirebaseError. - */ -export class FirebaseDataConnectError extends PrefixedFirebaseError { - /** - * @param info - The error code info. - * @param message - The error message. If provided, this will override the default message. - */ - constructor(info: ErrorInfo, message?: string) { - super('data-connect', info.code, message || info.message, info.httpResponse, info.cause); - - } -} diff --git a/src/data-connect/error.ts b/src/data-connect/error.ts new file mode 100644 index 0000000000..f20ac6cd48 --- /dev/null +++ b/src/data-connect/error.ts @@ -0,0 +1,63 @@ +/*! + * Copyright 2026 Google LLC + * + * 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 { PrefixedFirebaseError, ErrorInfo } from '../utils/error'; + +/** @const {Record} Data Connect server to client error code mapping. */ +export const DATA_CONNECT_ERROR_CODE_MAPPING: Record = { + ABORTED: 'aborted', + INVALID_ARGUMENT: 'invalid-argument', + INVALID_CREDENTIAL: 'invalid-credential', + INTERNAL: 'internal-error', + PERMISSION_DENIED: 'permission-denied', + UNAUTHENTICATED: 'unauthenticated', + NOT_FOUND: 'not-found', + UNKNOWN: 'unknown-error', + QUERY_ERROR: 'query-error', +}; + +/** + * The constant mapping for valid Data Connect client error codes. + */ +export const DataConnectErrorCode = { + ABORTED: 'aborted', + INVALID_ARGUMENT: 'invalid-argument', + INVALID_CREDENTIAL: 'invalid-credential', + INTERNAL: 'internal-error', + PERMISSION_DENIED: 'permission-denied', + UNAUTHENTICATED: 'unauthenticated', + NOT_FOUND: 'not-found', + UNKNOWN: 'unknown-error', + QUERY_ERROR: 'query-error', +} as const; + +/** + * The type definition for valid Data Connect client error codes. + */ +export type DataConnectErrorCode = typeof DataConnectErrorCode[keyof typeof DataConnectErrorCode]; + +/** + * Firebase Data Connect error type. This extends PrefixedFirebaseError. + */ +export class FirebaseDataConnectError extends PrefixedFirebaseError { + /** + * @param info - The error code info. + * @param message - The error message. If provided, this will override the default message. + */ + constructor(info: ErrorInfo, message?: string) { + super('data-connect', info.code, message || info.message, info.httpResponse, info.cause); + } +} diff --git a/src/data-connect/index.ts b/src/data-connect/index.ts index b2abe7aaa1..a8adac447f 100644 --- a/src/data-connect/index.ts +++ b/src/data-connect/index.ts @@ -90,4 +90,4 @@ export function getDataConnect(connectorConfig: ConnectorConfig, app?: App): Dat */ export const validateAdminArgs = _validateAdminArgs; -export { FirebaseDataConnectError, DataConnectErrorCode } from './data-connect-api-client-internal'; \ No newline at end of file +export { FirebaseDataConnectError, DataConnectErrorCode } from './error'; \ No newline at end of file diff --git a/src/data-connect/validate-admin-args.ts b/src/data-connect/validate-admin-args.ts index d8afa8a58d..8c4a210e0e 100644 --- a/src/data-connect/validate-admin-args.ts +++ b/src/data-connect/validate-admin-args.ts @@ -21,7 +21,7 @@ import { ConnectorConfig, OperationOptions } from './data-connect-api'; import { DATA_CONNECT_ERROR_CODE_MAPPING, FirebaseDataConnectError, -} from './data-connect-api-client-internal'; +} from './error'; /** * @internal diff --git a/src/database/database.ts b/src/database/database.ts index 34f1327c7a..4abea5f7a2 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -18,7 +18,9 @@ import { URL } from 'url'; import * as path from 'path'; import { FirebaseDatabase } from '@firebase/database-types'; -import { FirebaseDatabaseError, AppErrorCodes, FirebaseAppError, toHttpResponse } from '../utils/error'; +import { FirebaseDatabaseError } from './error'; +import { AppErrorCode, FirebaseAppError } from '../app/error'; +import { toHttpResponse } from '../utils/error'; import { Database as DatabaseImpl } from '@firebase/database-compat/standalone'; import { App } from '../app'; @@ -226,7 +228,7 @@ class DatabaseRulesClient { .then((resp) => { if (!resp.text) { throw new FirebaseAppError({ - code: AppErrorCodes.INTERNAL_ERROR, + code: AppErrorCode.INTERNAL_ERROR, message: 'HTTP response missing data.' }); } @@ -297,7 +299,7 @@ class DatabaseRulesClient { private handleError(err: Error): Error { if (err instanceof RequestResponseError) { return new FirebaseDatabaseError({ - code: AppErrorCodes.INTERNAL_ERROR, + code: AppErrorCode.INTERNAL_ERROR, message: this.getErrorMessage(err), httpResponse: toHttpResponse(err.response), cause: err, diff --git a/src/database/error.ts b/src/database/error.ts new file mode 100644 index 0000000000..15bbd45bea --- /dev/null +++ b/src/database/error.ts @@ -0,0 +1,32 @@ +/*! + * Copyright 2026 Google LLC + * + * 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 { PrefixedFirebaseError, ErrorInfo } from '../utils/error'; + +/** + * Firebase Database error code structure. This extends PrefixedFirebaseError. + */ +export class FirebaseDatabaseError extends PrefixedFirebaseError { + /** + * @param info - The error code info. + * @param message - The error message. This will override the default + * message if provided. + */ + constructor(info: ErrorInfo, message?: string) { + // Override default message if custom message provided. + super('database', info.code, message || info.message, info.httpResponse, info.cause); + } +} diff --git a/src/database/index.ts b/src/database/index.ts index c078893d2c..747633f819 100644 --- a/src/database/index.ts +++ b/src/database/index.ts @@ -126,4 +126,4 @@ function getDatabaseInstance(options: { url?: string; app?: App }): Database { return dbService.getDatabase(options.url); } -export { FirebaseDatabaseError } from '../utils/error'; +export { FirebaseDatabaseError } from './error'; diff --git a/src/eventarc/error.ts b/src/eventarc/error.ts new file mode 100644 index 0000000000..6d938af093 --- /dev/null +++ b/src/eventarc/error.ts @@ -0,0 +1,43 @@ +/*! + * Copyright 2026 Google LLC + * + * 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 { PrefixedFirebaseError, ErrorInfo } from '../utils/error'; + +/** + * The constant mapping for valid Eventarc client error codes. + */ +export const EventarcErrorCode = { + UNKNOWN_ERROR: 'unknown-error', + INVALID_ARGUMENT: 'invalid-argument', +} as const; + +/** + * The type definition for valid Eventarc client error codes. + */ +export type EventarcErrorCode = typeof EventarcErrorCode[keyof typeof EventarcErrorCode]; + +/** + * Firebase Eventarc error code structure. This extends PrefixedFirebaseError. + */ +export class FirebaseEventarcError extends PrefixedFirebaseError { + /** + * @param info - The error code info. + * @param message - The error message. If provided, this will override the default message. + */ + constructor(info: ErrorInfo, message?: string) { + super('eventarc', info.code, message || info.message, info.httpResponse, info.cause); + } +} diff --git a/src/eventarc/eventarc-client-internal.ts b/src/eventarc/eventarc-client-internal.ts index 2ae25611d8..4c517343b1 100644 --- a/src/eventarc/eventarc-client-internal.ts +++ b/src/eventarc/eventarc-client-internal.ts @@ -16,7 +16,8 @@ */ import * as validator from '../utils/validator'; -import { FirebaseEventarcError, toCloudEventProtoFormat } from './eventarc-utils'; +import { toCloudEventProtoFormat } from './eventarc-utils'; +import { FirebaseEventarcError } from './error'; import { App } from '../app'; import { Channel } from './eventarc'; import { diff --git a/src/eventarc/eventarc-utils.ts b/src/eventarc/eventarc-utils.ts index 2338b8660f..a57894157a 100644 --- a/src/eventarc/eventarc-utils.ts +++ b/src/eventarc/eventarc-utils.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { PrefixedFirebaseError, ErrorInfo } from '../utils/error'; +import { FirebaseEventarcError } from './error'; import { CloudEvent } from './cloudevent'; import { v4 as uuid } from 'uuid'; import * as validator from '../utils/validator'; @@ -25,25 +25,6 @@ import * as validator from '../utils/validator'; const TOP_LEVEL_CE_ATTRS: string[] = ['id', 'type', 'specversion', 'source', 'data', 'time', 'datacontenttype', 'subject']; -/** - * Eventarc client error codes and their default messages. - */ -export type EventarcErrorCode = 'unknown-error' | 'invalid-argument' - -/** - * Firebase Eventarc error code structure. This extends PrefixedFirebaseError. - */ -export class FirebaseEventarcError extends PrefixedFirebaseError { - /** - * @param info - The error code info. - * @param message - The error message. If provided, this will override the default message. - */ - constructor(info: ErrorInfo, message?: string) { - super('eventarc', info.code, message || info.message, info.httpResponse, info.cause); - - } -} - export function toCloudEventProtoFormat(ce: CloudEvent): any { const source = ce.source ?? process.env.EVENTARC_CLOUD_EVENT_SOURCE; if (typeof source === 'undefined' || !validator.isNonEmptyString(source)) { diff --git a/src/eventarc/eventarc.ts b/src/eventarc/eventarc.ts index 36419175c2..6276248ccb 100644 --- a/src/eventarc/eventarc.ts +++ b/src/eventarc/eventarc.ts @@ -17,7 +17,7 @@ import { App } from '../app'; import * as validator from '../utils/validator'; -import { FirebaseEventarcError } from './eventarc-utils'; +import { FirebaseEventarcError } from './error'; import { CloudEvent } from './cloudevent'; import { EventarcApiClient } from './eventarc-client-internal'; diff --git a/src/eventarc/index.ts b/src/eventarc/index.ts index a3a49cde74..6e8a18720a 100644 --- a/src/eventarc/index.ts +++ b/src/eventarc/index.ts @@ -64,4 +64,4 @@ export function getEventarc(app?: App): Eventarc { return firebaseApp.getOrInitService('eventarc', (app) => new Eventarc(app)); } -export { FirebaseEventarcError, EventarcErrorCode } from './eventarc-utils'; \ No newline at end of file +export { FirebaseEventarcError, EventarcErrorCode } from './error'; \ No newline at end of file diff --git a/src/extensions/error.ts b/src/extensions/error.ts new file mode 100644 index 0000000000..14b7f024ac --- /dev/null +++ b/src/extensions/error.ts @@ -0,0 +1,46 @@ +/*! + * Copyright 2026 Google LLC + * + * 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 { PrefixedFirebaseError, ErrorInfo } from '../utils/error'; + +/** + * The constant mapping for valid Extensions client error codes. + */ +export const ExtensionsErrorCode = { + INVALID_ARGUMENT: 'invalid-argument', + NOT_FOUND: 'not-found', + FORBIDDEN: 'forbidden', + INTERNAL_ERROR: 'internal-error', + UNKNOWN_ERROR: 'unknown-error', +} as const; + +/** + * The type definition for valid Extensions client error codes. + */ +export type ExtensionsErrorCode = typeof ExtensionsErrorCode[keyof typeof ExtensionsErrorCode]; + +/** + * Firebase Extensions error code structure. This extends PrefixedFirebaseError. + */ +export class FirebaseExtensionsError extends PrefixedFirebaseError { + /** + * @param info - The error code info. + * @param message - The error message. If provided, this will override the default message. + */ + constructor(info: ErrorInfo, message?: string) { + super('Extensions', info.code, message || info.message, info.httpResponse, info.cause); + } +} diff --git a/src/extensions/extensions-api-client-internal.ts b/src/extensions/extensions-api-client-internal.ts index 5b33d0448e..e89915f0bb 100644 --- a/src/extensions/extensions-api-client-internal.ts +++ b/src/extensions/extensions-api-client-internal.ts @@ -18,7 +18,9 @@ import { App } from '../app'; import { FirebaseApp } from '../app/firebase-app'; import { AuthorizedHttpClient, HttpClient, RequestResponseError, HttpRequestConfig } from '../utils/api-request'; -import { FirebaseAppError, PrefixedFirebaseError, ErrorInfo, toHttpResponse } from '../utils/error'; +import { PrefixedFirebaseError, toHttpResponse } from '../utils/error'; +import { FirebaseExtensionsError } from './error'; +import { FirebaseAppError } from '../app/error'; import * as validator from '../utils/validator'; import * as utils from '../utils'; @@ -152,21 +154,3 @@ type State = 'STATE_UNSPECIFIED' | 'PROCESSING_COMPLETE' | 'PROCESSING_WARNING' | 'PROCESSING_FAILED'; - -/** - * Extensions client error codes and their default messages. - */ -export type ExtensionsErrorCode = 'invalid-argument' | 'not-found' | 'forbidden' | 'internal-error' | 'unknown-error'; -/** - * Firebase Extensions error code structure. This extends PrefixedFirebaseError. - */ -export class FirebaseExtensionsError extends PrefixedFirebaseError { - /** - * @param info - The error code info. - * @param message - The error message. If provided, this will override the default message. - */ - constructor(info: ErrorInfo, message?: string) { - super('Extensions', info.code, message || info.message, info.httpResponse, info.cause); - - } -} diff --git a/src/extensions/extensions.ts b/src/extensions/extensions.ts index 63e090f0ca..3c99bc62a5 100644 --- a/src/extensions/extensions.ts +++ b/src/extensions/extensions.ts @@ -17,7 +17,8 @@ import { App } from '../app'; import { SettableProcessingState } from './extensions-api'; -import { ExtensionsApiClient, FirebaseExtensionsError } from './extensions-api-client-internal'; +import { ExtensionsApiClient } from './extensions-api-client-internal'; +import { FirebaseExtensionsError } from './error'; import * as validator from '../utils/validator'; /** diff --git a/src/extensions/index.ts b/src/extensions/index.ts index 645f0e22e1..04955071db 100644 --- a/src/extensions/index.ts +++ b/src/extensions/index.ts @@ -63,4 +63,4 @@ export function getExtensions(app?: App): Extensions { return firebaseApp.getOrInitService('extensions', (app) => new Extensions(app)); } -export { FirebaseExtensionsError, ExtensionsErrorCode } from './extensions-api-client-internal'; \ No newline at end of file +export { FirebaseExtensionsError, ExtensionsErrorCode } from './error'; \ No newline at end of file diff --git a/src/firestore/error.ts b/src/firestore/error.ts new file mode 100644 index 0000000000..6207cbda31 --- /dev/null +++ b/src/firestore/error.ts @@ -0,0 +1,32 @@ +/*! + * Copyright 2026 Google LLC + * + * 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 { PrefixedFirebaseError, ErrorInfo } from '../utils/error'; + +/** + * Firebase Firestore error code structure. This extends PrefixedFirebaseError. + */ +export class FirebaseFirestoreError extends PrefixedFirebaseError { + /** + * @param info - The error code info. + * @param message - The error message. This will override the default + * message if provided. + */ + constructor(info: ErrorInfo, message?: string) { + // Override default message if custom message provided. + super('firestore', info.code, message || info.message, info.httpResponse, info.cause); + } +} diff --git a/src/firestore/firestore-internal.ts b/src/firestore/firestore-internal.ts index e6b6544833..3642521943 100644 --- a/src/firestore/firestore-internal.ts +++ b/src/firestore/firestore-internal.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { FirebaseFirestoreError } from '../utils/error'; +import { FirebaseFirestoreError } from './error'; import { ServiceAccountCredential, isApplicationDefault } from '../app/credential-internal'; import { Firestore, Settings } from '@google-cloud/firestore'; diff --git a/src/firestore/index.ts b/src/firestore/index.ts index c41fd5ea1a..5fe0c3f803 100644 --- a/src/firestore/index.ts +++ b/src/firestore/index.ts @@ -221,4 +221,4 @@ export function initializeFirestore( return firestoreService.initializeDatabase(databaseId, settings); } -export { FirebaseFirestoreError } from '../utils/error'; +export { FirebaseFirestoreError } from './error'; diff --git a/src/functions/error.ts b/src/functions/error.ts new file mode 100644 index 0000000000..a59e00d3d1 --- /dev/null +++ b/src/functions/error.ts @@ -0,0 +1,64 @@ +/*! + * Copyright 2026 Google LLC + * + * 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 { PrefixedFirebaseError, ErrorInfo } from '../utils/error'; + +/** @const {Record} Functions server to client error code mapping. */ +export const FUNCTIONS_ERROR_CODE_MAPPING: Record = { + ABORTED: 'aborted', + INVALID_ARGUMENT: 'invalid-argument', + INVALID_CREDENTIAL: 'invalid-credential', + INTERNAL: 'internal-error', + FAILED_PRECONDITION: 'failed-precondition', + PERMISSION_DENIED: 'permission-denied', + UNAUTHENTICATED: 'unauthenticated', + NOT_FOUND: 'not-found', + UNKNOWN: 'unknown-error', +}; + +/** + * The constant mapping for valid Functions client error codes. + */ +export const FunctionsErrorCode = { + ABORTED: 'aborted', + INVALID_ARGUMENT: 'invalid-argument', + INVALID_CREDENTIAL: 'invalid-credential', + INTERNAL_ERROR: 'internal-error', + FAILED_PRECONDITION: 'failed-precondition', + PERMISSION_DENIED: 'permission-denied', + UNAUTHENTICATED: 'unauthenticated', + NOT_FOUND: 'not-found', + UNKNOWN_ERROR: 'unknown-error', + TASK_ALREADY_EXISTS: 'task-already-exists', +} as const; + +/** + * The type definition for valid Functions client error codes. + */ +export type FunctionsErrorCode = typeof FunctionsErrorCode[keyof typeof FunctionsErrorCode]; + +/** + * Firebase Functions error code structure. This extends PrefixedFirebaseError. + */ +export class FirebaseFunctionsError extends PrefixedFirebaseError { + /** + * @param info - The error code info. + * @param message - The error message. If provided, this will override the default message. + */ + constructor(info: ErrorInfo, message?: string) { + super('functions', info.code, message || info.message, info.httpResponse, info.cause); + } +} diff --git a/src/functions/functions-api-client-internal.ts b/src/functions/functions-api-client-internal.ts index 473eb2602a..34e55e5ddc 100644 --- a/src/functions/functions-api-client-internal.ts +++ b/src/functions/functions-api-client-internal.ts @@ -20,7 +20,8 @@ import { FirebaseApp } from '../app/firebase-app'; import { HttpRequestConfig, HttpClient, RequestResponseError, AuthorizedHttpClient } from '../utils/api-request'; -import { PrefixedFirebaseError, ErrorInfo, toHttpResponse } from '../utils/error'; +import { PrefixedFirebaseError, toHttpResponse } from '../utils/error'; +import { FirebaseFunctionsError, FunctionsErrorCode, FUNCTIONS_ERROR_CODE_MAPPING } from './error'; import * as utils from '../utils/index'; import * as validator from '../utils/validator'; import { TaskOptions } from './functions-api'; @@ -447,47 +448,6 @@ export interface Task { }; } -export const FUNCTIONS_ERROR_CODE_MAPPING: { [key: string]: FunctionsErrorCode } = { - ABORTED: 'aborted', - INVALID_ARGUMENT: 'invalid-argument', - INVALID_CREDENTIAL: 'invalid-credential', - INTERNAL: 'internal-error', - FAILED_PRECONDITION: 'failed-precondition', - PERMISSION_DENIED: 'permission-denied', - UNAUTHENTICATED: 'unauthenticated', - NOT_FOUND: 'not-found', - UNKNOWN: 'unknown-error', -}; - -/** - * Functions client error codes and their default messages. - */ -export type FunctionsErrorCode = - 'aborted' - | 'invalid-argument' - | 'invalid-credential' - | 'internal-error' - | 'failed-precondition' - | 'permission-denied' - | 'unauthenticated' - | 'not-found' - | 'unknown-error' - | 'task-already-exists'; - -/** - * Firebase Functions error code structure. This extends PrefixedFirebaseError. - */ -export class FirebaseFunctionsError extends PrefixedFirebaseError { - /** - * @param info - The error code info. - * @param message - The error message. If provided, this will override the default message. - */ - constructor(info: ErrorInfo, message?: string) { - super('functions', info.code, message || info.message, info.httpResponse, info.cause); - - } -} - function tasksEmulatorUrl(resources: utils.ParsedResource): string | undefined { if (process.env.CLOUD_TASKS_EMULATOR_HOST) { return `http://${process.env.CLOUD_TASKS_EMULATOR_HOST}/projects/${resources.projectId}/locations/${resources.locationId}/queues/${resources.resourceId}/tasks`; diff --git a/src/functions/functions.ts b/src/functions/functions.ts index 0adc0fdac3..805654aaa6 100644 --- a/src/functions/functions.ts +++ b/src/functions/functions.ts @@ -16,7 +16,8 @@ */ import { App } from '../app'; -import { FirebaseFunctionsError, FunctionsApiClient } from './functions-api-client-internal'; +import { FunctionsApiClient } from './functions-api-client-internal'; +import { FirebaseFunctionsError } from './error'; import { TaskOptions } from './functions-api'; import * as validator from '../utils/validator'; diff --git a/src/functions/index.ts b/src/functions/index.ts index 5520f14f5a..0894d244a6 100644 --- a/src/functions/index.ts +++ b/src/functions/index.ts @@ -72,4 +72,4 @@ export function getFunctions(app?: App): Functions { return firebaseApp.getOrInitService('functions', (app) => new Functions(app)); } -export { FirebaseFunctionsError, FunctionsErrorCode } from './functions-api-client-internal'; \ No newline at end of file +export { FirebaseFunctionsError, FunctionsErrorCode } from './error'; \ No newline at end of file diff --git a/src/installations/error.ts b/src/installations/error.ts new file mode 100644 index 0000000000..8f53d5c874 --- /dev/null +++ b/src/installations/error.ts @@ -0,0 +1,70 @@ +/*! + * Copyright 2026 Google LLC + * + * 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 { PrefixedFirebaseError, ErrorInfo } from '../utils/error'; + +/** + * The constant mapping for valid Installations client error codes. + */ +export const InstallationsErrorCode = { + INVALID_ARGUMENT: 'invalid-argument', + INVALID_PROJECT_ID: 'invalid-project-id', + INVALID_INSTALLATION_ID: 'invalid-installation-id', + API_ERROR: 'api-error', +} as const; + +/** + * The type definition for valid Installations client error codes. + */ +export type InstallationsErrorCode = typeof InstallationsErrorCode[keyof typeof InstallationsErrorCode]; + +/** + * Internal Installations client error code mapping used to construct ErrorInfo. + */ +export const installationsClientErrorCode: { readonly [K in keyof typeof InstallationsErrorCode]: ErrorInfo } = { + INVALID_ARGUMENT: { + code: InstallationsErrorCode.INVALID_ARGUMENT, + message: 'Invalid argument provided.', + }, + INVALID_PROJECT_ID: { + code: InstallationsErrorCode.INVALID_PROJECT_ID, + message: 'Invalid project ID provided.', + }, + INVALID_INSTALLATION_ID: { + code: InstallationsErrorCode.INVALID_INSTALLATION_ID, + message: 'Invalid installation ID provided.', + }, + API_ERROR: { + code: InstallationsErrorCode.API_ERROR, + message: 'Installation ID API call failed.', + }, +}; + +/** + * Firebase Installations service error code structure. This extends `PrefixedFirebaseError`. + */ +export class FirebaseInstallationsError extends PrefixedFirebaseError { + /** + * + * @param info - The error code info. + * @param message - The error message. This will override the default + * message if provided. + */ + constructor(info: ErrorInfo, message?: string) { + // Override default message if custom message provided. + super('installations', info.code, message || info.message, info.httpResponse, info.cause); + } +} diff --git a/src/installations/index.ts b/src/installations/index.ts index e7fc00ab78..567d27f2be 100644 --- a/src/installations/index.ts +++ b/src/installations/index.ts @@ -62,4 +62,7 @@ export function getInstallations(app?: App): Installations { return firebaseApp.getOrInitService('installations', (app) => new Installations(app)); } -export { FirebaseInstallationsError, InstallationsClientErrorCode } from '../utils/error'; +export { + FirebaseInstallationsError, + InstallationsErrorCode, +} from './error'; diff --git a/src/installations/installations-request-handler.ts b/src/installations/installations-request-handler.ts index 8542971e6e..a5fceda6bc 100644 --- a/src/installations/installations-request-handler.ts +++ b/src/installations/installations-request-handler.ts @@ -17,7 +17,8 @@ import { App } from '../app/index'; import { FirebaseApp } from '../app/firebase-app'; -import { FirebaseInstallationsError, InstallationsClientErrorCode, toHttpResponse } from '../utils/error'; +import { installationsClientErrorCode, FirebaseInstallationsError } from './error'; +import { toHttpResponse } from '../utils/error'; import { ApiSettings, AuthorizedHttpClient, HttpRequestConfig, RequestResponseError, } from '../utils/api-request'; @@ -66,7 +67,7 @@ export class FirebaseInstallationsRequestHandler { public deleteInstallation(fid: string): Promise { if (!validator.isNonEmptyString(fid)) { return Promise.reject(new FirebaseInstallationsError( - InstallationsClientErrorCode.INVALID_INSTALLATION_ID, + installationsClientErrorCode.INVALID_INSTALLATION_ID, 'Installation ID must be a non-empty string.', )); } @@ -101,7 +102,7 @@ export class FirebaseInstallationsRequestHandler { const message: string = template ? `Installation ID "${apiSettings.getEndpoint()}": ${template}` : errorMessage; throw new FirebaseInstallationsError({ - ...InstallationsClientErrorCode.API_ERROR, + ...installationsClientErrorCode.API_ERROR, message, httpResponse: toHttpResponse(response), cause: err, @@ -123,7 +124,7 @@ export class FirebaseInstallationsRequestHandler { if (!validator.isNonEmptyString(projectId)) { // Assert for an explicit projct ID (either via AppOptions or the cert itself). throw new FirebaseInstallationsError( - InstallationsClientErrorCode.INVALID_PROJECT_ID, + installationsClientErrorCode.INVALID_PROJECT_ID, 'Failed to determine project ID for Installations. Initialize the ' + 'SDK with service account credentials or set project ID as an app option. ' + 'Alternatively set the GOOGLE_CLOUD_PROJECT environment variable.', diff --git a/src/installations/installations.ts b/src/installations/installations.ts index fb2bb5fa15..f677c8dda3 100644 --- a/src/installations/installations.ts +++ b/src/installations/installations.ts @@ -15,7 +15,7 @@ */ import { App } from '../app/index'; -import { FirebaseInstallationsError, InstallationsClientErrorCode } from '../utils/error'; +import { installationsClientErrorCode, FirebaseInstallationsError } from './error'; import { FirebaseInstallationsRequestHandler } from './installations-request-handler'; import * as validator from '../utils/validator'; @@ -35,7 +35,7 @@ export class Installations { constructor(app: App) { if (!validator.isNonNullObject(app) || !('options' in app)) { throw new FirebaseInstallationsError( - InstallationsClientErrorCode.INVALID_ARGUMENT, + installationsClientErrorCode.INVALID_ARGUMENT, 'First argument passed to admin.installations() must be a valid Firebase app instance.', ); } diff --git a/src/instance-id/error.ts b/src/instance-id/error.ts new file mode 100644 index 0000000000..e0cda50167 --- /dev/null +++ b/src/instance-id/error.ts @@ -0,0 +1,61 @@ +/*! + * Copyright 2026 Google LLC + * + * 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 { PrefixedFirebaseError, ErrorInfo } from '../utils/error'; +import { installationsClientErrorCode } from '../installations/error'; + +/** + * The constant mapping for valid Instance ID client error codes. + */ +export const InstanceIdErrorCode = { + INVALID_ARGUMENT: 'invalid-argument', + INVALID_PROJECT_ID: 'invalid-project-id', + INVALID_INSTALLATION_ID: 'invalid-installation-id', + API_ERROR: 'api-error', + INVALID_INSTANCE_ID: 'invalid-instance-id', +} as const; + +/** + * The type definition for valid Instance ID client error codes. + */ +export type InstanceIdErrorCode = typeof InstanceIdErrorCode[keyof typeof InstanceIdErrorCode]; + +/** + * Internal Instance ID client error code mapping used to construct ErrorInfo. + */ +export const instanceIdClientErrorCode: { readonly [K in keyof typeof InstanceIdErrorCode]: ErrorInfo } = { + ...installationsClientErrorCode, + INVALID_INSTANCE_ID: { + code: InstanceIdErrorCode.INVALID_INSTANCE_ID, + message: 'Invalid instance ID provided.', + }, +}; + +/** + * Firebase Instance ID service error code structure. This extends `PrefixedFirebaseError`. + */ +export class FirebaseInstanceIdError extends PrefixedFirebaseError { + /** + * + * @param info - The error code info. + * @param message - The error message. This will override the default + * message if provided. + */ + constructor(info: ErrorInfo, message?: string) { + // Override default message if custom message provided. + super('instance-id', info.code, message || info.message, info.httpResponse, info.cause); + } +} diff --git a/src/instance-id/index.ts b/src/instance-id/index.ts index 0486a927af..a774c0a75b 100644 --- a/src/instance-id/index.ts +++ b/src/instance-id/index.ts @@ -71,4 +71,7 @@ export function getInstanceId(app?: App): InstanceId { return firebaseApp.getOrInitService('instanceId', (app) => new InstanceId(app)); } -export { FirebaseInstanceIdError, InstanceIdClientErrorCode } from '../utils/error'; +export { + FirebaseInstanceIdError, + InstanceIdErrorCode, +} from './error'; diff --git a/src/instance-id/instance-id.ts b/src/instance-id/instance-id.ts index 3a5baac09f..4564cd56ed 100644 --- a/src/instance-id/instance-id.ts +++ b/src/instance-id/instance-id.ts @@ -15,11 +15,9 @@ */ import { getInstallations } from '../installations'; +import { FirebaseInstallationsError, installationsClientErrorCode } from '../installations/error'; import { App } from '../app/index'; -import { - FirebaseInstallationsError, FirebaseInstanceIdError, - InstallationsClientErrorCode, InstanceIdClientErrorCode, -} from '../utils/error'; +import { FirebaseInstanceIdError, instanceIdClientErrorCode } from './error'; import * as validator from '../utils/validator'; /** @@ -40,7 +38,7 @@ export class InstanceId { constructor(app: App) { if (!validator.isNonNullObject(app) || !('options' in app)) { throw new FirebaseInstanceIdError( - InstanceIdClientErrorCode.INVALID_ARGUMENT, + instanceIdClientErrorCode.INVALID_ARGUMENT, 'First argument passed to instanceId() must be a valid Firebase app instance.', ); } @@ -67,8 +65,8 @@ export class InstanceId { .catch((err) => { if (err instanceof FirebaseInstallationsError) { let code = err.code.replace('installations/', ''); - if (code === InstallationsClientErrorCode.INVALID_INSTALLATION_ID.code) { - code = InstanceIdClientErrorCode.INVALID_INSTANCE_ID.code; + if (code === installationsClientErrorCode.INVALID_INSTALLATION_ID.code) { + code = instanceIdClientErrorCode.INVALID_INSTANCE_ID.code; } throw new FirebaseInstanceIdError({ code, message: err.message }); diff --git a/src/machine-learning/machine-learning-utils.ts b/src/machine-learning/error.ts similarity index 73% rename from src/machine-learning/machine-learning-utils.ts rename to src/machine-learning/error.ts index 6c59a56f0c..b8a2a0e102 100644 --- a/src/machine-learning/machine-learning-utils.ts +++ b/src/machine-learning/error.ts @@ -17,26 +17,32 @@ import { PrefixedFirebaseError, ErrorInfo } from '../utils/error'; /** - * Machine Learning client error codes and their default messages. + * The constant mapping for valid Machine Learning client error codes. */ -export type MachineLearningErrorCode = - 'already-exists' - | 'authentication-error' - | 'internal-error' - | 'invalid-argument' - | 'invalid-server-response' - | 'not-found' - | 'resource-exhausted' - | 'service-unavailable' - | 'unknown-error' - | 'cancelled' - | 'deadline-exceeded' - | 'permission-denied' - | 'failed-precondition' - | 'aborted' - | 'out-of-range' - | 'data-loss' - | 'unauthenticated'; +export const MachineLearningErrorCode = { + ALREADY_EXISTS: 'already-exists', + AUTHENTICATION_ERROR: 'authentication-error', + INTERNAL_ERROR: 'internal-error', + INVALID_ARGUMENT: 'invalid-argument', + INVALID_SERVER_RESPONSE: 'invalid-server-response', + NOT_FOUND: 'not-found', + RESOURCE_EXHAUSTED: 'resource-exhausted', + SERVICE_UNAVAILABLE: 'service-unavailable', + UNKNOWN_ERROR: 'unknown-error', + CANCELLED: 'cancelled', + DEADLINE_EXCEEDED: 'deadline-exceeded', + PERMISSION_DENIED: 'permission-denied', + FAILED_PRECONDITION: 'failed-precondition', + ABORTED: 'aborted', + OUT_OF_RANGE: 'out-of-range', + DATA_LOSS: 'data-loss', + UNAUTHENTICATED: 'unauthenticated', +} as const; + +/** + * The type definition for valid Machine Learning client error codes. + */ +export type MachineLearningErrorCode = typeof MachineLearningErrorCode[keyof typeof MachineLearningErrorCode]; export class FirebaseMachineLearningError extends PrefixedFirebaseError { /** @internal */ @@ -68,6 +74,5 @@ export class FirebaseMachineLearningError extends PrefixedFirebaseError { */ constructor(info: ErrorInfo, message?: string) { super('machine-learning', info.code, message || info.message, info.httpResponse, info.cause); - } } diff --git a/src/machine-learning/index.ts b/src/machine-learning/index.ts index 82c4263796..0ebb351a5d 100644 --- a/src/machine-learning/index.ts +++ b/src/machine-learning/index.ts @@ -72,4 +72,4 @@ export function getMachineLearning(app?: App): MachineLearning { return firebaseApp.getOrInitService('machineLearning', (app) => new MachineLearning(app)); } -export { FirebaseMachineLearningError, MachineLearningErrorCode } from './machine-learning-utils'; \ No newline at end of file +export { FirebaseMachineLearningError, MachineLearningErrorCode } from './error'; \ No newline at end of file diff --git a/src/machine-learning/machine-learning-api-client.ts b/src/machine-learning/machine-learning-api-client.ts index b287e6490f..1e8576d577 100644 --- a/src/machine-learning/machine-learning-api-client.ts +++ b/src/machine-learning/machine-learning-api-client.ts @@ -22,7 +22,7 @@ import { import { PrefixedFirebaseError, toHttpResponse } from '../utils/error'; import * as utils from '../utils/index'; import * as validator from '../utils/validator'; -import { FirebaseMachineLearningError, MachineLearningErrorCode } from './machine-learning-utils'; +import { FirebaseMachineLearningError, MachineLearningErrorCode } from './error'; /** * Firebase ML Model input objects diff --git a/src/machine-learning/machine-learning.ts b/src/machine-learning/machine-learning.ts index 1586d79289..ca080643dd 100644 --- a/src/machine-learning/machine-learning.ts +++ b/src/machine-learning/machine-learning.ts @@ -24,7 +24,7 @@ import { MachineLearningApiClient, ModelResponse, ModelUpdateOptions, isGcsTfliteModelOptions, ListModelsOptions, ModelOptions, } from './machine-learning-api-client'; -import { FirebaseMachineLearningError } from './machine-learning-utils'; +import { FirebaseMachineLearningError } from './error'; /** Response object for a listModels operation. */ export interface ListModelsResult { diff --git a/src/messaging/error.ts b/src/messaging/error.ts new file mode 100644 index 0000000000..749479e085 --- /dev/null +++ b/src/messaging/error.ts @@ -0,0 +1,318 @@ +/*! + * Copyright 2026 Google LLC + * + * 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 { ErrorInfo, PrefixedFirebaseError, toHttpResponse } from '../utils/error'; +import { RequestResponseError } from '../utils/api-request'; +import { deepCopy } from '../utils/deep-copy'; +import { BatchResponse } from './messaging-api'; + +/** + * The constant mapping for valid Messaging client error codes. + */ +export const MessagingErrorCode = { + INVALID_ARGUMENT: 'invalid-argument', + INVALID_RECIPIENT: 'invalid-recipient', + INVALID_PAYLOAD: 'invalid-payload', + INVALID_DATA_PAYLOAD_KEY: 'invalid-data-payload-key', + PAYLOAD_SIZE_LIMIT_EXCEEDED: 'payload-size-limit-exceeded', + INVALID_OPTIONS: 'invalid-options', + INVALID_REGISTRATION_TOKEN: 'invalid-registration-token', + REGISTRATION_TOKEN_NOT_REGISTERED: 'registration-token-not-registered', + MISMATCHED_CREDENTIAL: 'mismatched-credential', + INVALID_PACKAGE_NAME: 'invalid-package-name', + DEVICE_MESSAGE_RATE_EXCEEDED: 'device-message-rate-exceeded', + TOPICS_MESSAGE_RATE_EXCEEDED: 'topics-message-rate-exceeded', + MESSAGE_RATE_EXCEEDED: 'message-rate-exceeded', + THIRD_PARTY_AUTH_ERROR: 'third-party-auth-error', + TOO_MANY_TOPICS: 'too-many-topics', + AUTHENTICATION_ERROR: 'authentication-error', + SERVER_UNAVAILABLE: 'server-unavailable', + INTERNAL_ERROR: 'internal-error', + UNKNOWN_ERROR: 'unknown-error', +} as const; + +/** + * The type definition for valid Messaging client error codes. + */ +export type MessagingErrorCode = typeof MessagingErrorCode[keyof typeof MessagingErrorCode]; + +/** + * Internal Messaging client error code mapping used to construct ErrorInfo. + */ +export const messagingClientErrorCode: { readonly [K in keyof typeof MessagingErrorCode]: ErrorInfo } = { + INVALID_ARGUMENT: { + code: MessagingErrorCode.INVALID_ARGUMENT, + message: 'Invalid argument provided.', + }, + INVALID_RECIPIENT: { + code: MessagingErrorCode.INVALID_RECIPIENT, + message: 'Invalid message recipient provided.', + }, + INVALID_PAYLOAD: { + code: MessagingErrorCode.INVALID_PAYLOAD, + message: 'Invalid message payload provided.', + }, + INVALID_DATA_PAYLOAD_KEY: { + code: MessagingErrorCode.INVALID_DATA_PAYLOAD_KEY, + message: 'The data message payload contains an invalid key. See the reference ' + + 'documentation for the DataMessagePayload type for restricted keys.', + }, + PAYLOAD_SIZE_LIMIT_EXCEEDED: { + code: MessagingErrorCode.PAYLOAD_SIZE_LIMIT_EXCEEDED, + message: 'The provided message payload exceeds the FCM size limits. See the ' + + 'error documentation for more details.', + }, + INVALID_OPTIONS: { + code: MessagingErrorCode.INVALID_OPTIONS, + message: 'Invalid message options provided.', + }, + INVALID_REGISTRATION_TOKEN: { + code: MessagingErrorCode.INVALID_REGISTRATION_TOKEN, + message: 'Invalid registration token provided. Make sure it matches the ' + + 'registration token the client app receives from registering with FCM.', + }, + REGISTRATION_TOKEN_NOT_REGISTERED: { + code: MessagingErrorCode.REGISTRATION_TOKEN_NOT_REGISTERED, + message: 'The provided registration token is not registered. A ' + + 'previously valid registration token can be unregistered for a variety of reasons. See the ' + + 'error documentation for more details. Remove this registration token and stop using it to ' + + 'send messages.', + }, + MISMATCHED_CREDENTIAL: { + code: MessagingErrorCode.MISMATCHED_CREDENTIAL, + message: 'The credential used to authenticate this SDK does not have permission ' + + 'to send messages to the device corresponding to the provided registration token. Make sure the ' + + 'credential and registration token both belong to the same Firebase project.', + }, + INVALID_PACKAGE_NAME: { + code: MessagingErrorCode.INVALID_PACKAGE_NAME, + message: 'The message was addressed to a registration token whose package name does ' + + 'not match the provided "restrictedPackageName" option.', + }, + DEVICE_MESSAGE_RATE_EXCEEDED: { + code: MessagingErrorCode.DEVICE_MESSAGE_RATE_EXCEEDED, + message: 'The rate of messages to a particular device is too high. Reduce ' + + 'the number of messages sent to this device and do not immediately retry sending to this device.', + }, + TOPICS_MESSAGE_RATE_EXCEEDED: { + code: MessagingErrorCode.TOPICS_MESSAGE_RATE_EXCEEDED, + message: 'The rate of messages to subscribers to a particular topic is too ' + + 'high. Reduce the number of messages sent for this topic, and do not immediately retry sending ' + + 'to this topic.', + }, + MESSAGE_RATE_EXCEEDED: { + code: MessagingErrorCode.MESSAGE_RATE_EXCEEDED, + message: 'Sending limit exceeded for the message target.', + }, + THIRD_PARTY_AUTH_ERROR: { + code: MessagingErrorCode.THIRD_PARTY_AUTH_ERROR, + message: 'A message targeted to an iOS device could not be sent because the ' + + 'required APNs SSL certificate was not uploaded or has expired. Check the validity of your ' + + 'development and production certificates.', + }, + TOO_MANY_TOPICS: { + code: MessagingErrorCode.TOO_MANY_TOPICS, + message: 'The maximum number of topics the provided registration token can be ' + + 'subscribed to has been exceeded.', + }, + AUTHENTICATION_ERROR: { + code: MessagingErrorCode.AUTHENTICATION_ERROR, + message: 'An error occurred when trying to authenticate to the FCM servers. Make ' + + 'sure the credential used to authenticate this SDK has the proper permissions. See ' + + 'https://firebase.google.com/docs/admin/setup for setup instructions.', + }, + SERVER_UNAVAILABLE: { + code: MessagingErrorCode.SERVER_UNAVAILABLE, + message: 'The FCM server could not process the request in time. See the error ' + + 'documentation for more details.', + }, + INTERNAL_ERROR: { + code: MessagingErrorCode.INTERNAL_ERROR, + message: 'An internal error has occurred. Please retry the request.', + }, + UNKNOWN_ERROR: { + code: MessagingErrorCode.UNKNOWN_ERROR, + message: 'An unknown server error was returned.', + }, +}; + +/** @const {Record} Messaging server to client enum error codes. */ +const MESSAGING_SERVER_TO_CLIENT_CODE: Record = { + /* GENERIC ERRORS */ + // Generic invalid message parameter provided. + InvalidParameters: 'INVALID_ARGUMENT', + // Mismatched sender ID. + MismatchSenderId: 'MISMATCHED_CREDENTIAL', + // FCM server unavailable. + Unavailable: 'SERVER_UNAVAILABLE', + // FCM server internal error. + InternalServerError: 'INTERNAL_ERROR', + + /* SEND ERRORS */ + // Invalid registration token format. + InvalidRegistration: 'INVALID_REGISTRATION_TOKEN', + // Registration token is not registered. + NotRegistered: 'REGISTRATION_TOKEN_NOT_REGISTERED', + // Registration token does not match restricted package name. + InvalidPackageName: 'INVALID_PACKAGE_NAME', + // Message payload size limit exceeded. + MessageTooBig: 'PAYLOAD_SIZE_LIMIT_EXCEEDED', + // Invalid key in the data message payload. + InvalidDataKey: 'INVALID_DATA_PAYLOAD_KEY', + // Invalid time to live option. + InvalidTtl: 'INVALID_OPTIONS', + // Device message rate exceeded. + DeviceMessageRateExceeded: 'DEVICE_MESSAGE_RATE_EXCEEDED', + // Topics message rate exceeded. + TopicsMessageRateExceeded: 'TOPICS_MESSAGE_RATE_EXCEEDED', + // Invalid APNs credentials. + InvalidApnsCredential: 'THIRD_PARTY_AUTH_ERROR', + + /* FCM v1 canonical error codes */ + NOT_FOUND: 'REGISTRATION_TOKEN_NOT_REGISTERED', + PERMISSION_DENIED: 'MISMATCHED_CREDENTIAL', + RESOURCE_EXHAUSTED: 'MESSAGE_RATE_EXCEEDED', + UNAUTHENTICATED: 'THIRD_PARTY_AUTH_ERROR', + + /* FCM v1 new error codes */ + APNS_AUTH_ERROR: 'THIRD_PARTY_AUTH_ERROR', + INTERNAL: 'INTERNAL_ERROR', + INVALID_ARGUMENT: 'INVALID_ARGUMENT', + QUOTA_EXCEEDED: 'MESSAGE_RATE_EXCEEDED', + SENDER_ID_MISMATCH: 'MISMATCHED_CREDENTIAL', + THIRD_PARTY_AUTH_ERROR: 'THIRD_PARTY_AUTH_ERROR', + UNAVAILABLE: 'SERVER_UNAVAILABLE', + UNREGISTERED: 'REGISTRATION_TOKEN_NOT_REGISTERED', + UNSPECIFIED_ERROR: 'UNKNOWN_ERROR', +}; + +/** + * @const {Record} Topic management (IID) + * server to client enum error codes. + */ +const TOPIC_MGT_SERVER_TO_CLIENT_CODE: Record = { + /* TOPIC SUBSCRIPTION MANAGEMENT ERRORS */ + NOT_FOUND: 'REGISTRATION_TOKEN_NOT_REGISTERED', + INVALID_ARGUMENT: 'INVALID_REGISTRATION_TOKEN', + TOO_MANY_TOPICS: 'TOO_MANY_TOPICS', + RESOURCE_EXHAUSTED: 'TOO_MANY_TOPICS', + PERMISSION_DENIED: 'AUTHENTICATION_ERROR', + DEADLINE_EXCEEDED: 'SERVER_UNAVAILABLE', + INTERNAL: 'INTERNAL_ERROR', + UNKNOWN: 'UNKNOWN_ERROR', +}; + +/** + * Firebase Messaging error code structure. This extends `PrefixedFirebaseError`. + */ +export class FirebaseMessagingError extends PrefixedFirebaseError { + /** + * Creates the developer-facing error corresponding to the backend error code. + * + * @param serverErrorCode - The server error code. + * @param [message] The error message. The default message is used + * if not provided. + * @param [serverError] The error's raw server response. + * @returns The corresponding developer-facing error. + * @internal + */ + public static fromServerError( + serverErrorCode: string | null, + message?: string | null, + serverError?: RequestResponseError, + ): FirebaseMessagingError { + // If not found, default to unknown error. + let clientCodeKey = 'UNKNOWN_ERROR'; + if (serverErrorCode && serverErrorCode in MESSAGING_SERVER_TO_CLIENT_CODE) { + clientCodeKey = MESSAGING_SERVER_TO_CLIENT_CODE[serverErrorCode]; + } + const error: ErrorInfo = deepCopy((messagingClientErrorCode as any)[clientCodeKey]); + error.message = message || error.message; + + const rawData = serverError?.response?.data; + if (clientCodeKey === 'UNKNOWN_ERROR' && typeof rawData !== 'undefined') { + try { + error.message += ` Raw server response: "${typeof rawData === 'string' ? rawData : JSON.stringify(rawData)}"`; + } catch (e) { + // Ignore JSON parsing error. + } + } + + error.cause = serverError; + error.httpResponse = serverError?.response ? toHttpResponse(serverError.response) : undefined; + return new FirebaseMessagingError(error); + } + + /** + * @internal + */ + public static fromTopicManagementServerError( + serverErrorCode: string, + message?: string, + serverError?: RequestResponseError, + ): FirebaseMessagingError { + // If not found, default to unknown error. + const clientCodeKey = TOPIC_MGT_SERVER_TO_CLIENT_CODE[serverErrorCode] || 'UNKNOWN_ERROR'; + const error: ErrorInfo = deepCopy((messagingClientErrorCode as any)[clientCodeKey]); + error.message = message || error.message; + + const rawData = serverError?.response?.data; + if (clientCodeKey === 'UNKNOWN_ERROR' && typeof rawData !== 'undefined') { + try { + error.message += ` Raw server response: "${typeof rawData === 'string' ? rawData : JSON.stringify(rawData)}"`; + } catch (e) { + // Ignore JSON parsing error. + } + } + + error.cause = serverError; + error.httpResponse = serverError?.response ? toHttpResponse(serverError.response) : undefined; + return new FirebaseMessagingError(error); + } + + /** + * + * @param info - The error code info. + * @param message - The error message. This will override the default message if provided. + */ + constructor(info: ErrorInfo, message?: string) { + // Override default message if custom message provided. + super('messaging', info.code, message || info.message, info.httpResponse, info.cause); + } +} + +export class FirebaseMessagingSessionError extends FirebaseMessagingError { + public pendingBatchResponse?: Promise; + /** + * + * @param info - The error code info. + * @param message - The error message. This will override the default message if provided. + * @param pendingBatchResponse - BatchResponse for pending messages when session error occured. + */ + constructor(info: ErrorInfo, message?: string, pendingBatchResponse?: Promise) { + // Override default message if custom message provided. + super(info, message || info.message); + this.pendingBatchResponse = pendingBatchResponse; + } + + /** @returns The object representation of the error. */ + public toJSON(): object { + return { + ...super.toJSON(), + pendingBatchResponse: this.pendingBatchResponse, + }; + } +} diff --git a/src/messaging/index.ts b/src/messaging/index.ts index 16054c2c38..48ddbd0333 100644 --- a/src/messaging/index.ts +++ b/src/messaging/index.ts @@ -96,4 +96,7 @@ export function getMessaging(app?: App): Messaging { return firebaseApp.getOrInitService('messaging', (app) => new Messaging(app)); } -export { FirebaseMessagingError, MessagingClientErrorCode } from '../utils/error'; +export { + FirebaseMessagingError, + MessagingErrorCode, +} from './error'; diff --git a/src/messaging/messaging-errors-internal.ts b/src/messaging/messaging-errors-internal.ts index 86d5ee09b3..d759b14876 100644 --- a/src/messaging/messaging-errors-internal.ts +++ b/src/messaging/messaging-errors-internal.ts @@ -15,7 +15,8 @@ */ import { RequestResponseError } from '../utils/api-request'; -import { FirebaseMessagingError, MessagingClientErrorCode, toHttpResponse } from '../utils/error'; +import { FirebaseMessagingError, messagingClientErrorCode } from './error'; +import { toHttpResponse } from '../utils/error'; import * as validator from '../utils/validator'; /** @@ -38,21 +39,21 @@ export function createFirebaseError(err: RequestResponseError): FirebaseMessagin let error: {code: string; message: string}; switch (err.response.status) { case 400: - error = MessagingClientErrorCode.INVALID_ARGUMENT; + error = messagingClientErrorCode.INVALID_ARGUMENT; break; case 401: case 403: - error = MessagingClientErrorCode.AUTHENTICATION_ERROR; + error = messagingClientErrorCode.AUTHENTICATION_ERROR; break; case 500: - error = MessagingClientErrorCode.INTERNAL_ERROR; + error = messagingClientErrorCode.INTERNAL_ERROR; break; case 503: - error = MessagingClientErrorCode.SERVER_UNAVAILABLE; + error = messagingClientErrorCode.SERVER_UNAVAILABLE; break; default: // Treat non-JSON responses with unexpected status codes as unknown errors. - error = MessagingClientErrorCode.UNKNOWN_ERROR; + error = messagingClientErrorCode.UNKNOWN_ERROR; } return new FirebaseMessagingError({ code: error.code, diff --git a/src/messaging/messaging-internal.ts b/src/messaging/messaging-internal.ts index 6427b98e0a..8139c87b94 100644 --- a/src/messaging/messaging-internal.ts +++ b/src/messaging/messaging-internal.ts @@ -15,7 +15,7 @@ */ import { renameProperties, transformMillisecondsToSecondsString } from '../utils/index'; -import { MessagingClientErrorCode, FirebaseMessagingError, } from '../utils/error'; +import { messagingClientErrorCode, FirebaseMessagingError } from './error'; import * as validator from '../utils/validator'; import { @@ -42,7 +42,7 @@ export const BLACKLISTED_OPTIONS_KEYS = [ export function validateMessage(message: Message): void { if (!validator.isNonNullObject(message)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'Message must be a non-null object'); + messagingClientErrorCode.INVALID_PAYLOAD, 'Message must be a non-null object'); } const anyMessage = message as any; @@ -54,14 +54,14 @@ export function validateMessage(message: Message): void { // Checks for illegal characters and empty string. if (!/^[a-zA-Z0-9-_.~%]+$/.test(anyMessage.topic)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'Malformed topic name'); + messagingClientErrorCode.INVALID_PAYLOAD, 'Malformed topic name'); } } const targets = [anyMessage.token, anyMessage.topic, anyMessage.condition]; if (targets.filter((v) => validator.isNonEmptyString(v)).length !== 1) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'Exactly one of topic, token or condition is required'); } @@ -84,12 +84,12 @@ function validateStringMap(map: { [key: string]: any } | undefined, label: strin return; } else if (!validator.isNonNullObject(map)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, `${label} must be a non-null object`); + messagingClientErrorCode.INVALID_PAYLOAD, `${label} must be a non-null object`); } Object.keys(map).forEach((key) => { if (!validator.isString(map[key])) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, `${label} must only contain string values`); + messagingClientErrorCode.INVALID_PAYLOAD, `${label} must only contain string values`); } }); } @@ -104,7 +104,7 @@ function validateWebpushConfig(config: WebpushConfig | undefined): void { return; } else if (!validator.isNonNullObject(config)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'webpush must be a non-null object'); + messagingClientErrorCode.INVALID_PAYLOAD, 'webpush must be a non-null object'); } validateStringMap(config.headers, 'webpush.headers'); validateStringMap(config.data, 'webpush.data'); @@ -121,7 +121,7 @@ function validateApnsConfig(config: ApnsConfig | undefined): void { return; } else if (!validator.isNonNullObject(config)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'apns must be a non-null object'); + messagingClientErrorCode.INVALID_PAYLOAD, 'apns must be a non-null object'); } validateApnsLiveActivityToken(config.liveActivityToken); validateStringMap(config.headers, 'apns.headers'); @@ -140,12 +140,12 @@ function validateApnsLiveActivityToken(liveActivityToken: ApnsConfig['liveActivi return; } else if (!validator.isString(liveActivityToken)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'apns.liveActivityToken must be a string value', ); } else if (!validator.isNonEmptyString(liveActivityToken)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'apns.liveActivityToken must be a non-empty string', ); } @@ -161,19 +161,19 @@ function validateApnsFcmOptions(fcmOptions: ApnsFcmOptions | undefined): void { return; } else if (!validator.isNonNullObject(fcmOptions)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'fcmOptions must be a non-null object'); + messagingClientErrorCode.INVALID_PAYLOAD, 'fcmOptions must be a non-null object'); } if (typeof fcmOptions.imageUrl !== 'undefined' && !validator.isURL(fcmOptions.imageUrl)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'imageUrl must be a valid URL string'); } if (typeof fcmOptions.analyticsLabel !== 'undefined' && !validator.isString(fcmOptions.analyticsLabel)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'analyticsLabel must be a string value'); + messagingClientErrorCode.INVALID_PAYLOAD, 'analyticsLabel must be a string value'); } const propertyMappings: { [key: string]: string } = { @@ -182,7 +182,7 @@ function validateApnsFcmOptions(fcmOptions: ApnsFcmOptions | undefined): void { Object.keys(propertyMappings).forEach((key) => { if (key in fcmOptions && propertyMappings[key] in fcmOptions) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, `Multiple specifications for ${key} in ApnsFcmOptions`); } }); @@ -199,12 +199,12 @@ function validateFcmOptions(fcmOptions: FcmOptions | undefined): void { return; } else if (!validator.isNonNullObject(fcmOptions)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'fcmOptions must be a non-null object'); + messagingClientErrorCode.INVALID_PAYLOAD, 'fcmOptions must be a non-null object'); } if (typeof fcmOptions.analyticsLabel !== 'undefined' && !validator.isString(fcmOptions.analyticsLabel)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'analyticsLabel must be a string value'); + messagingClientErrorCode.INVALID_PAYLOAD, 'analyticsLabel must be a string value'); } } @@ -218,12 +218,12 @@ function validateNotification(notification: Notification | undefined): void { return; } else if (!validator.isNonNullObject(notification)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'notification must be a non-null object'); + messagingClientErrorCode.INVALID_PAYLOAD, 'notification must be a non-null object'); } if (typeof notification.imageUrl !== 'undefined' && !validator.isURL(notification.imageUrl)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'notification.imageUrl must be a valid URL string'); + messagingClientErrorCode.INVALID_PAYLOAD, 'notification.imageUrl must be a valid URL string'); } const propertyMappings: { [key: string]: string } = { @@ -232,7 +232,7 @@ function validateNotification(notification: Notification | undefined): void { Object.keys(propertyMappings).forEach((key) => { if (key in notification && propertyMappings[key] in notification) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, `Multiple specifications for ${key} in Notification`); } }); @@ -249,7 +249,7 @@ function validateApnsPayload(payload: ApnsPayload | undefined): void { return; } else if (!validator.isNonNullObject(payload)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload must be a non-null object'); + messagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload must be a non-null object'); } validateAps(payload.aps); } @@ -265,7 +265,7 @@ function validateAps(aps: Aps): void { return; } else if (!validator.isNonNullObject(aps)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps must be a non-null object'); + messagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps must be a non-null object'); } validateApsAlert(aps.alert); validateApsSound(aps.sound); @@ -278,7 +278,7 @@ function validateAps(aps: Aps): void { Object.keys(propertyMappings).forEach((key) => { if (key in aps && propertyMappings[key] in aps) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, `Multiple specifications for ${key} in Aps`); + messagingClientErrorCode.INVALID_PAYLOAD, `Multiple specifications for ${key} in Aps`); } }); renameProperties(aps, propertyMappings); @@ -307,25 +307,25 @@ function validateApsSound(sound: string | CriticalSound | undefined): void { return; } else if (!validator.isNonNullObject(sound)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps.sound must be a non-empty string or a non-null object'); } if (!validator.isNonEmptyString(sound.name)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps.sound.name must be a non-empty string'); } const volume = sound.volume; if (typeof volume !== 'undefined') { if (!validator.isNumber(volume)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps.sound.volume must be a number'); } if (volume < 0 || volume > 1) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps.sound.volume must be in the interval [0, 1]'); } } @@ -353,7 +353,7 @@ function validateApsAlert(alert: string | ApsAlert | undefined): void { return; } else if (!validator.isNonNullObject(alert)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps.alert must be a string or a non-null object'); } @@ -361,19 +361,19 @@ function validateApsAlert(alert: string | ApsAlert | undefined): void { if (validator.isNonEmptyArray(apsAlert.locArgs) && !validator.isNonEmptyString(apsAlert.locKey)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps.alert.locKey is required when specifying locArgs'); } if (validator.isNonEmptyArray(apsAlert.titleLocArgs) && !validator.isNonEmptyString(apsAlert.titleLocKey)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps.alert.titleLocKey is required when specifying titleLocArgs'); } if (validator.isNonEmptyArray(apsAlert.subtitleLocArgs) && !validator.isNonEmptyString(apsAlert.subtitleLocKey)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'apns.payload.aps.alert.subtitleLocKey is required when specifying subtitleLocArgs'); } @@ -402,13 +402,13 @@ function validateAndroidConfig(config: AndroidConfig | undefined): void { return; } else if (!validator.isNonNullObject(config)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'android must be a non-null object'); + messagingClientErrorCode.INVALID_PAYLOAD, 'android must be a non-null object'); } if (typeof config.ttl !== 'undefined') { if (!validator.isNumber(config.ttl) || config.ttl < 0) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'TTL must be a non-negative duration in milliseconds'); } const duration: string = transformMillisecondsToSecondsString(config.ttl); @@ -440,36 +440,36 @@ function validateAndroidNotification(notification: AndroidNotification | undefin return; } else if (!validator.isNonNullObject(notification)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'android.notification must be a non-null object'); + messagingClientErrorCode.INVALID_PAYLOAD, 'android.notification must be a non-null object'); } if (typeof notification.color !== 'undefined' && !/^#[0-9a-fA-F]{6}$/.test(notification.color)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.color must be in the form #RRGGBB'); + messagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.color must be in the form #RRGGBB'); } if (validator.isNonEmptyArray(notification.bodyLocArgs) && !validator.isNonEmptyString(notification.bodyLocKey)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.bodyLocKey is required when specifying bodyLocArgs'); } if (validator.isNonEmptyArray(notification.titleLocArgs) && !validator.isNonEmptyString(notification.titleLocKey)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.titleLocKey is required when specifying titleLocArgs'); } if (typeof notification.imageUrl !== 'undefined' && !validator.isURL(notification.imageUrl)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.imageUrl must be a valid URL string'); } if (typeof notification.eventTimestamp !== 'undefined') { if (!(notification.eventTimestamp instanceof Date)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.eventTimestamp must be a valid `Date` object'); + messagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.eventTimestamp must be a valid `Date` object'); } // Convert timestamp to RFC3339 UTC "Zulu" format, example "2014-10-02T15:01:23.045123456Z" const zuluTimestamp = notification.eventTimestamp.toISOString(); @@ -479,14 +479,14 @@ function validateAndroidNotification(notification: AndroidNotification | undefin if (typeof notification.vibrateTimingsMillis !== 'undefined') { if (!validator.isNonEmptyArray(notification.vibrateTimingsMillis)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.vibrateTimingsMillis must be a non-empty array of numbers'); } const vibrateTimings: string[] = []; notification.vibrateTimingsMillis.forEach((value) => { if (!validator.isNumber(value) || value < 0) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.vibrateTimingsMillis must be non-negative durations in milliseconds'); } const duration = transformMillisecondsToSecondsString(value); @@ -545,12 +545,12 @@ function validateLightSettings(lightSettings?: LightSettings): void { return; } else if (!validator.isNonNullObject(lightSettings)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.lightSettings must be a non-null object'); + messagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.lightSettings must be a non-null object'); } if (!validator.isNumber(lightSettings.lightOnDurationMillis) || lightSettings.lightOnDurationMillis < 0) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.lightSettings.lightOnDurationMillis must be a non-negative duration in milliseconds'); } const durationOn = transformMillisecondsToSecondsString(lightSettings.lightOnDurationMillis); @@ -558,7 +558,7 @@ function validateLightSettings(lightSettings?: LightSettings): void { if (!validator.isNumber(lightSettings.lightOffDurationMillis) || lightSettings.lightOffDurationMillis < 0) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.lightSettings.lightOffDurationMillis must be a non-negative duration in milliseconds'); } const durationOff = transformMillisecondsToSecondsString(lightSettings.lightOffDurationMillis); @@ -567,14 +567,14 @@ function validateLightSettings(lightSettings?: LightSettings): void { if (!validator.isString(lightSettings.color) || (!/^#[0-9a-fA-F]{6}$/.test(lightSettings.color) && !/^#[0-9a-fA-F]{8}$/.test(lightSettings.color))) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, + messagingClientErrorCode.INVALID_PAYLOAD, 'android.notification.lightSettings.color must be in the form #RRGGBB or #RRGGBBAA format'); } const colorString = lightSettings.color.length === 7 ? lightSettings.color + 'FF' : lightSettings.color; const rgb = /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/i.exec(colorString); if (!rgb || rgb.length < 4) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INTERNAL_ERROR, + messagingClientErrorCode.INTERNAL_ERROR, 'regex to extract rgba values from ' + colorString + ' failed.'); } const color = { @@ -602,11 +602,11 @@ function validateAndroidFcmOptions(fcmOptions: AndroidFcmOptions | undefined): v return; } else if (!validator.isNonNullObject(fcmOptions)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'fcmOptions must be a non-null object'); + messagingClientErrorCode.INVALID_PAYLOAD, 'fcmOptions must be a non-null object'); } if (typeof fcmOptions.analyticsLabel !== 'undefined' && !validator.isString(fcmOptions.analyticsLabel)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_PAYLOAD, 'analyticsLabel must be a string value'); + messagingClientErrorCode.INVALID_PAYLOAD, 'analyticsLabel must be a string value'); } } diff --git a/src/messaging/messaging.ts b/src/messaging/messaging.ts index 05367128f4..3835a931c8 100644 --- a/src/messaging/messaging.ts +++ b/src/messaging/messaging.ts @@ -18,8 +18,9 @@ import { App } from '../app'; import { deepCopy } from '../utils/deep-copy'; import { - ErrorInfo, MessagingClientErrorCode, FirebaseMessagingError, FirebaseMessagingSessionError -} from '../utils/error'; + messagingClientErrorCode, FirebaseMessagingError, FirebaseMessagingSessionError +} from './error'; +import { ErrorInfo } from '../utils/error'; import * as utils from '../utils'; import * as validator from '../utils/validator'; import { validateMessage } from './messaging-internal'; @@ -98,7 +99,7 @@ export class Messaging { constructor(app: App) { if (!validator.isNonNullObject(app) || !('options' in app)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_ARGUMENT, + messagingClientErrorCode.INVALID_ARGUMENT, 'First argument passed to admin.messaging() must be a valid Firebase app instance.', ); } @@ -152,7 +153,7 @@ export class Messaging { validateMessage(copy); if (typeof dryRun !== 'undefined' && !validator.isBoolean(dryRun)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_ARGUMENT, 'dryRun must be a boolean'); + messagingClientErrorCode.INVALID_ARGUMENT, 'dryRun must be a boolean'); } return this.getUrlPath() .then((urlPath) => { @@ -196,16 +197,16 @@ export class Messaging { const copy: Message[] = deepCopy(messages); if (!validator.isNonEmptyArray(copy)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_ARGUMENT, 'messages must be a non-empty array'); + messagingClientErrorCode.INVALID_ARGUMENT, 'messages must be a non-empty array'); } if (copy.length > FCM_MAX_BATCH_SIZE) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_ARGUMENT, + messagingClientErrorCode.INVALID_ARGUMENT, `messages list must not contain more than ${FCM_MAX_BATCH_SIZE} items`); } if (typeof dryRun !== 'undefined' && !validator.isBoolean(dryRun)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_ARGUMENT, 'dryRun must be a boolean'); + messagingClientErrorCode.INVALID_ARGUMENT, 'dryRun must be a boolean'); } const http2SessionHandler = this.useLegacyTransport ? undefined : new Http2SessionHandler(`https://${FCM_SEND_HOST}`); @@ -295,15 +296,15 @@ export class Messaging { const copy: MulticastMessage = deepCopy(message); if (!validator.isNonNullObject(copy)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_ARGUMENT, 'MulticastMessage must be a non-null object'); + messagingClientErrorCode.INVALID_ARGUMENT, 'MulticastMessage must be a non-null object'); } if (!validator.isNonEmptyArray(copy.tokens)) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_ARGUMENT, 'tokens must be a non-empty array'); + messagingClientErrorCode.INVALID_ARGUMENT, 'tokens must be a non-empty array'); } if (copy.tokens.length > FCM_MAX_BATCH_SIZE) { throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_ARGUMENT, + messagingClientErrorCode.INVALID_ARGUMENT, `tokens list must not contain more than ${FCM_MAX_BATCH_SIZE} items`); } @@ -385,7 +386,7 @@ export class Messaging { if (!validator.isNonEmptyString(projectId)) { // Assert for an explicit project ID (either via AppOptions or the cert itself). throw new FirebaseMessagingError( - MessagingClientErrorCode.INVALID_ARGUMENT, + messagingClientErrorCode.INVALID_ARGUMENT, 'Failed to determine project ID for Messaging. Initialize the ' + 'SDK with service account credentials or set project ID as an app option. ' + 'Alternatively set the GOOGLE_CLOUD_PROJECT environment variable.', @@ -458,7 +459,7 @@ export class Messaging { private validateRegistrationTokensType( registrationTokenOrTokens: string | string[], methodName: string, - errorInfo: ErrorInfo = MessagingClientErrorCode.INVALID_ARGUMENT, + errorInfo: ErrorInfo = messagingClientErrorCode.INVALID_ARGUMENT, ): void { if (!validator.isNonEmptyArray(registrationTokenOrTokens) && !validator.isNonEmptyString(registrationTokenOrTokens)) { @@ -481,7 +482,7 @@ export class Messaging { private validateRegistrationTokens( registrationTokenOrTokens: string | string[], methodName: string, - errorInfo: ErrorInfo = MessagingClientErrorCode.INVALID_ARGUMENT, + errorInfo: ErrorInfo = messagingClientErrorCode.INVALID_ARGUMENT, ): void { if (validator.isArray(registrationTokenOrTokens)) { // Validate the array contains no more than 1,000 registration tokens. @@ -516,7 +517,7 @@ export class Messaging { private validateTopicType( topic: string | string[], methodName: string, - errorInfo: ErrorInfo = MessagingClientErrorCode.INVALID_ARGUMENT, + errorInfo: ErrorInfo = messagingClientErrorCode.INVALID_ARGUMENT, ): void { if (!validator.isNonEmptyString(topic)) { throw new FirebaseMessagingError( @@ -537,7 +538,7 @@ export class Messaging { private validateTopic( topic: string, methodName: string, - errorInfo: ErrorInfo = MessagingClientErrorCode.INVALID_ARGUMENT, + errorInfo: ErrorInfo = messagingClientErrorCode.INVALID_ARGUMENT, ): void { if (!validator.isTopic(topic)) { throw new FirebaseMessagingError( diff --git a/src/phone-number-verification/error.ts b/src/phone-number-verification/error.ts new file mode 100644 index 0000000000..898f890005 --- /dev/null +++ b/src/phone-number-verification/error.ts @@ -0,0 +1,46 @@ +/*! + * Copyright 2026 Google LLC + * + * 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 { PrefixedFirebaseError, ErrorInfo } from '../utils/error'; + +/** + * The constant mapping for valid Phone Number Verification client error codes. + */ +export const PhoneNumberVerificationErrorCode = { + INVALID_ARGUMENT: 'invalid-argument', + INVALID_TOKEN: 'invalid-token', + EXPIRED_TOKEN: 'expired-token', +} as const; + +/** + * The type definition for valid Phone Number Verification client error codes. + */ +export type PhoneNumberVerificationErrorCode = + typeof PhoneNumberVerificationErrorCode[keyof typeof PhoneNumberVerificationErrorCode]; + +export const FPNV_ERROR_CODE_MAPPING = PhoneNumberVerificationErrorCode; + +/** + * Firebase Phone Number Verification error code structure. This extends `PrefixedFirebaseError`. + * + * @param info - The error code info. + * @param message - The error message. If provided, this will override the default message. + */ +export class FirebasePhoneNumberVerificationError extends PrefixedFirebaseError { + constructor(info: ErrorInfo, message?: string) { + super('phone-number-verification', info.code, message || info.message, info.httpResponse, info.cause); + } +} diff --git a/src/phone-number-verification/index.ts b/src/phone-number-verification/index.ts index 0de332c976..f27db5700c 100644 --- a/src/phone-number-verification/index.ts +++ b/src/phone-number-verification/index.ts @@ -68,4 +68,4 @@ export function getPhoneNumberVerification(app?: App): PhoneNumberVerification { export { FirebasePhoneNumberVerificationError, PhoneNumberVerificationErrorCode, -} from './phone-number-verification-api-client-internal'; +} from './error'; diff --git a/src/phone-number-verification/phone-number-verification-api-client-internal.ts b/src/phone-number-verification/phone-number-verification-api-client-internal.ts index 0ad11d5128..484f8e8a33 100644 --- a/src/phone-number-verification/phone-number-verification-api-client-internal.ts +++ b/src/phone-number-verification/phone-number-verification-api-client-internal.ts @@ -15,8 +15,6 @@ * limitations under the License. */ -import { PrefixedFirebaseError, ErrorInfo } from '../utils/error'; - export interface FirebasePhoneNumberTokenInfo { /** Documentation URL. */ url: string; @@ -39,33 +37,3 @@ export const FPNV_TOKEN_INFO: FirebasePhoneNumberTokenInfo = { shortName: 'FPNV token', typ: 'JWT', }; - -export const FPNV_ERROR_CODE_MAPPING = { - INVALID_ARGUMENT: 'invalid-argument', - INVALID_TOKEN: 'invalid-token', - EXPIRED_TOKEN: 'expired-token', -} satisfies Record; - -export type PhoneNumberVerificationErrorCode = - | 'invalid-argument' - | 'invalid-token' - | 'expired-token' - -/** - * Firebase Phone Number Verification error code structure. This extends `PrefixedFirebaseError`. - * - * @param info - The error code info. - * @param message - The error message. If provided, this will override the default message. - */ -export class FirebasePhoneNumberVerificationError extends PrefixedFirebaseError { - constructor(info: ErrorInfo, message?: string) { - super('phone-number-verification', info.code, message || info.message, info.httpResponse, info.cause); - - /* tslint:disable:max-line-length */ - // Set the prototype explicitly. See the following link for more details: - // https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work - /* tslint:enable:max-line-length */ - (this as any).__proto__ = FirebasePhoneNumberVerificationError.prototype; - } -} - diff --git a/src/phone-number-verification/phone-number-verification-api.ts b/src/phone-number-verification/phone-number-verification-api.ts index e3c937b728..a3527d78d6 100644 --- a/src/phone-number-verification/phone-number-verification-api.ts +++ b/src/phone-number-verification/phone-number-verification-api.ts @@ -82,5 +82,5 @@ export interface PhoneNumberVerificationToken { export { PhoneNumberVerificationErrorCode, FirebasePhoneNumberVerificationError, -} from './phone-number-verification-api-client-internal'; +} from './error'; diff --git a/src/phone-number-verification/token-verifier.ts b/src/phone-number-verification/token-verifier.ts index 9bc4e7dd1a..3eda268599 100644 --- a/src/phone-number-verification/token-verifier.ts +++ b/src/phone-number-verification/token-verifier.ts @@ -23,7 +23,8 @@ import { DecodedToken, decodeJwt, JwtError, JwtErrorCode, PublicKeySignatureVerifier, ALGORITHM_ES256, SignatureVerifier, } from '../utils/jwt'; -import { FirebasePhoneNumberTokenInfo, FPNV_ERROR_CODE_MAPPING } from './phone-number-verification-api-client-internal'; +import { FirebasePhoneNumberTokenInfo } from './phone-number-verification-api-client-internal'; +import { FPNV_ERROR_CODE_MAPPING } from './error'; export class PhoneNumberTokenVerifier { private readonly shortNameArticle: string; diff --git a/src/project-management/android-app.ts b/src/project-management/android-app.ts index c2013fb297..0d28d0afd5 100644 --- a/src/project-management/android-app.ts +++ b/src/project-management/android-app.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { FirebaseProjectManagementError } from '../utils/error'; +import { FirebaseProjectManagementError } from './error'; import * as validator from '../utils/validator'; import { ProjectManagementRequestHandler, assertServerResponse } from './project-management-api-request-internal'; import { AppMetadata, AppPlatform } from './app-metadata'; diff --git a/src/project-management/error.ts b/src/project-management/error.ts new file mode 100644 index 0000000000..7c734372de --- /dev/null +++ b/src/project-management/error.ts @@ -0,0 +1,51 @@ +/*! + * Copyright 2026 Google LLC + * + * 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 { PrefixedFirebaseError, ErrorInfo } from '../utils/error'; + +/** + * The constant mapping for valid Project Management client error codes. + */ +export const ProjectManagementErrorCode = { + ALREADY_EXISTS: 'already-exists', + AUTHENTICATION_ERROR: 'authentication-error', + INTERNAL_ERROR: 'internal-error', + INVALID_ARGUMENT: 'invalid-argument', + INVALID_PROJECT_ID: 'invalid-project-id', + INVALID_SERVER_RESPONSE: 'invalid-server-response', + NOT_FOUND: 'not-found', + SERVICE_UNAVAILABLE: 'service-unavailable', + UNKNOWN_ERROR: 'unknown-error', +} as const; + +/** + * The type definition for valid Project Management client error codes. + */ +export type ProjectManagementErrorCode = typeof ProjectManagementErrorCode[keyof typeof ProjectManagementErrorCode]; + +/** + * Firebase project management error code structure. This extends PrefixedFirebaseError. + */ +export class FirebaseProjectManagementError extends PrefixedFirebaseError { + /** + * @param info - The error code info. + * @param message - The error message. This will override the default message if provided. + */ + constructor(info: ErrorInfo, message?: string) { + // Override default message if custom message provided. + super('project-management', info.code, message || info.message, info.httpResponse, info.cause); + } +} diff --git a/src/project-management/index.ts b/src/project-management/index.ts index 87a6d570ce..d60da77f53 100644 --- a/src/project-management/index.ts +++ b/src/project-management/index.ts @@ -63,4 +63,4 @@ export function getProjectManagement(app?: App): ProjectManagement { return firebaseApp.getOrInitService('projectManagement', (app) => new ProjectManagement(app)); } -export { FirebaseProjectManagementError, ProjectManagementErrorCode } from '../utils/error'; +export { FirebaseProjectManagementError, ProjectManagementErrorCode } from './error'; diff --git a/src/project-management/ios-app.ts b/src/project-management/ios-app.ts index 082c05932a..b969110b09 100644 --- a/src/project-management/ios-app.ts +++ b/src/project-management/ios-app.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { FirebaseProjectManagementError } from '../utils/error'; +import { FirebaseProjectManagementError } from './error'; import * as validator from '../utils/validator'; import { ProjectManagementRequestHandler, assertServerResponse } from './project-management-api-request-internal'; import { AppMetadata, AppPlatform } from './app-metadata'; diff --git a/src/project-management/project-management-api-request-internal.ts b/src/project-management/project-management-api-request-internal.ts index 0395ca4b56..588586a94e 100644 --- a/src/project-management/project-management-api-request-internal.ts +++ b/src/project-management/project-management-api-request-internal.ts @@ -20,8 +20,9 @@ import { AuthorizedHttpClient, RequestResponseError, HttpMethod, HttpRequestConfig, ExponentialBackoffPoller, } from '../utils/api-request'; import { - FirebaseProjectManagementError, ProjectManagementErrorCode, HttpResponse, toHttpResponse, -} from '../utils/error'; + FirebaseProjectManagementError, ProjectManagementErrorCode, +} from './error'; +import { HttpResponse, toHttpResponse } from '../utils/error'; import { getSdkVersion } from '../utils/index'; import * as validator from '../utils/validator'; import { ShaCertificate } from './android-app'; diff --git a/src/project-management/project-management.ts b/src/project-management/project-management.ts index b3517dc944..e3380b6527 100644 --- a/src/project-management/project-management.ts +++ b/src/project-management/project-management.ts @@ -15,7 +15,7 @@ */ import { App } from '../app'; -import { FirebaseProjectManagementError } from '../utils/error'; +import { FirebaseProjectManagementError } from './error'; import * as utils from '../utils/index'; import * as validator from '../utils/validator'; import { AndroidApp, ShaCertificate } from './android-app'; diff --git a/src/remote-config/error.ts b/src/remote-config/error.ts new file mode 100644 index 0000000000..ae25bce881 --- /dev/null +++ b/src/remote-config/error.ts @@ -0,0 +1,67 @@ +/*! + * Copyright 2026 Google LLC + * + * 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 { PrefixedFirebaseError, ErrorInfo } from '../utils/error'; + +/** @const {Record} Remote Config server to client error code mapping. */ +export const ERROR_CODE_MAPPING: Record = { + ABORTED: 'aborted', + ALREADY_EXISTS: 'already-exists', + INVALID_ARGUMENT: 'invalid-argument', + INTERNAL: 'internal-error', + FAILED_PRECONDITION: 'failed-precondition', + NOT_FOUND: 'not-found', + OUT_OF_RANGE: 'out-of-range', + PERMISSION_DENIED: 'permission-denied', + RESOURCE_EXHAUSTED: 'resource-exhausted', + UNAUTHENTICATED: 'unauthenticated', + UNKNOWN: 'unknown-error', +}; + +/** + * The constant mapping for valid Remote Config client error codes. + */ +export const RemoteConfigErrorCode = { + ABORTED: 'aborted', + ALREADY_EXISTS: 'already-exists', + FAILED_PRECONDITION: 'failed-precondition', + INTERNAL_ERROR: 'internal-error', + INVALID_ARGUMENT: 'invalid-argument', + NOT_FOUND: 'not-found', + OUT_OF_RANGE: 'out-of-range', + PERMISSION_DENIED: 'permission-denied', + RESOURCE_EXHAUSTED: 'resource-exhausted', + UNAUTHENTICATED: 'unauthenticated', + UNKNOWN_ERROR: 'unknown-error', +} as const; + +/** + * The type definition for valid Remote Config client error codes. + */ +export type RemoteConfigErrorCode = typeof RemoteConfigErrorCode[keyof typeof RemoteConfigErrorCode]; + +/** + * Firebase Remote Config error code structure. This extends PrefixedFirebaseError. + */ +export class FirebaseRemoteConfigError extends PrefixedFirebaseError { + /** + * @param info - The error code info. + * @param message - The error message. If provided, this will override the default message. + */ + constructor(info: ErrorInfo, message?: string) { + super('remote-config', info.code, message || info.message, info.httpResponse, info.cause); + } +} diff --git a/src/remote-config/index.ts b/src/remote-config/index.ts index 61d903e70c..94a74bbffe 100644 --- a/src/remote-config/index.ts +++ b/src/remote-config/index.ts @@ -107,4 +107,4 @@ export function getRemoteConfig(app?: App): RemoteConfig { return firebaseApp.getOrInitService('remoteConfig', (app) => new RemoteConfig(app)); } -export { FirebaseRemoteConfigError, RemoteConfigErrorCode } from './remote-config-api-client-internal'; \ No newline at end of file +export { FirebaseRemoteConfigError, RemoteConfigErrorCode } from './error'; \ No newline at end of file diff --git a/src/remote-config/remote-config-api-client-internal.ts b/src/remote-config/remote-config-api-client-internal.ts index 7b88dea1f6..4898491096 100644 --- a/src/remote-config/remote-config-api-client-internal.ts +++ b/src/remote-config/remote-config-api-client-internal.ts @@ -19,7 +19,8 @@ import { FirebaseApp } from '../app/firebase-app'; import { HttpRequestConfig, HttpClient, RequestResponseError, AuthorizedHttpClient, RequestResponse } from '../utils/api-request'; -import { PrefixedFirebaseError, ErrorInfo, toHttpResponse } from '../utils/error'; +import { PrefixedFirebaseError, toHttpResponse } from '../utils/error'; +import { FirebaseRemoteConfigError, RemoteConfigErrorCode, ERROR_CODE_MAPPING } from './error'; import * as utils from '../utils/index'; import * as validator from '../utils/validator'; import { deepCopy } from '../utils/deep-copy'; @@ -479,46 +480,3 @@ interface RemoteConfigApiError { message?: string; status?: string; } - -const ERROR_CODE_MAPPING: { [key: string]: RemoteConfigErrorCode; } = { - ABORTED: 'aborted', - ALREADY_EXISTS: 'already-exists', - INVALID_ARGUMENT: 'invalid-argument', - INTERNAL: 'internal-error', - FAILED_PRECONDITION: 'failed-precondition', - NOT_FOUND: 'not-found', - OUT_OF_RANGE: 'out-of-range', - PERMISSION_DENIED: 'permission-denied', - RESOURCE_EXHAUSTED: 'resource-exhausted', - UNAUTHENTICATED: 'unauthenticated', - UNKNOWN: 'unknown-error', -}; - -/** - * Remote Config client error codes and their default messages. - */ -export type RemoteConfigErrorCode = - 'aborted' - | 'already-exists' - | 'failed-precondition' - | 'internal-error' - | 'invalid-argument' - | 'not-found' - | 'out-of-range' - | 'permission-denied' - | 'resource-exhausted' - | 'unauthenticated' - | 'unknown-error'; - -/** - * Firebase Remote Config error code structure. This extends PrefixedFirebaseError. - */ -export class FirebaseRemoteConfigError extends PrefixedFirebaseError { - /** - * @param info - The error code info. - * @param message - The error message. If provided, this will override the default message. - */ - constructor(info: ErrorInfo, message?: string) { - super('remote-config', info.code, message || info.message, info.httpResponse, info.cause); - } -} diff --git a/src/remote-config/remote-config.ts b/src/remote-config/remote-config.ts index 47e5bc64d2..a90913e100 100644 --- a/src/remote-config/remote-config.ts +++ b/src/remote-config/remote-config.ts @@ -17,7 +17,8 @@ import { App } from '../app'; import * as utils from '../utils/index'; import * as validator from '../utils/validator'; -import { FirebaseRemoteConfigError, RemoteConfigApiClient } from './remote-config-api-client-internal'; +import { RemoteConfigApiClient } from './remote-config-api-client-internal'; +import { FirebaseRemoteConfigError } from './error'; import { ConditionEvaluator } from './condition-evaluator-internal'; import { ValueImpl } from './internal/value-impl'; import { diff --git a/src/security-rules/security-rules-internal.ts b/src/security-rules/error.ts similarity index 61% rename from src/security-rules/security-rules-internal.ts rename to src/security-rules/error.ts index b9c4547556..37b327c9bb 100644 --- a/src/security-rules/security-rules-internal.ts +++ b/src/security-rules/error.ts @@ -17,18 +17,24 @@ import { PrefixedFirebaseError, ErrorInfo } from '../utils/error'; /** - * Security Rules client error codes and their default messages. + * The constant mapping for valid Security Rules client error codes. */ -export type SecurityRulesErrorCode = - 'already-exists' - | 'authentication-error' - | 'internal-error' - | 'invalid-argument' - | 'invalid-server-response' - | 'not-found' - | 'resource-exhausted' - | 'service-unavailable' - | 'unknown-error'; +export const SecurityRulesErrorCode = { + ALREADY_EXISTS: 'already-exists', + AUTHENTICATION_ERROR: 'authentication-error', + INTERNAL_ERROR: 'internal-error', + INVALID_ARGUMENT: 'invalid-argument', + INVALID_SERVER_RESPONSE: 'invalid-server-response', + NOT_FOUND: 'not-found', + RESOURCE_EXHAUSTED: 'resource-exhausted', + SERVICE_UNAVAILABLE: 'service-unavailable', + UNKNOWN_ERROR: 'unknown-error', +} as const; + +/** + * The type definition for valid Security Rules client error codes. + */ +export type SecurityRulesErrorCode = typeof SecurityRulesErrorCode[keyof typeof SecurityRulesErrorCode]; export class FirebaseSecurityRulesError extends PrefixedFirebaseError { /** diff --git a/src/security-rules/index.ts b/src/security-rules/index.ts index c7b6f59171..4da1c7ea9d 100644 --- a/src/security-rules/index.ts +++ b/src/security-rules/index.ts @@ -66,4 +66,4 @@ export function getSecurityRules(app?: App): SecurityRules { return firebaseApp.getOrInitService('securityRules', (app) => new SecurityRules(app)); } -export { FirebaseSecurityRulesError, SecurityRulesErrorCode } from './security-rules-internal'; \ No newline at end of file +export { FirebaseSecurityRulesError, SecurityRulesErrorCode } from './error'; \ No newline at end of file diff --git a/src/security-rules/security-rules-api-client-internal.ts b/src/security-rules/security-rules-api-client-internal.ts index e95c6480d5..8f447a9593 100644 --- a/src/security-rules/security-rules-api-client-internal.ts +++ b/src/security-rules/security-rules-api-client-internal.ts @@ -16,7 +16,7 @@ import { HttpRequestConfig, HttpClient, RequestResponseError, AuthorizedHttpClient } from '../utils/api-request'; import { PrefixedFirebaseError, toHttpResponse } from '../utils/error'; -import { FirebaseSecurityRulesError, SecurityRulesErrorCode } from './security-rules-internal'; +import { FirebaseSecurityRulesError, SecurityRulesErrorCode } from './error'; import * as utils from '../utils/index'; import * as validator from '../utils/validator'; import { FirebaseApp } from '../app/firebase-app'; diff --git a/src/security-rules/security-rules.ts b/src/security-rules/security-rules.ts index c7dfbfc134..9f30732299 100644 --- a/src/security-rules/security-rules.ts +++ b/src/security-rules/security-rules.ts @@ -19,7 +19,7 @@ import * as validator from '../utils/validator'; import { SecurityRulesApiClient, RulesetResponse, RulesetContent, ListRulesetsResponse, } from './security-rules-api-client-internal'; -import { FirebaseSecurityRulesError } from './security-rules-internal'; +import { FirebaseSecurityRulesError } from './error'; /** * A source file containing some Firebase security rules. The content includes raw diff --git a/src/utils/api-request.ts b/src/utils/api-request.ts index ffb8528209..642a628b8c 100644 --- a/src/utils/api-request.ts +++ b/src/utils/api-request.ts @@ -16,7 +16,8 @@ */ import { FirebaseApp } from '../app/firebase-app'; -import { AppErrorCodes, FirebaseAppError, toHttpResponse } from './error'; +import { toHttpResponse } from './error'; +import { AppErrorCode, FirebaseAppError } from '../app/error'; import * as validator from './validator'; import http = require('http'); @@ -142,7 +143,7 @@ class DefaultRequestResponse implements RequestResponse { this.text = resp.data; try { if (!resp.data) { - throw new FirebaseAppError({ code: AppErrorCodes.INTERNAL_ERROR, message: 'HTTP response missing data.' }); + throw new FirebaseAppError({ code: AppErrorCode.INTERNAL_ERROR, message: 'HTTP response missing data.' }); } this.parsedData = JSON.parse(resp.data); } catch (err) { @@ -156,7 +157,7 @@ class DefaultRequestResponse implements RequestResponse { return this.parsedData; } throw new FirebaseAppError({ - code: AppErrorCodes.UNABLE_TO_PARSE_RESPONSE, + code: AppErrorCode.UNABLE_TO_PARSE_RESPONSE, message: 'Error while parsing response data', cause: this.parseError as Error, httpResponse: toHttpResponse(this) @@ -186,14 +187,14 @@ class MultipartRequestResponse implements RequestResponse { get text(): string { throw new FirebaseAppError({ - code: AppErrorCodes.UNABLE_TO_PARSE_RESPONSE, + code: AppErrorCode.UNABLE_TO_PARSE_RESPONSE, message: 'Unable to parse multipart payload as text' }); } get data(): any { throw new FirebaseAppError({ - code: AppErrorCodes.UNABLE_TO_PARSE_RESPONSE, + code: AppErrorCode.UNABLE_TO_PARSE_RESPONSE, message: 'Unable to parse multipart payload as JSON' }); } @@ -259,7 +260,7 @@ export function defaultRetryConfig(): RetryConfig { function validateRetryConfig(retry: RetryConfig): void { if (!validator.isNumber(retry.maxRetries) || retry.maxRetries < 0) { throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_ARGUMENT, + code: AppErrorCode.INVALID_ARGUMENT, message: 'maxRetries must be a non-negative integer' }); } @@ -267,7 +268,7 @@ function validateRetryConfig(retry: RetryConfig): void { if (typeof retry.backOffFactor !== 'undefined') { if (!validator.isNumber(retry.backOffFactor) || retry.backOffFactor < 0) { throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_ARGUMENT, + code: AppErrorCode.INVALID_ARGUMENT, message: 'backOffFactor must be a non-negative number' }); } @@ -275,17 +276,17 @@ function validateRetryConfig(retry: RetryConfig): void { if (!validator.isNumber(retry.maxDelayInMillis) || retry.maxDelayInMillis < 0) { throw new FirebaseAppError({ - code: AppErrorCodes.INVALID_ARGUMENT, + code: AppErrorCode.INVALID_ARGUMENT, message: 'maxDelayInMillis must be a non-negative integer' }); } if (typeof retry.statusCodes !== 'undefined' && !validator.isArray(retry.statusCodes)) { - throw new FirebaseAppError({ code: AppErrorCodes.INVALID_ARGUMENT, message: 'statusCodes must be an array' }); + throw new FirebaseAppError({ code: AppErrorCode.INVALID_ARGUMENT, message: 'statusCodes must be an array' }); } if (typeof retry.ioErrorCodes !== 'undefined' && !validator.isArray(retry.ioErrorCodes)) { - throw new FirebaseAppError({ code: AppErrorCodes.INVALID_ARGUMENT, message: 'ioErrorCodes must be an array' }); + throw new FirebaseAppError({ code: AppErrorCode.INVALID_ARGUMENT, message: 'ioErrorCodes must be an array' }); } } @@ -385,7 +386,7 @@ export class RequestClient { } if (!this.retry) { - throw new FirebaseAppError({ code: AppErrorCodes.INTERNAL_ERROR, message: 'Expected this.retry to exist.' }); + throw new FirebaseAppError({ code: AppErrorCode.INTERNAL_ERROR, message: 'Expected this.retry to exist.' }); } const backOffFactor = this.retry.backOffFactor || 0; @@ -446,13 +447,13 @@ export class HttpClient extends RequestClient { if (err.code === 'ETIMEDOUT') { throw new FirebaseAppError({ - code: AppErrorCodes.NETWORK_TIMEOUT, + code: AppErrorCode.NETWORK_TIMEOUT, message: `Error while making request: ${err.message}.`, cause: err }); } throw new FirebaseAppError({ - code: AppErrorCodes.NETWORK_ERROR, + code: AppErrorCode.NETWORK_ERROR, message: `Error while making request: ${err.message}. Error code: ${err.code}`, cause: err }); @@ -512,13 +513,13 @@ export class Http2Client extends RequestClient { if (err.code === 'ETIMEDOUT') { throw new FirebaseAppError({ - code: AppErrorCodes.NETWORK_TIMEOUT, + code: AppErrorCode.NETWORK_TIMEOUT, message: `Error while making request: ${err.message}.`, cause: err }); } throw new FirebaseAppError({ - code: AppErrorCodes.NETWORK_ERROR, + code: AppErrorCode.NETWORK_ERROR, message: `Error while making request: ${err.message}. Error code: ${err.code}`, cause: err }); @@ -568,7 +569,7 @@ export function parseHttpResponse( request: null, }; if (!validator.isNumber(lowLevelResponse.status)) { - throw new FirebaseAppError({ code: AppErrorCodes.INTERNAL_ERROR, message: 'Malformed HTTP status line.' }); + throw new FirebaseAppError({ code: AppErrorCode.INTERNAL_ERROR, message: 'Malformed HTTP status line.' }); } return new DefaultRequestResponse(lowLevelResponse); } @@ -793,7 +794,7 @@ class AsyncHttpCall extends AsyncRequestCall { if (!res.statusCode) { throw new FirebaseAppError({ - code: AppErrorCodes.INTERNAL_ERROR, + code: AppErrorCode.INTERNAL_ERROR, message: 'Expected a statusCode on the response from a ClientRequest' }); } @@ -901,7 +902,7 @@ class AsyncHttp2Call extends AsyncRequestCall { if (!headers[':status']) { throw new FirebaseAppError({ - code: AppErrorCodes.INTERNAL_ERROR, + code: AppErrorCode.INTERNAL_ERROR, message: 'Expected a statusCode on the response from a ClientRequest' }); } @@ -1365,7 +1366,7 @@ export class Http2SessionHandler { http2Session.on('goaway', (errorCode, _, opaqueData) => { this.reject(new FirebaseAppError({ - code: AppErrorCodes.NETWORK_ERROR, + code: AppErrorCode.NETWORK_ERROR, message: `Error while making requests: GOAWAY - ${opaqueData?.toString()}, Error code: ${errorCode}` })); }) @@ -1379,7 +1380,7 @@ export class Http2SessionHandler { errorMessage = `Session error while making requests: ${error.code} - ${error.message} ` } this.reject(new FirebaseAppError({ - code: AppErrorCodes.NETWORK_ERROR, + code: AppErrorCode.NETWORK_ERROR, message: errorMessage, cause: error, })); diff --git a/src/utils/error.ts b/src/utils/error.ts index f428e28780..15ea1f5b98 100644 --- a/src/utils/error.ts +++ b/src/utils/error.ts @@ -15,9 +15,7 @@ * limitations under the License. */ -import { BatchResponse } from '../messaging/messaging-api'; -import { deepCopy } from '../utils/deep-copy'; -import { RequestResponseError, RequestResponse } from './api-request'; +import { RequestResponse } from './api-request'; /** * Represents the raw HTTP response object. @@ -60,13 +58,6 @@ export interface ErrorInfo { cause?: Error; } -/** - * Defines a type that stores all server to client codes (string enum). - */ -interface ServerToClientCode { - [code: string]: string; -} - /** * `FirebaseError` is a subclass of the standard JavaScript `Error` object. In * addition to a message string and stack trace, it contains a string code. @@ -136,7 +127,6 @@ export class FirebaseError extends Error implements FirebaseError { if (errorInfo.httpResponse !== undefined) { this.httpResponse = errorInfo.httpResponse; } - } /** @returns The object representation of the error. */ @@ -185,7 +175,6 @@ export class PrefixedFirebaseError extends FirebaseError { errorInfo.cause = cause; } super(errorInfo); - } /** @@ -199,1022 +188,3 @@ export class PrefixedFirebaseError extends FirebaseError { return `${this.codePrefix}/${code}` === this.code; } } - -/** - * Firebase App error code structure. This extends PrefixedFirebaseError. - */ -export class FirebaseAppError extends PrefixedFirebaseError { - /** - * @param info - The error code info. - * @param message - The error message. This will override the default message if provided. - */ - constructor(info: ErrorInfo, message?: string) { - // Override default message if custom message provided. - super('app', info.code, message || info.message, info.httpResponse, info.cause); - - } -} - -/** - * Firebase Auth error code structure. This extends PrefixedFirebaseError. - */ -export class FirebaseAuthError extends PrefixedFirebaseError { - /** - * Creates the developer-facing error corresponding to the backend error code. - * - * @param serverErrorCode - The server error code. - * @param [message] The error message. The default message is used - * if not provided. - * @param [serverError] The error's raw server response. - * @returns The corresponding developer-facing error. - * @internal - */ - public static fromServerError( - serverErrorCode: string, - message?: string, - serverError?: RequestResponseError, - ): FirebaseAuthError { - // serverErrorCode could contain additional details: - // ERROR_CODE : Detailed message which can also contain colons - const colonSeparator = (serverErrorCode || '').indexOf(':'); - let customMessage = null; - if (colonSeparator !== -1) { - customMessage = serverErrorCode.substring(colonSeparator + 1).trim(); - serverErrorCode = serverErrorCode.substring(0, colonSeparator).trim(); - } - // If not found, default to internal error. - const clientCodeKey = AUTH_SERVER_TO_CLIENT_CODE[serverErrorCode] || 'INTERNAL_ERROR'; - const error: ErrorInfo = deepCopy((AuthClientErrorCode as any)[clientCodeKey]); - // Server detailed message should have highest priority. - error.message = customMessage || message || error.message; - error.cause = serverError; - error.httpResponse = serverError?.response ? toHttpResponse(serverError.response) : undefined; - return new FirebaseAuthError(error); - } - - /** - * @param info - The error code info. - * @param message - The error message. This will override the default message if provided. - */ - constructor(info: ErrorInfo, message?: string) { - // Override default message if custom message provided. - super('auth', info.code, message || info.message, info.httpResponse, info.cause); - - } -} - -/** - * Firebase Database error code structure. This extends FirebaseError. - */ -export class FirebaseDatabaseError extends FirebaseError { - /** - * @param info - The error code info. - * @param message - The error message. This will override the default - * message if provided. - */ - constructor(info: ErrorInfo, message?: string) { - // Override default message if custom message provided. - super({ - code: 'database/' + info.code, - message: message || info.message, - httpResponse: info.httpResponse, - cause: info.cause - }); - } -} - -/** - * Firebase Firestore error code structure. This extends FirebaseError. - */ -export class FirebaseFirestoreError extends FirebaseError { - /** - * @param info - The error code info. - * @param message - The error message. This will override the default - * message if provided. - */ - constructor(info: ErrorInfo, message?: string) { - // Override default message if custom message provided. - super({ - code: 'firestore/' + info.code, - message: message || info.message, - httpResponse: info.httpResponse, - cause: info.cause - }); - } -} - -/** - * Firebase instance ID error code structure. This extends FirebaseError. - */ -export class FirebaseInstanceIdError extends FirebaseError { - /** - * - * @param info - The error code info. - * @param message - The error message. This will override the default - * message if provided. - */ - constructor(info: ErrorInfo, message?: string) { - // Override default message if custom message provided. - super({ - code: 'instance-id/' + info.code, - message: message || info.message, - httpResponse: info.httpResponse, - cause: info.cause - }); - } -} - -/** - * Firebase Installations service error code structure. This extends `FirebaseError`. - */ -export class FirebaseInstallationsError extends FirebaseError { - /** - * - * @param info - The error code info. - * @param message - The error message. This will override the default - * message if provided. - */ - constructor(info: ErrorInfo, message?: string) { - // Override default message if custom message provided. - super({ - code: 'installations/' + info.code, - message: message || info.message, - httpResponse: info.httpResponse, - cause: info.cause - }); - } -} - - -/** - * Firebase Messaging error code structure. This extends PrefixedFirebaseError. - */ -export class FirebaseMessagingError extends PrefixedFirebaseError { - /** - * Creates the developer-facing error corresponding to the backend error code. - * - * @param serverErrorCode - The server error code. - * @param [message] The error message. The default message is used - * if not provided. - * @param [serverError] The error's raw server response. - * @returns The corresponding developer-facing error. - * @internal - */ - public static fromServerError( - serverErrorCode: string | null, - message?: string | null, - serverError?: RequestResponseError, - ): FirebaseMessagingError { - // If not found, default to unknown error. - let clientCodeKey = 'UNKNOWN_ERROR'; - if (serverErrorCode && serverErrorCode in MESSAGING_SERVER_TO_CLIENT_CODE) { - clientCodeKey = MESSAGING_SERVER_TO_CLIENT_CODE[serverErrorCode]; - } - const error: ErrorInfo = deepCopy((MessagingClientErrorCode as any)[clientCodeKey]); - error.message = message || error.message; - - const rawData = serverError?.response?.data; - if (clientCodeKey === 'UNKNOWN_ERROR' && typeof rawData !== 'undefined') { - try { - error.message += ` Raw server response: "${typeof rawData === 'string' ? rawData : JSON.stringify(rawData)}"`; - } catch (e) { - // Ignore JSON parsing error. - } - } - - error.cause = serverError; - error.httpResponse = serverError?.response ? toHttpResponse(serverError.response) : undefined; - return new FirebaseMessagingError(error); - } - - /** - * @internal - */ - public static fromTopicManagementServerError( - serverErrorCode: string, - message?: string, - serverError?: RequestResponseError, - ): FirebaseMessagingError { - // If not found, default to unknown error. - const clientCodeKey = TOPIC_MGT_SERVER_TO_CLIENT_CODE[serverErrorCode] || 'UNKNOWN_ERROR'; - const error: ErrorInfo = deepCopy((MessagingClientErrorCode as any)[clientCodeKey]); - error.message = message || error.message; - - const rawData = serverError?.response?.data; - if (clientCodeKey === 'UNKNOWN_ERROR' && typeof rawData !== 'undefined') { - try { - error.message += ` Raw server response: "${typeof rawData === 'string' ? rawData : JSON.stringify(rawData)}"`; - } catch (e) { - // Ignore JSON parsing error. - } - } - - error.cause = serverError; - error.httpResponse = serverError?.response ? toHttpResponse(serverError.response) : undefined; - return new FirebaseMessagingError(error); - } - - /** - * - * @param info - The error code info. - * @param message - The error message. This will override the default message if provided. - */ - constructor(info: ErrorInfo, message?: string) { - // Override default message if custom message provided. - super('messaging', info.code, message || info.message, info.httpResponse, info.cause); - - } -} - -export class FirebaseMessagingSessionError extends FirebaseMessagingError { - public pendingBatchResponse?: Promise; - /** - * - * @param info - The error code info. - * @param message - The error message. This will override the default message if provided. - * @param pendingBatchResponse - BatchResponse for pending messages when session error occured. - */ - constructor(info: ErrorInfo, message?: string, pendingBatchResponse?: Promise) { - // Override default message if custom message provided. - super(info, message || info.message); - this.pendingBatchResponse = pendingBatchResponse; - - } - - /** @returns The object representation of the error. */ - public toJSON(): object { - return { - code: this.code, - message: this.message, - pendingBatchResponse: this.pendingBatchResponse, - }; - } -} - -/** - * Firebase project management error code structure. This extends PrefixedFirebaseError. - */ -export class FirebaseProjectManagementError extends PrefixedFirebaseError { - /** - * @param info - The error code info. - * @param message - The error message. This will override the default message if provided. - */ - constructor(info: ErrorInfo, message?: string) { - // Override default message if custom message provided. - super('project-management', info.code, message || info.message, info.httpResponse, info.cause); - - } -} - -/** - * App client error codes and their default messages. - */ -export class AppErrorCodes { - public static APP_DELETED = 'app-deleted'; - public static DUPLICATE_APP = 'duplicate-app'; - public static INVALID_ARGUMENT = 'invalid-argument'; - public static INTERNAL_ERROR = 'internal-error'; - public static INVALID_APP_NAME = 'invalid-app-name'; - public static INVALID_APP_OPTIONS = 'invalid-app-options'; - public static INVALID_CREDENTIAL = 'invalid-credential'; - public static NETWORK_ERROR = 'network-error'; - public static NETWORK_TIMEOUT = 'network-timeout'; - public static NO_APP = 'no-app'; - public static UNABLE_TO_PARSE_RESPONSE = 'unable-to-parse-response'; -} - -/** - * Auth client error codes and their default messages. - */ -// eslint-disable-next-line @typescript-eslint/naming-convention -export const AuthClientErrorCode = { - AUTH_BLOCKING_TOKEN_EXPIRED: { - code: 'auth-blocking-token-expired', - message: 'The provided Firebase Auth Blocking token is expired.', - }, - BILLING_NOT_ENABLED: { - code: 'billing-not-enabled', - message: 'Feature requires billing to be enabled.', - }, - CLAIMS_TOO_LARGE: { - code: 'claims-too-large', - message: 'Developer claims maximum payload size exceeded.', - }, - CONFIGURATION_EXISTS: { - code: 'configuration-exists', - message: 'A configuration already exists with the provided identifier.', - }, - CONFIGURATION_NOT_FOUND: { - code: 'configuration-not-found', - message: 'There is no configuration corresponding to the provided identifier.', - }, - ID_TOKEN_EXPIRED: { - code: 'id-token-expired', - message: 'The provided Firebase ID token is expired.', - }, - INVALID_ARGUMENT: { - code: 'argument-error', - message: 'Invalid argument provided.', - }, - INVALID_CONFIG: { - code: 'invalid-config', - message: 'The provided configuration is invalid.', - }, - EMAIL_ALREADY_EXISTS: { - code: 'email-already-exists', - message: 'The email address is already in use by another account.', - }, - EMAIL_NOT_FOUND: { - code: 'email-not-found', - message: 'There is no user record corresponding to the provided email.', - }, - FORBIDDEN_CLAIM: { - code: 'reserved-claim', - message: 'The specified developer claim is reserved and cannot be specified.', - }, - INVALID_ID_TOKEN: { - code: 'invalid-id-token', - message: 'The provided ID token is not a valid Firebase ID token.', - }, - ID_TOKEN_REVOKED: { - code: 'id-token-revoked', - message: 'The Firebase ID token has been revoked.', - }, - INTERNAL_ERROR: { - code: 'internal-error', - message: 'An internal error has occurred.', - }, - INVALID_CLAIMS: { - code: 'invalid-claims', - message: 'The provided custom claim attributes are invalid.', - }, - INVALID_CONTINUE_URI: { - code: 'invalid-continue-uri', - message: 'The continue URL must be a valid URL string.', - }, - INVALID_CREATION_TIME: { - code: 'invalid-creation-time', - message: 'The creation time must be a valid UTC date string.', - }, - INVALID_CREDENTIAL: { - code: 'invalid-credential', - message: 'Invalid credential object provided.', - }, - INVALID_DISABLED_FIELD: { - code: 'invalid-disabled-field', - message: 'The disabled field must be a boolean.', - }, - INVALID_DISPLAY_NAME: { - code: 'invalid-display-name', - message: 'The displayName field must be a valid string.', - }, - INVALID_DYNAMIC_LINK_DOMAIN: { - code: 'invalid-dynamic-link-domain', - message: 'The provided dynamic link domain is not configured or authorized ' + - 'for the current project.', - }, - INVALID_HOSTING_LINK_DOMAIN: { - code: 'invalid-hosting-link-domain', - message: 'The provided hosting link domain is not configured in Firebase ' + - 'Hosting or is not owned by the current project.', - }, - INVALID_EMAIL_VERIFIED: { - code: 'invalid-email-verified', - message: 'The emailVerified field must be a boolean.', - }, - INVALID_EMAIL: { - code: 'invalid-email', - message: 'The email address is improperly formatted.', - }, - INVALID_NEW_EMAIL: { - code: 'invalid-new-email', - message: 'The new email address is improperly formatted.', - }, - INVALID_ENROLLED_FACTORS: { - code: 'invalid-enrolled-factors', - message: 'The enrolled factors must be a valid array of MultiFactorInfo objects.', - }, - INVALID_ENROLLMENT_TIME: { - code: 'invalid-enrollment-time', - message: 'The second factor enrollment time must be a valid UTC date string.', - }, - INVALID_HASH_ALGORITHM: { - code: 'invalid-hash-algorithm', - message: 'The hash algorithm must match one of the strings in the list of ' + - 'supported algorithms.', - }, - INVALID_HASH_BLOCK_SIZE: { - code: 'invalid-hash-block-size', - message: 'The hash block size must be a valid number.', - }, - INVALID_HASH_DERIVED_KEY_LENGTH: { - code: 'invalid-hash-derived-key-length', - message: 'The hash derived key length must be a valid number.', - }, - INVALID_HASH_KEY: { - code: 'invalid-hash-key', - message: 'The hash key must a valid byte buffer.', - }, - INVALID_HASH_MEMORY_COST: { - code: 'invalid-hash-memory-cost', - message: 'The hash memory cost must be a valid number.', - }, - INVALID_HASH_PARALLELIZATION: { - code: 'invalid-hash-parallelization', - message: 'The hash parallelization must be a valid number.', - }, - INVALID_HASH_ROUNDS: { - code: 'invalid-hash-rounds', - message: 'The hash rounds must be a valid number.', - }, - INVALID_HASH_SALT_SEPARATOR: { - code: 'invalid-hash-salt-separator', - message: 'The hashing algorithm salt separator field must be a valid byte buffer.', - }, - INVALID_LAST_SIGN_IN_TIME: { - code: 'invalid-last-sign-in-time', - message: 'The last sign-in time must be a valid UTC date string.', - }, - INVALID_NAME: { - code: 'invalid-name', - message: 'The resource name provided is invalid.', - }, - INVALID_OAUTH_CLIENT_ID: { - code: 'invalid-oauth-client-id', - message: 'The provided OAuth client ID is invalid.', - }, - INVALID_PAGE_TOKEN: { - code: 'invalid-page-token', - message: 'The page token must be a valid non-empty string.', - }, - INVALID_PASSWORD: { - code: 'invalid-password', - message: 'The password must be a string with at least 6 characters.', - }, - INVALID_PASSWORD_HASH: { - code: 'invalid-password-hash', - message: 'The password hash must be a valid byte buffer.', - }, - INVALID_PASSWORD_SALT: { - code: 'invalid-password-salt', - message: 'The password salt must be a valid byte buffer.', - }, - INVALID_PHONE_NUMBER: { - code: 'invalid-phone-number', - message: 'The phone number must be a non-empty E.164 standard compliant identifier ' + - 'string.', - }, - INVALID_PHOTO_URL: { - code: 'invalid-photo-url', - message: 'The photoURL field must be a valid URL.', - }, - INVALID_PROJECT_ID: { - code: 'invalid-project-id', - message: 'Invalid parent project. Either parent project doesn\'t exist or didn\'t enable multi-tenancy.', - }, - INVALID_PROVIDER_DATA: { - code: 'invalid-provider-data', - message: 'The providerData must be a valid array of UserInfo objects.', - }, - INVALID_PROVIDER_ID: { - code: 'invalid-provider-id', - message: 'The providerId must be a valid supported provider identifier string.', - }, - INVALID_PROVIDER_UID: { - code: 'invalid-provider-uid', - message: 'The providerUid must be a valid provider uid string.', - }, - INVALID_OAUTH_RESPONSETYPE: { - code: 'invalid-oauth-responsetype', - message: 'Only exactly one OAuth responseType should be set to true.', - }, - INVALID_SESSION_COOKIE_DURATION: { - code: 'invalid-session-cookie-duration', - message: 'The session cookie duration must be a valid number in milliseconds ' + - 'between 5 minutes and 2 weeks.', - }, - INVALID_TENANT_ID: { - code: 'invalid-tenant-id', - message: 'The tenant ID must be a valid non-empty string.', - }, - INVALID_TENANT_TYPE: { - code: 'invalid-tenant-type', - message: 'Tenant type must be either "full_service" or "lightweight".', - }, - INVALID_TESTING_PHONE_NUMBER: { - code: 'invalid-testing-phone-number', - message: 'Invalid testing phone number or invalid test code provided.', - }, - INVALID_UID: { - code: 'invalid-uid', - message: 'The uid must be a non-empty string with at most 128 characters.', - }, - INVALID_USER_IMPORT: { - code: 'invalid-user-import', - message: 'The user record to import is invalid.', - }, - INVALID_TOKENS_VALID_AFTER_TIME: { - code: 'invalid-tokens-valid-after-time', - message: 'The tokensValidAfterTime must be a valid UTC number in seconds.', - }, - MISMATCHING_TENANT_ID: { - code: 'mismatching-tenant-id', - message: 'User tenant ID does not match with the current TenantAwareAuth tenant ID.', - }, - MISSING_ANDROID_PACKAGE_NAME: { - code: 'missing-android-pkg-name', - message: 'An Android Package Name must be provided if the Android App is ' + - 'required to be installed.', - }, - MISSING_CONFIG: { - code: 'missing-config', - message: 'The provided configuration is missing required attributes.', - }, - MISSING_CONTINUE_URI: { - code: 'missing-continue-uri', - message: 'A valid continue URL must be provided in the request.', - }, - MISSING_DISPLAY_NAME: { - code: 'missing-display-name', - message: 'The resource being created or edited is missing a valid display name.', - }, - MISSING_EMAIL: { - code: 'missing-email', - message: 'The email is required for the specified action. For example, a multi-factor user ' + - 'requires a verified email.', - }, - MISSING_IOS_BUNDLE_ID: { - code: 'missing-ios-bundle-id', - message: 'The request is missing an iOS Bundle ID.', - }, - MISSING_ISSUER: { - code: 'missing-issuer', - message: 'The OAuth/OIDC configuration issuer must not be empty.', - }, - MISSING_HASH_ALGORITHM: { - code: 'missing-hash-algorithm', - message: 'Importing users with password hashes requires that the hashing ' + - 'algorithm and its parameters be provided.', - }, - MISSING_OAUTH_CLIENT_ID: { - code: 'missing-oauth-client-id', - message: 'The OAuth/OIDC configuration client ID must not be empty.', - }, - MISSING_OAUTH_CLIENT_SECRET: { - code: 'missing-oauth-client-secret', - message: 'The OAuth configuration client secret is required to enable OIDC code flow.', - }, - MISSING_PROVIDER_ID: { - code: 'missing-provider-id', - message: 'A valid provider ID must be provided in the request.', - }, - MISSING_SAML_RELYING_PARTY_CONFIG: { - code: 'missing-saml-relying-party-config', - message: 'The SAML configuration provided is missing a relying party configuration.', - }, - MAXIMUM_TEST_PHONE_NUMBER_EXCEEDED: { - code: 'test-phone-number-limit-exceeded', - message: 'The maximum allowed number of test phone number / code pairs has been exceeded.', - }, - MAXIMUM_USER_COUNT_EXCEEDED: { - code: 'maximum-user-count-exceeded', - message: 'The maximum allowed number of users to import has been exceeded.', - }, - MISSING_UID: { - code: 'missing-uid', - message: 'A uid identifier is required for the current operation.', - }, - OPERATION_NOT_ALLOWED: { - code: 'operation-not-allowed', - message: 'The given sign-in provider is disabled for this Firebase project. ' + - 'Enable it in the Firebase console, under the sign-in method tab of the ' + - 'Auth section.', - }, - PHONE_NUMBER_ALREADY_EXISTS: { - code: 'phone-number-already-exists', - message: 'The user with the provided phone number already exists.', - }, - PROJECT_NOT_FOUND: { - code: 'project-not-found', - message: 'No Firebase project was found for the provided credential.', - }, - INSUFFICIENT_PERMISSION: { - code: 'insufficient-permission', - message: 'Credential implementation provided to initializeApp() via the "credential" property ' + - 'has insufficient permission to access the requested resource. See ' + - 'https://firebase.google.com/docs/admin/setup for details on how to authenticate this SDK ' + - 'with appropriate permissions.', - }, - QUOTA_EXCEEDED: { - code: 'quota-exceeded', - message: 'The project quota for the specified operation has been exceeded.', - }, - SECOND_FACTOR_LIMIT_EXCEEDED: { - code: 'second-factor-limit-exceeded', - message: 'The maximum number of allowed second factors on a user has been exceeded.', - }, - SECOND_FACTOR_UID_ALREADY_EXISTS: { - code: 'second-factor-uid-already-exists', - message: 'The specified second factor "uid" already exists.', - }, - SESSION_COOKIE_EXPIRED: { - code: 'session-cookie-expired', - message: 'The Firebase session cookie is expired.', - }, - SESSION_COOKIE_REVOKED: { - code: 'session-cookie-revoked', - message: 'The Firebase session cookie has been revoked.', - }, - TENANT_NOT_FOUND: { - code: 'tenant-not-found', - message: 'There is no tenant corresponding to the provided identifier.', - }, - UID_ALREADY_EXISTS: { - code: 'uid-already-exists', - message: 'The user with the provided uid already exists.', - }, - UNAUTHORIZED_DOMAIN: { - code: 'unauthorized-continue-uri', - message: 'The domain of the continue URL is not whitelisted. Whitelist the domain in the ' + - 'Firebase console.', - }, - UNSUPPORTED_FIRST_FACTOR: { - code: 'unsupported-first-factor', - message: 'A multi-factor user requires a supported first factor.', - }, - UNSUPPORTED_SECOND_FACTOR: { - code: 'unsupported-second-factor', - message: 'The request specified an unsupported type of second factor.', - }, - UNSUPPORTED_TENANT_OPERATION: { - code: 'unsupported-tenant-operation', - message: 'This operation is not supported in a multi-tenant context.', - }, - UNVERIFIED_EMAIL: { - code: 'unverified-email', - message: 'A verified email is required for the specified action. For example, a multi-factor user ' + - 'requires a verified email.', - }, - USER_NOT_FOUND: { - code: 'user-not-found', - message: 'There is no user record corresponding to the provided identifier.', - }, - NOT_FOUND: { - code: 'not-found', - message: 'The requested resource was not found.', - }, - USER_DISABLED: { - code: 'user-disabled', - message: 'The user record is disabled.', - }, - USER_NOT_DISABLED: { - code: 'user-not-disabled', - message: 'The user must be disabled in order to bulk delete it (or you must pass force=true).', - }, - INVALID_RECAPTCHA_ACTION: { - code: 'invalid-recaptcha-action', - message: 'reCAPTCHA action must be "BLOCK".' - }, - INVALID_RECAPTCHA_ENFORCEMENT_STATE: { - code: 'invalid-recaptcha-enforcement-state', - message: 'reCAPTCHA enforcement state must be either "OFF", "AUDIT" or "ENFORCE".' - }, - RECAPTCHA_NOT_ENABLED: { - code: 'racaptcha-not-enabled', - message: 'reCAPTCHA enterprise is not enabled.' - } -} satisfies Record; - -/** - * Messaging client error codes and their default messages. - */ -// eslint-disable-next-line @typescript-eslint/naming-convention -export const MessagingClientErrorCode = { - INVALID_ARGUMENT: { - code: 'invalid-argument', - message: 'Invalid argument provided.', - }, - INVALID_RECIPIENT: { - code: 'invalid-recipient', - message: 'Invalid message recipient provided.', - }, - INVALID_PAYLOAD: { - code: 'invalid-payload', - message: 'Invalid message payload provided.', - }, - INVALID_DATA_PAYLOAD_KEY: { - code: 'invalid-data-payload-key', - message: 'The data message payload contains an invalid key. See the reference documentation ' + - 'for the DataMessagePayload type for restricted keys.', - }, - PAYLOAD_SIZE_LIMIT_EXCEEDED: { - code: 'payload-size-limit-exceeded', - message: 'The provided message payload exceeds the FCM size limits. See the error documentation ' + - 'for more details.', - }, - INVALID_OPTIONS: { - code: 'invalid-options', - message: 'Invalid message options provided.', - }, - INVALID_REGISTRATION_TOKEN: { - code: 'invalid-registration-token', - message: 'Invalid registration token provided. Make sure it matches the registration token ' + - 'the client app receives from registering with FCM.', - }, - REGISTRATION_TOKEN_NOT_REGISTERED: { - code: 'registration-token-not-registered', - message: 'The provided registration token is not registered. A previously valid registration ' + - 'token can be unregistered for a variety of reasons. See the error documentation for more ' + - 'details. Remove this registration token and stop using it to send messages.', - }, - MISMATCHED_CREDENTIAL: { - code: 'mismatched-credential', - message: 'The credential used to authenticate this SDK does not have permission to send ' + - 'messages to the device corresponding to the provided registration token. Make sure the ' + - 'credential and registration token both belong to the same Firebase project.', - }, - INVALID_PACKAGE_NAME: { - code: 'invalid-package-name', - message: 'The message was addressed to a registration token whose package name does not match ' + - 'the provided "restrictedPackageName" option.', - }, - DEVICE_MESSAGE_RATE_EXCEEDED: { - code: 'device-message-rate-exceeded', - message: 'The rate of messages to a particular device is too high. Reduce the number of ' + - 'messages sent to this device and do not immediately retry sending to this device.', - }, - TOPICS_MESSAGE_RATE_EXCEEDED: { - code: 'topics-message-rate-exceeded', - message: 'The rate of messages to subscribers to a particular topic is too high. Reduce the ' + - 'number of messages sent for this topic, and do not immediately retry sending to this topic.', - }, - MESSAGE_RATE_EXCEEDED: { - code: 'message-rate-exceeded', - message: 'Sending limit exceeded for the message target.', - }, - THIRD_PARTY_AUTH_ERROR: { - code: 'third-party-auth-error', - message: 'A message targeted to an iOS device could not be sent because the required APNs ' + - 'SSL certificate was not uploaded or has expired. Check the validity of your development ' + - 'and production certificates.', - }, - TOO_MANY_TOPICS: { - code: 'too-many-topics', - message: 'The maximum number of topics the provided registration token can be subscribed to ' + - 'has been exceeded.', - }, - AUTHENTICATION_ERROR: { - code: 'authentication-error', - message: 'An error occurred when trying to authenticate to the FCM servers. Make sure the ' + - 'credential used to authenticate this SDK has the proper permissions. See ' + - 'https://firebase.google.com/docs/admin/setup for setup instructions.', - }, - SERVER_UNAVAILABLE: { - code: 'server-unavailable', - message: 'The FCM server could not process the request in time. See the error documentation ' + - 'for more details.', - }, - INTERNAL_ERROR: { - code: 'internal-error', - message: 'An internal error has occurred. Please retry the request.', - }, - UNKNOWN_ERROR: { - code: 'unknown-error', - message: 'An unknown server error was returned.', - }, -} satisfies Record; - -// eslint-disable-next-line @typescript-eslint/naming-convention -export const InstallationsClientErrorCode = { - INVALID_ARGUMENT: { - code: 'invalid-argument', - message: 'Invalid argument provided.', - }, - INVALID_PROJECT_ID: { - code: 'invalid-project-id', - message: 'Invalid project ID provided.', - }, - INVALID_INSTALLATION_ID: { - code: 'invalid-installation-id', - message: 'Invalid installation ID provided.', - }, - API_ERROR: { - code: 'api-error', - message: 'Installation ID API call failed.', - }, -} satisfies Record; - -// eslint-disable-next-line @typescript-eslint/naming-convention -export const InstanceIdClientErrorCode = { - ...InstallationsClientErrorCode, - INVALID_INSTANCE_ID: { - code: 'invalid-instance-id', - message: 'Invalid instance ID provided.', - }, -} satisfies Record; - -export type ProjectManagementErrorCode = - 'already-exists' - | 'authentication-error' - | 'internal-error' - | 'invalid-argument' - | 'invalid-project-id' - | 'invalid-server-response' - | 'not-found' - | 'service-unavailable' - | 'unknown-error'; - -/** @const {ServerToClientCode} Auth server to client enum error codes. */ -const AUTH_SERVER_TO_CLIENT_CODE: ServerToClientCode = { - // Feature being configured or used requires a billing account. - BILLING_NOT_ENABLED: 'BILLING_NOT_ENABLED', - // Claims payload is too large. - CLAIMS_TOO_LARGE: 'CLAIMS_TOO_LARGE', - // Configuration being added already exists. - CONFIGURATION_EXISTS: 'CONFIGURATION_EXISTS', - // Configuration not found. - CONFIGURATION_NOT_FOUND: 'CONFIGURATION_NOT_FOUND', - // Provided credential has insufficient permissions. - INSUFFICIENT_PERMISSION: 'INSUFFICIENT_PERMISSION', - // Provided configuration has invalid fields. - INVALID_CONFIG: 'INVALID_CONFIG', - // Provided configuration identifier is invalid. - INVALID_CONFIG_ID: 'INVALID_PROVIDER_ID', - // ActionCodeSettings missing continue URL. - INVALID_CONTINUE_URI: 'INVALID_CONTINUE_URI', - // Dynamic link domain in provided ActionCodeSettings is not authorized. - INVALID_DYNAMIC_LINK_DOMAIN: 'INVALID_DYNAMIC_LINK_DOMAIN', - // Hosting link domain in provided ActionCodeSettings is not owned by the current project. - INVALID_HOSTING_LINK_DOMAIN: 'INVALID_HOSTING_LINK_DOMAIN', - // uploadAccount provides an email that already exists. - DUPLICATE_EMAIL: 'EMAIL_ALREADY_EXISTS', - // uploadAccount provides a localId that already exists. - DUPLICATE_LOCAL_ID: 'UID_ALREADY_EXISTS', - // Request specified a multi-factor enrollment ID that already exists. - DUPLICATE_MFA_ENROLLMENT_ID: 'SECOND_FACTOR_UID_ALREADY_EXISTS', - // setAccountInfo email already exists. - EMAIL_EXISTS: 'EMAIL_ALREADY_EXISTS', - // /accounts:sendOobCode for password reset when user is not found. - EMAIL_NOT_FOUND: 'EMAIL_NOT_FOUND', - // Reserved claim name. - FORBIDDEN_CLAIM: 'FORBIDDEN_CLAIM', - // Invalid claims provided. - INVALID_CLAIMS: 'INVALID_CLAIMS', - // Invalid session cookie duration. - INVALID_DURATION: 'INVALID_SESSION_COOKIE_DURATION', - // Invalid email provided. - INVALID_EMAIL: 'INVALID_EMAIL', - // Invalid new email provided. - INVALID_NEW_EMAIL: 'INVALID_NEW_EMAIL', - // Invalid tenant display name. This can be thrown on CreateTenant and UpdateTenant. - INVALID_DISPLAY_NAME: 'INVALID_DISPLAY_NAME', - // Invalid ID token provided. - INVALID_ID_TOKEN: 'INVALID_ID_TOKEN', - // Invalid tenant/parent resource name. - INVALID_NAME: 'INVALID_NAME', - // OIDC configuration has an invalid OAuth client ID. - INVALID_OAUTH_CLIENT_ID: 'INVALID_OAUTH_CLIENT_ID', - // Invalid page token. - INVALID_PAGE_SELECTION: 'INVALID_PAGE_TOKEN', - // Invalid phone number. - INVALID_PHONE_NUMBER: 'INVALID_PHONE_NUMBER', - // Invalid agent project. Either agent project doesn't exist or didn't enable multi-tenancy. - INVALID_PROJECT_ID: 'INVALID_PROJECT_ID', - // Invalid provider ID. - INVALID_PROVIDER_ID: 'INVALID_PROVIDER_ID', - // Invalid service account. - INVALID_SERVICE_ACCOUNT: 'INVALID_SERVICE_ACCOUNT', - // Invalid testing phone number. - INVALID_TESTING_PHONE_NUMBER: 'INVALID_TESTING_PHONE_NUMBER', - // Invalid tenant type. - INVALID_TENANT_TYPE: 'INVALID_TENANT_TYPE', - // Missing Android package name. - MISSING_ANDROID_PACKAGE_NAME: 'MISSING_ANDROID_PACKAGE_NAME', - // Missing configuration. - MISSING_CONFIG: 'MISSING_CONFIG', - // Missing configuration identifier. - MISSING_CONFIG_ID: 'MISSING_PROVIDER_ID', - // Missing tenant display name: This can be thrown on CreateTenant and UpdateTenant. - MISSING_DISPLAY_NAME: 'MISSING_DISPLAY_NAME', - // Email is required for the specified action. For example a multi-factor user requires - // a verified email. - MISSING_EMAIL: 'MISSING_EMAIL', - // Missing iOS bundle ID. - MISSING_IOS_BUNDLE_ID: 'MISSING_IOS_BUNDLE_ID', - // Missing OIDC issuer. - MISSING_ISSUER: 'MISSING_ISSUER', - // No localId provided (deleteAccount missing localId). - MISSING_LOCAL_ID: 'MISSING_UID', - // OIDC configuration is missing an OAuth client ID. - MISSING_OAUTH_CLIENT_ID: 'MISSING_OAUTH_CLIENT_ID', - // Missing provider ID. - MISSING_PROVIDER_ID: 'MISSING_PROVIDER_ID', - // Missing SAML RP config. - MISSING_SAML_RELYING_PARTY_CONFIG: 'MISSING_SAML_RELYING_PARTY_CONFIG', - // Empty user list in uploadAccount. - MISSING_USER_ACCOUNT: 'MISSING_UID', - // Password auth disabled in console. - OPERATION_NOT_ALLOWED: 'OPERATION_NOT_ALLOWED', - // Provided credential has insufficient permissions. - PERMISSION_DENIED: 'INSUFFICIENT_PERMISSION', - // Phone number already exists. - PHONE_NUMBER_EXISTS: 'PHONE_NUMBER_ALREADY_EXISTS', - // Project not found. - PROJECT_NOT_FOUND: 'PROJECT_NOT_FOUND', - // In multi-tenancy context: project creation quota exceeded. - QUOTA_EXCEEDED: 'QUOTA_EXCEEDED', - // Currently only 5 second factors can be set on the same user. - SECOND_FACTOR_LIMIT_EXCEEDED: 'SECOND_FACTOR_LIMIT_EXCEEDED', - // Tenant not found. - TENANT_NOT_FOUND: 'TENANT_NOT_FOUND', - // Tenant ID mismatch. - TENANT_ID_MISMATCH: 'MISMATCHING_TENANT_ID', - // Token expired error. - TOKEN_EXPIRED: 'ID_TOKEN_EXPIRED', - // Continue URL provided in ActionCodeSettings has a domain that is not whitelisted. - UNAUTHORIZED_DOMAIN: 'UNAUTHORIZED_DOMAIN', - // A multi-factor user requires a supported first factor. - UNSUPPORTED_FIRST_FACTOR: 'UNSUPPORTED_FIRST_FACTOR', - // The request specified an unsupported type of second factor. - UNSUPPORTED_SECOND_FACTOR: 'UNSUPPORTED_SECOND_FACTOR', - // Operation is not supported in a multi-tenant context. - UNSUPPORTED_TENANT_OPERATION: 'UNSUPPORTED_TENANT_OPERATION', - // A verified email is required for the specified action. For example a multi-factor user - // requires a verified email. - UNVERIFIED_EMAIL: 'UNVERIFIED_EMAIL', - // User on which action is to be performed is not found. - USER_NOT_FOUND: 'USER_NOT_FOUND', - // User record is disabled. - USER_DISABLED: 'USER_DISABLED', - // Password provided is too weak. - WEAK_PASSWORD: 'INVALID_PASSWORD', - // Unrecognized reCAPTCHA action. - INVALID_RECAPTCHA_ACTION: 'INVALID_RECAPTCHA_ACTION', - // Unrecognized reCAPTCHA enforcement state. - INVALID_RECAPTCHA_ENFORCEMENT_STATE: 'INVALID_RECAPTCHA_ENFORCEMENT_STATE', - // reCAPTCHA is not enabled for account defender. - RECAPTCHA_NOT_ENABLED: 'RECAPTCHA_NOT_ENABLED' -}; - -/** @const {ServerToClientCode} Messaging server to client enum error codes. */ -const MESSAGING_SERVER_TO_CLIENT_CODE: ServerToClientCode = { - /* GENERIC ERRORS */ - // Generic invalid message parameter provided. - InvalidParameters: 'INVALID_ARGUMENT', - // Mismatched sender ID. - MismatchSenderId: 'MISMATCHED_CREDENTIAL', - // FCM server unavailable. - Unavailable: 'SERVER_UNAVAILABLE', - // FCM server internal error. - InternalServerError: 'INTERNAL_ERROR', - - /* SEND ERRORS */ - // Invalid registration token format. - InvalidRegistration: 'INVALID_REGISTRATION_TOKEN', - // Registration token is not registered. - NotRegistered: 'REGISTRATION_TOKEN_NOT_REGISTERED', - // Registration token does not match restricted package name. - InvalidPackageName: 'INVALID_PACKAGE_NAME', - // Message payload size limit exceeded. - MessageTooBig: 'PAYLOAD_SIZE_LIMIT_EXCEEDED', - // Invalid key in the data message payload. - InvalidDataKey: 'INVALID_DATA_PAYLOAD_KEY', - // Invalid time to live option. - InvalidTtl: 'INVALID_OPTIONS', - // Device message rate exceeded. - DeviceMessageRateExceeded: 'DEVICE_MESSAGE_RATE_EXCEEDED', - // Topics message rate exceeded. - TopicsMessageRateExceeded: 'TOPICS_MESSAGE_RATE_EXCEEDED', - // Invalid APNs credentials. - InvalidApnsCredential: 'THIRD_PARTY_AUTH_ERROR', - - /* FCM v1 canonical error codes */ - NOT_FOUND: 'REGISTRATION_TOKEN_NOT_REGISTERED', - PERMISSION_DENIED: 'MISMATCHED_CREDENTIAL', - RESOURCE_EXHAUSTED: 'MESSAGE_RATE_EXCEEDED', - UNAUTHENTICATED: 'THIRD_PARTY_AUTH_ERROR', - - /* FCM v1 new error codes */ - APNS_AUTH_ERROR: 'THIRD_PARTY_AUTH_ERROR', - INTERNAL: 'INTERNAL_ERROR', - INVALID_ARGUMENT: 'INVALID_ARGUMENT', - QUOTA_EXCEEDED: 'MESSAGE_RATE_EXCEEDED', - SENDER_ID_MISMATCH: 'MISMATCHED_CREDENTIAL', - THIRD_PARTY_AUTH_ERROR: 'THIRD_PARTY_AUTH_ERROR', - UNAVAILABLE: 'SERVER_UNAVAILABLE', - UNREGISTERED: 'REGISTRATION_TOKEN_NOT_REGISTERED', - UNSPECIFIED_ERROR: 'UNKNOWN_ERROR', -}; - -/** @const {ServerToClientCode} Topic management (IID) server to client enum error codes. */ -const TOPIC_MGT_SERVER_TO_CLIENT_CODE: ServerToClientCode = { - /* TOPIC SUBSCRIPTION MANAGEMENT ERRORS */ - NOT_FOUND: 'REGISTRATION_TOKEN_NOT_REGISTERED', - INVALID_ARGUMENT: 'INVALID_REGISTRATION_TOKEN', - TOO_MANY_TOPICS: 'TOO_MANY_TOPICS', - RESOURCE_EXHAUSTED: 'TOO_MANY_TOPICS', - PERMISSION_DENIED: 'AUTHENTICATION_ERROR', - DEADLINE_EXCEEDED: 'SERVER_UNAVAILABLE', - INTERNAL: 'INTERNAL_ERROR', - UNKNOWN: 'UNKNOWN_ERROR', -}; diff --git a/test/unit/app-check/app-check-api-client-internal.spec.ts b/test/unit/app-check/app-check-api-client-internal.spec.ts index d25c87d0a9..e9e2bfad8b 100644 --- a/test/unit/app-check/app-check-api-client-internal.spec.ts +++ b/test/unit/app-check/app-check-api-client-internal.spec.ts @@ -26,8 +26,10 @@ import * as mocks from '../../resources/mocks'; import { getMetricsHeader, getSdkVersion } from '../../../src/utils'; import { FirebaseApp } from '../../../src/app/firebase-app'; -import { AppCheckApiClient, FirebaseAppCheckError } from '../../../src/app-check/app-check-api-client-internal'; -import { FirebaseAppError, toHttpResponse } from '../../../src/utils/error'; +import { AppCheckApiClient } from '../../../src/app-check/app-check-api-client-internal'; +import { FirebaseAppCheckError } from '../../../src/app-check/error'; +import { toHttpResponse } from '../../../src/utils/error'; +import { FirebaseAppError } from '../../../src/app/error'; import { deepCopy } from '../../../src/utils/deep-copy'; const expect = chai.expect; diff --git a/test/unit/app-check/app-check.spec.ts b/test/unit/app-check/app-check.spec.ts index 51f2f8b016..0c42fd61d2 100644 --- a/test/unit/app-check/app-check.spec.ts +++ b/test/unit/app-check/app-check.spec.ts @@ -24,7 +24,8 @@ import * as mocks from '../../resources/mocks'; import { FirebaseApp } from '../../../src/app/firebase-app'; import { AppCheck } from '../../../src/app-check/index'; -import { AppCheckApiClient, FirebaseAppCheckError } from '../../../src/app-check/app-check-api-client-internal'; +import { AppCheckApiClient } from '../../../src/app-check/app-check-api-client-internal'; +import { FirebaseAppCheckError } from '../../../src/app-check/error'; import { AppCheckTokenGenerator } from '../../../src/app-check/token-generator'; import { HttpClient } from '../../../src/utils/api-request'; import { ServiceAccountSigner } from '../../../src/utils/crypto-signer'; diff --git a/test/unit/app-check/token-generator.spec.ts b/test/unit/app-check/token-generator.spec.ts index d629e62e90..edfc57a3e0 100644 --- a/test/unit/app-check/token-generator.spec.ts +++ b/test/unit/app-check/token-generator.spec.ts @@ -33,7 +33,7 @@ import { CryptoSignerError, CryptoSignerErrorCode, ServiceAccountSigner } from '../../../src/utils/crypto-signer'; import { ServiceAccountCredential } from '../../../src/app/credential-internal'; -import { FirebaseAppCheckError } from '../../../src/app-check/app-check-api-client-internal'; +import { FirebaseAppCheckError } from '../../../src/app-check/error'; import * as utils from '../utils'; chai.should(); diff --git a/test/unit/app/credential-internal.spec.ts b/test/unit/app/credential-internal.spec.ts index d1d7d89549..2d56a74dd0 100644 --- a/test/unit/app/credential-internal.spec.ts +++ b/test/unit/app/credential-internal.spec.ts @@ -37,7 +37,7 @@ import { getApplicationDefault, isApplicationDefault, ImpersonatedServiceAccountCredential, ApplicationDefaultCredential } from '../../../src/app/credential-internal'; import { deepCopy } from '../../../src/utils/deep-copy'; -import { FirebaseAppError } from '../../../src/utils/error'; +import { FirebaseAppError } from '../../../src/app/error'; chai.should(); chai.use(sinonChai); diff --git a/test/unit/app/firebase-app.spec.ts b/test/unit/app/firebase-app.spec.ts index fa55b91fa9..ce2b07cdbc 100644 --- a/test/unit/app/firebase-app.spec.ts +++ b/test/unit/app/firebase-app.spec.ts @@ -35,7 +35,7 @@ import { auth, messaging, machineLearning, storage, firestore, database, instanceId, installations, projectManagement, securityRules, remoteConfig, appCheck, } from '../../../src/firebase-namespace-api'; -import { FirebaseAppError, AppErrorCodes } from '../../../src/utils/error'; +import { FirebaseAppError, AppErrorCode } from '../../../src/app/error'; import Auth = auth.Auth; import Database = database.Database; @@ -850,7 +850,7 @@ describe('FirebaseApp', () => { it('Includes the original error in exception', async () => { getTokenStub.restore(); const mockError = new FirebaseAppError({ - code: AppErrorCodes.INVALID_CREDENTIAL, + code: AppErrorCode.INVALID_CREDENTIAL, message: 'Something went wrong' }); getTokenStub = sinon.stub(ServiceAccountCredential.prototype, 'getAccessToken').rejects(mockError); @@ -869,7 +869,7 @@ describe('FirebaseApp', () => { it('Returns a detailed message when an error is due to an invalid_grant', async () => { getTokenStub.restore(); const mockError = new FirebaseAppError({ - code: AppErrorCodes.INVALID_CREDENTIAL, + code: AppErrorCode.INVALID_CREDENTIAL, message: 'Failed to get credentials: invalid_grant (reason)' }); getTokenStub = sinon.stub(ServiceAccountCredential.prototype, 'getAccessToken').rejects(mockError); diff --git a/test/unit/auth/action-code-settings-builder.spec.ts b/test/unit/auth/action-code-settings-builder.spec.ts index f8eed33d74..eb3fadf774 100644 --- a/test/unit/auth/action-code-settings-builder.spec.ts +++ b/test/unit/auth/action-code-settings-builder.spec.ts @@ -20,7 +20,7 @@ import * as sinonChai from 'sinon-chai'; import * as chaiAsPromised from 'chai-as-promised'; import { ActionCodeSettingsBuilder } from '../../../src/auth/action-code-settings-builder'; -import { AuthClientErrorCode } from '../../../src/utils/error'; +import { authClientErrorCode } from '../../../src/auth/error'; chai.should(); @@ -75,7 +75,7 @@ describe('ActionCodeSettingsBuilder', () => { dynamicLinkDomain: 'custom.page.link', linkDomain: TEST_LINK_DOMAIN, } as any); - }).to.throw(AuthClientErrorCode.MISSING_CONTINUE_URI.message); + }).to.throw(authClientErrorCode.MISSING_CONTINUE_URI.message); }); const invalidUrls = [null, NaN, 0, 1, true, false, '', 'a', [], [1, 'a'], {}, { a: 1 }, _.noop]; @@ -85,7 +85,7 @@ describe('ActionCodeSettingsBuilder', () => { return new ActionCodeSettingsBuilder({ url, } as any); - }).to.throw(AuthClientErrorCode.INVALID_CONTINUE_URI.message); + }).to.throw(authClientErrorCode.INVALID_CONTINUE_URI.message); }); }); @@ -110,7 +110,7 @@ describe('ActionCodeSettingsBuilder', () => { handleCodeInApp: true, dynamicLinkDomain: domain, } as any); - }).to.throw(AuthClientErrorCode.INVALID_DYNAMIC_LINK_DOMAIN.message); + }).to.throw(authClientErrorCode.INVALID_DYNAMIC_LINK_DOMAIN.message); }); }); @@ -124,7 +124,7 @@ describe('ActionCodeSettingsBuilder', () => { handleCodeInApp: true, linkDomain: domain, } as any); - }).to.throw(AuthClientErrorCode.INVALID_HOSTING_LINK_DOMAIN.message); + }).to.throw(authClientErrorCode.INVALID_HOSTING_LINK_DOMAIN.message); }); }); @@ -148,7 +148,7 @@ describe('ActionCodeSettingsBuilder', () => { handleCodeInApp: true, iOS: {}, } as any); - }).to.throw(AuthClientErrorCode.MISSING_IOS_BUNDLE_ID.message); + }).to.throw(authClientErrorCode.MISSING_IOS_BUNDLE_ID.message); }); const invalidBundleIds = [null, NaN, 0, 1, true, false, '', ['com.example.ios'], _.noop]; @@ -184,7 +184,7 @@ describe('ActionCodeSettingsBuilder', () => { handleCodeInApp: true, android: {}, } as any); - }).to.throw(AuthClientErrorCode.MISSING_ANDROID_PACKAGE_NAME.message); + }).to.throw(authClientErrorCode.MISSING_ANDROID_PACKAGE_NAME.message); }); const invalidPackageNames = [null, NaN, 0, 1, true, false, '', ['com.example.android'], _.noop]; diff --git a/test/unit/auth/auth-api-request.spec.ts b/test/unit/auth/auth-api-request.spec.ts index 8bcad5c2ac..36872c4498 100644 --- a/test/unit/auth/auth-api-request.spec.ts +++ b/test/unit/auth/auth-api-request.spec.ts @@ -38,7 +38,7 @@ import { EMAIL_ACTION_REQUEST_TYPES, TenantAwareAuthRequestHandler, AbstractAuthRequestHandler, } from '../../../src/auth/auth-api-request'; import { UserImportBuilder } from '../../../src/auth/user-import-builder'; -import { AuthClientErrorCode, FirebaseAuthError } from '../../../src/utils/error'; +import { authClientErrorCode, FirebaseAuthError } from '../../../src/auth/error'; import { ActionCodeSettingsBuilder } from '../../../src/auth/action-code-settings-builder'; import { SAMLConfigServerResponse } from '../../../src/auth/auth-config'; import { expectUserImportResult } from './user-import-builder.spec'; @@ -1015,7 +1015,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); it('should be rejected given an invalid ID token', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_ID_TOKEN, + authClientErrorCode.INVALID_ID_TOKEN, ); const requestHandler = handler.init(mockApp); @@ -1028,7 +1028,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); it('should be rejected given an invalid duration', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_SESSION_COOKIE_DURATION, + authClientErrorCode.INVALID_SESSION_COOKIE_DURATION, ); const requestHandler = handler.init(mockApp); @@ -1041,7 +1041,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); it('should be rejected given a duration less than minimum allowed', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_SESSION_COOKIE_DURATION, + authClientErrorCode.INVALID_SESSION_COOKIE_DURATION, ); const outOfBoundDuration = 60 * 1000 * 5 - 1; @@ -1055,7 +1055,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); it('should be rejected given a duration greater than maximum allowed', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_SESSION_COOKIE_DURATION, + authClientErrorCode.INVALID_SESSION_COOKIE_DURATION, ); // Add more than a second since this value is Math.floor()'ed const outOfBoundDuration = 60 * 60 * 1000 * 24 * 14 + 1001; @@ -1074,7 +1074,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { message: 'INVALID_ID_TOKEN', }, }); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_ID_TOKEN); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_ID_TOKEN); const data = { idToken: 'invalid-token', validDuration: durationInMs / 1000 }; const stub = sinon.stub(HttpClient.prototype, 'send').rejects(expectedResult); stubs.push(stub); @@ -1120,7 +1120,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const expectedResult = utils.responseFrom({ kind: 'identitytoolkit#GetAccountInfoResponse', }); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.USER_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.USER_NOT_FOUND); const data = { email: ['user@example.com'] }; const stub = sinon.stub(HttpClient.prototype, 'send').resolves(expectedResult); stubs.push(stub); @@ -1160,7 +1160,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const expectedResult = utils.responseFrom({ kind: 'identitytoolkit#GetAccountInfoResponse', }); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.USER_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.USER_NOT_FOUND); const data = { localId: ['uid'] }; const stub = sinon.stub(HttpClient.prototype, 'send').resolves(expectedResult); stubs.push(stub); @@ -1232,7 +1232,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); it('should be rejected given an invalid phoneNumber', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_PHONE_NUMBER); + authClientErrorCode.INVALID_PHONE_NUMBER); const stub = sinon.stub(HttpClient.prototype, 'send'); stubs.push(stub); @@ -1250,7 +1250,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const expectedResult = utils.responseFrom({ kind: 'identitytoolkit#GetAccountInfoResponse', }); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.USER_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.USER_NOT_FOUND); const data = { phoneNumber: ['+11234567890'], }; @@ -1443,7 +1443,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should throw on invalid options without making an underlying API call', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_ALGORITHM, + authClientErrorCode.INVALID_HASH_ALGORITHM, 'Unsupported hash algorithm provider "invalid".', ); const invalidOptions = { @@ -1463,7 +1463,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should throw when 1001 UserImportRecords are provided', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.MAXIMUM_USER_COUNT_EXCEEDED, + authClientErrorCode.MAXIMUM_USER_COUNT_EXCEEDED, 'A maximum of 1000 users can be imported at once.', ); const stub = sinon.stub(HttpClient.prototype, 'send'); @@ -1490,7 +1490,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const mismatchIndex = 34; const mismatchTenantId = 'MISMATCHING-TENANT-ID'; const expectedError = new FirebaseAuthError( - AuthClientErrorCode.MISMATCHING_TENANT_ID, + authClientErrorCode.MISMATCHING_TENANT_ID, `UserRecord of index "${mismatchIndex}" has mismatching tenant ID "${mismatchTenantId}"`, ); const stub = sinon.stub(HttpClient.prototype, 'send'); @@ -1585,8 +1585,8 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { successCount: 0, failureCount: 2, errors: [ - { index: 0, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_PHONE_NUMBER) }, - { index: 1, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_EMAIL) }, + { index: 0, error: new FirebaseAuthError(authClientErrorCode.INVALID_PHONE_NUMBER) }, + { index: 1, error: new FirebaseAuthError(authClientErrorCode.INVALID_EMAIL) }, ], }; const stub = sinon.stub(HttpClient.prototype, 'send'); @@ -1713,79 +1713,79 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { successCount: 0, failureCount: testUsers.length, errors: [ - { index: 0, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_DISPLAY_NAME) }, - { index: 1, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_UID) }, - { index: 2, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_EMAIL) }, - { index: 3, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_PHONE_NUMBER) }, - { index: 4, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_EMAIL_VERIFIED) }, - { index: 5, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_PHOTO_URL) }, - { index: 6, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_DISABLED_FIELD) }, - { index: 7, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_CREATION_TIME) }, - { index: 8, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_LAST_SIGN_IN_TIME) }, + { index: 0, error: new FirebaseAuthError(authClientErrorCode.INVALID_DISPLAY_NAME) }, + { index: 1, error: new FirebaseAuthError(authClientErrorCode.INVALID_UID) }, + { index: 2, error: new FirebaseAuthError(authClientErrorCode.INVALID_EMAIL) }, + { index: 3, error: new FirebaseAuthError(authClientErrorCode.INVALID_PHONE_NUMBER) }, + { index: 4, error: new FirebaseAuthError(authClientErrorCode.INVALID_EMAIL_VERIFIED) }, + { index: 5, error: new FirebaseAuthError(authClientErrorCode.INVALID_PHOTO_URL) }, + { index: 6, error: new FirebaseAuthError(authClientErrorCode.INVALID_DISABLED_FIELD) }, + { index: 7, error: new FirebaseAuthError(authClientErrorCode.INVALID_CREATION_TIME) }, + { index: 8, error: new FirebaseAuthError(authClientErrorCode.INVALID_LAST_SIGN_IN_TIME) }, { index: 9, error: new FirebaseAuthError( - AuthClientErrorCode.FORBIDDEN_CLAIM, + authClientErrorCode.FORBIDDEN_CLAIM, 'Developer claim "aud" is reserved and cannot be specified.', ), }, - { index: 10, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_PASSWORD_HASH) }, - { index: 11, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_PASSWORD_SALT) }, + { index: 10, error: new FirebaseAuthError(authClientErrorCode.INVALID_PASSWORD_HASH) }, + { index: 11, error: new FirebaseAuthError(authClientErrorCode.INVALID_PASSWORD_SALT) }, { index: 12, error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_UID, + authClientErrorCode.INVALID_UID, 'The provider "uid" for "google.com" must be a valid non-empty string.', ), }, { index: 13, error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_DISPLAY_NAME, + authClientErrorCode.INVALID_DISPLAY_NAME, 'The provider "displayName" for "google.com" must be a valid string.', ), }, { index: 14, error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_EMAIL, + authClientErrorCode.INVALID_EMAIL, 'The provider "email" for "google.com" must be a valid email string.', ), }, { index: 15, error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_PHOTO_URL, + authClientErrorCode.INVALID_PHOTO_URL, 'The provider "photoURL" for "google.com" must be a valid URL string.', ), }, - { index: 16, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID) }, - { index: 17, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_UID) }, + { index: 16, error: new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID) }, + { index: 17, error: new FirebaseAuthError(authClientErrorCode.INVALID_UID) }, { index: 18, error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_UID, + authClientErrorCode.INVALID_UID, 'The second factor "uid" must be a valid non-empty string.', ), }, { index: 19, error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_DISPLAY_NAME, + authClientErrorCode.INVALID_DISPLAY_NAME, 'The second factor "displayName" for "mfaUid1" must be a valid string.', ), }, { index: 20, error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_ENROLLMENT_TIME, + authClientErrorCode.INVALID_ENROLLMENT_TIME, 'The second factor "enrollmentTime" for "mfaUid2" must be a valid UTC date string.', ), }, { index: 21, error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_PHONE_NUMBER, + authClientErrorCode.INVALID_PHONE_NUMBER, 'The second factor "phoneNumber" for "mfaUid3" must be a non-empty ' + 'E.164 standard compliant identifier string.', ), @@ -1793,7 +1793,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { { index: 22, error: new FirebaseAuthError( - AuthClientErrorCode.UNSUPPORTED_SECOND_FACTOR, + authClientErrorCode.UNSUPPORTED_SECOND_FACTOR, `Unsupported second factor "${JSON.stringify(testUsers[22].multiFactor.enrolledFactors[0])}" provided.`, ), }, @@ -1817,7 +1817,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }, }); const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'An internal error has occurred. Raw server response: ' + `"${JSON.stringify(expectedServerError.response.data)}"`, ); @@ -1897,7 +1897,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); it('should be rejected given an invalid maxResults', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'Required "maxResults" must be a positive integer that does not ' + 'exceed 1000.', ); @@ -1912,7 +1912,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); it('should be rejected given an invalid next page token', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_PAGE_TOKEN, + authClientErrorCode.INVALID_PAGE_TOKEN, ); const requestHandler = handler.init(mockApp); @@ -2272,7 +2272,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given invalid parameters such as email', () => { // Expected error when an invalid email is provided. - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_EMAIL); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_EMAIL); const requestHandler = handler.init(mockApp); // Send update request with invalid email. return requestHandler.updateExistingAccount(uid, invalidData) @@ -2294,7 +2294,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { { name: 'invalid second factor uid', error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_UID, + authClientErrorCode.INVALID_UID, 'The second factor "uid" must be a valid non-empty string.', ), secondFactor: { @@ -2307,7 +2307,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { { name: 'invalid second factor display name', error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_DISPLAY_NAME, + authClientErrorCode.INVALID_DISPLAY_NAME, 'The second factor "displayName" for "enrolledSecondFactor1" must be a valid string.', ), secondFactor: { @@ -2320,7 +2320,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { { name: 'invalid second factor phone number', error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_PHONE_NUMBER, + authClientErrorCode.INVALID_PHONE_NUMBER, 'The second factor "phoneNumber" for "enrolledSecondFactor1" must be a non-empty ' + 'E.164 standard compliant identifier string.'), secondFactor: { @@ -2333,7 +2333,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { { name: 'invalid second factor enrollment time', error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_ENROLLMENT_TIME, + authClientErrorCode.INVALID_ENROLLMENT_TIME, 'The second factor "enrollmentTime" for "enrolledSecondFactor1" must be a valid ' + 'UTC date string.'), secondFactor: { @@ -2347,7 +2347,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { { name: 'invalid second factor type', error: new FirebaseAuthError( - AuthClientErrorCode.UNSUPPORTED_SECOND_FACTOR, + authClientErrorCode.UNSUPPORTED_SECOND_FACTOR, `Unsupported second factor "${JSON.stringify(unsupportedSecondFactor)}" provided.`), secondFactor: unsupportedSecondFactor, }, @@ -2375,7 +2375,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { (dataWithModifiedTenantId as any).tenantId = 'MODIFIED-TENANT-ID'; // Expected error when a tenant ID is provided. const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"tenantId" is an invalid "UpdateRequest" property.', ); const requestHandler = handler.init(mockApp); @@ -2391,7 +2391,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given invalid parameters such as phoneNumber', () => { // Expected error when an invalid phone number is provided. - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_PHONE_NUMBER); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_PHONE_NUMBER); const requestHandler = handler.init(mockApp); // Send update request with invalid phone number. return requestHandler.updateExistingAccount(uid, invalidPhoneNumberData) @@ -2480,7 +2480,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given invalid parameters such as uid', () => { // Expected error when an invalid uid is provided. - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_UID); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_UID); const requestHandler = handler.init(mockApp); // Send request with invalid uid. return requestHandler.setCustomUserClaims('', claims) @@ -2495,7 +2495,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given invalid parameters such as customClaims', () => { // Expected error when invalid claims are provided. const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'CustomUserClaims argument must be an object or null.', ); const requestHandler = handler.init(mockApp); @@ -2512,7 +2512,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given customClaims with blacklisted claims', () => { // Expected error when invalid claims are provided. const expectedError = new FirebaseAuthError( - AuthClientErrorCode.FORBIDDEN_CLAIM, + authClientErrorCode.FORBIDDEN_CLAIM, 'Developer claim "aud" is reserved and cannot be specified.', ); const requestHandler = handler.init(mockApp); @@ -2588,7 +2588,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); it('should be rejected given an invalid uid', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_UID); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_UID); const invalidUid: any = { localId: uid }; const requestHandler = handler.init(mockApp); @@ -2730,7 +2730,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given invalid parameters such as email', () => { // Expected error when an invalid email is provided. - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_EMAIL); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_EMAIL); const requestHandler = handler.init(mockApp); // Create new account with invalid email. return requestHandler.createNewAccount(invalidData) @@ -2778,7 +2778,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { { name: 'unsupported second factor uid', error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"uid" is not supported when adding second factors via "createUser()"', ), secondFactor: { @@ -2791,7 +2791,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { { name: 'invalid second factor display name', error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_DISPLAY_NAME, + authClientErrorCode.INVALID_DISPLAY_NAME, 'The second factor "displayName" for "+16505557348" must be a valid string.', ), secondFactor: { @@ -2803,7 +2803,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { { name: 'invalid second factor phone number', error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_PHONE_NUMBER, + authClientErrorCode.INVALID_PHONE_NUMBER, 'The second factor "phoneNumber" for "invalid" must be a non-empty ' + 'E.164 standard compliant identifier string.'), secondFactor: { @@ -2815,7 +2815,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { { name: 'unsupported second factor enrollment time', error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"enrollmentTime" is not supported when adding second factors via "createUser()"'), secondFactor: { phoneNumber: '+16505557348', @@ -2827,7 +2827,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { { name: 'invalid second factor type', error: new FirebaseAuthError( - AuthClientErrorCode.UNSUPPORTED_SECOND_FACTOR, + authClientErrorCode.UNSUPPORTED_SECOND_FACTOR, `Unsupported second factor "${JSON.stringify(unsupportedSecondFactor)}" provided.`), secondFactor: unsupportedSecondFactor, }, @@ -2857,7 +2857,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given tenantId in CreateRequest', () => { // Expected error when a tenantId is provided. const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"tenantId" is an invalid "CreateRequest" property.'); const validDataWithTenantId = deepCopy(validData); (validDataWithTenantId as any).tenantId = TENANT_ID; @@ -2875,7 +2875,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given invalid parameters such as phoneNumber', () => { // Expected error when an invalid phone number is provided. - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_PHONE_NUMBER); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_PHONE_NUMBER); const requestHandler = handler.init(mockApp); // Create new account with invalid phone number. return requestHandler.createNewAccount(invalidPhoneNumberData) @@ -2889,7 +2889,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected when the backend returns a user exists error', () => { // Expected error when the uid already exists. - const expectedError = new FirebaseAuthError(AuthClientErrorCode.UID_ALREADY_EXISTS); + const expectedError = new FirebaseAuthError(authClientErrorCode.UID_ALREADY_EXISTS); const expectedResult = utils.errorFrom({ error: { message: 'DUPLICATE_LOCAL_ID', @@ -2913,7 +2913,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected when the backend returns an email exists error', () => { // Expected error when the email already exists. - const expectedError = new FirebaseAuthError(AuthClientErrorCode.EMAIL_ALREADY_EXISTS); + const expectedError = new FirebaseAuthError(authClientErrorCode.EMAIL_ALREADY_EXISTS); const expectedResult = utils.errorFrom({ error: { message: 'EMAIL_EXISTS', @@ -3014,7 +3014,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given invalid parameters such as email', () => { // Expected error when an invalid email is provided. const expectedError = - new FirebaseAuthError(AuthClientErrorCode.INVALID_EMAIL); + new FirebaseAuthError(authClientErrorCode.INVALID_EMAIL); const requestHandler = handler.init(mockApp); // Send create new account request with invalid data. return requestHandler.createNewAccount(invalidData) @@ -3028,7 +3028,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given invalid parameters such as phone number', () => { // Expected error when an invalid phone number is provided. const expectedError = - new FirebaseAuthError(AuthClientErrorCode.INVALID_PHONE_NUMBER); + new FirebaseAuthError(authClientErrorCode.INVALID_PHONE_NUMBER); const requestHandler = handler.init(mockApp); // Send create new account request with invalid data. return requestHandler.createNewAccount(invalidPhoneNumberData) @@ -3182,7 +3182,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given requestType: VERIFY_AND_CHANGE and no new Email address', () => { const requestHandler = handler.init(mockApp); const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '`newEmail` is required when `requestType` === \'VERIFY_AND_CHANGE_EMAIL\'', ) @@ -3197,7 +3197,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given an invalid email', () => { const invalidEmail = 'invalid'; - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_EMAIL); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_EMAIL); const requestHandler = handler.init(mockApp); return requestHandler.getEmailActionLink('PASSWORD_RESET', invalidEmail, actionCodeSettings) @@ -3211,7 +3211,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given an invalid new email', () => { const invalidNewEmail = 'invalid'; - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_NEW_EMAIL); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_NEW_EMAIL); const requestHandler = handler.init(mockApp); return requestHandler.getEmailActionLink('VERIFY_AND_CHANGE_EMAIL', email, actionCodeSettings, invalidNewEmail) @@ -3226,7 +3226,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given an invalid request type', () => { const invalidRequestType = 'invalid'; const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"invalid" is not a supported email action request type.', ); @@ -3243,7 +3243,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given an invalid ActionCodeSettings object', () => { const invalidActionCodeSettings = 'invalid' as any; const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"ActionCodeSettings" must be a non-null object.', ); @@ -3259,7 +3259,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected when the response does not contain a link', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create the email action link'); const requestData = deepExtend({ requestType: 'VERIFY_EMAIL', @@ -3333,7 +3333,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { null, NaN, 0, 1, true, false, '', 'saml.provider', ['oidc.provider'], [], {}, { a: 1 }, _.noop]; invalidProviderIds.forEach((invalidProviderId) => { it('should be rejected given an invalid provider ID:' + JSON.stringify(invalidProviderId), () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID); const requestHandler = handler.init(mockApp); return requestHandler.getOAuthIdpConfig(invalidProviderId as any) @@ -3346,7 +3346,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); it('should be rejected given a backend error', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.CONFIGURATION_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.CONFIGURATION_NOT_FOUND); const expectedServerError = utils.errorFrom({ error: { message: 'CONFIGURATION_NOT_FOUND', @@ -3433,7 +3433,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given an invalid maxResults', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'Required "maxResults" must be a positive integer that does not ' + 'exceed 100.', ); @@ -3449,7 +3449,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given an invalid next page token', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_PAGE_TOKEN, + authClientErrorCode.INVALID_PAGE_TOKEN, ); const requestHandler = handler.init(mockApp); @@ -3510,7 +3510,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { null, NaN, 0, 1, true, false, '', 'saml.provider', ['oidc.provider'], [], {}, { a: 1 }, _.noop]; invalidProviderIds.forEach((invalidProviderId) => { it('should be rejected given an invalid provider ID:' + JSON.stringify(invalidProviderId), () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID); const requestHandler = handler.init(mockApp); return requestHandler.deleteOAuthIdpConfig(invalidProviderId as any) @@ -3523,7 +3523,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); it('should be rejected given a backend error', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.CONFIGURATION_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.CONFIGURATION_NOT_FOUND); const expectedServerError = utils.errorFrom({ error: { message: 'CONFIGURATION_NOT_FOUND', @@ -3615,7 +3615,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given invalid parameters', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"OIDCAuthProviderConfig.issuer" must be a valid URL string.', ); const invalidOptions: OIDCAuthProviderConfig = deepCopy(configOptions); @@ -3632,7 +3632,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected when the backend returns a response missing name', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new OIDC configuration', ); const stub = sinon.stub(HttpClient.prototype, 'send').resolves(utils.responseFrom({})); @@ -3655,7 +3655,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { message: 'INVALID_CONFIG', }, }); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_CONFIG); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_CONFIG); const stub = sinon.stub(HttpClient.prototype, 'send').rejects(expectedServerError); stubs.push(stub); @@ -3800,7 +3800,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { null, NaN, 0, 1, true, false, '', 'saml.provider', ['oidc.provider'], [], {}, { a: 1 }, _.noop]; invalidProviderIds.forEach((invalidProviderId) => { it('should be rejected given an invalid provider ID:' + JSON.stringify(invalidProviderId), () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID); const requestHandler = handler.init(mockApp); return requestHandler.updateOAuthIdpConfig(invalidProviderId as any, configOptions) @@ -3814,7 +3814,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given invalid parameters', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"OIDCAuthProviderConfig.issuer" must be a valid URL string.', ); const invalidOptions: OIDCUpdateAuthProviderRequest = deepCopy(configOptions); @@ -3832,7 +3832,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected when the backend returns a response missing name', () => { const expectedPath = path + '?updateMask=enabled,displayName,issuer,clientId'; const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to update OIDC configuration', ); const stub = sinon.stub(HttpClient.prototype, 'send').resolves(utils.responseFrom({})); @@ -3856,7 +3856,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { message: 'INVALID_CONFIG', }, }); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_CONFIG); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_CONFIG); const stub = sinon.stub(HttpClient.prototype, 'send').rejects(expectedServerError); stubs.push(stub); @@ -3897,7 +3897,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { null, NaN, 0, 1, true, false, '', 'oidc.provider', ['saml.provider'], [], {}, { a: 1 }, _.noop]; invalidProviderIds.forEach((invalidProviderId) => { it('should be rejected given an invalid provider ID:' + JSON.stringify(invalidProviderId), () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID); const requestHandler = handler.init(mockApp); return requestHandler.getInboundSamlConfig(invalidProviderId as any) @@ -3910,7 +3910,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); it('should be rejected given a backend error', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.CONFIGURATION_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.CONFIGURATION_NOT_FOUND); const expectedServerError = utils.errorFrom({ error: { message: 'CONFIGURATION_NOT_FOUND', @@ -3993,7 +3993,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given an invalid maxResults', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'Required "maxResults" must be a positive integer that does not ' + 'exceed 100.', ); @@ -4009,7 +4009,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given an invalid next page token', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_PAGE_TOKEN, + authClientErrorCode.INVALID_PAGE_TOKEN, ); const requestHandler = handler.init(mockApp); @@ -4068,7 +4068,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { null, NaN, 0, 1, true, false, '', 'oidc.provider', ['saml.provider'], [], {}, { a: 1 }, _.noop]; invalidProviderIds.forEach((invalidProviderId) => { it('should be rejected given an invalid provider ID:' + JSON.stringify(invalidProviderId), () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID); const requestHandler = handler.init(mockApp); return requestHandler.deleteInboundSamlConfig(invalidProviderId as any) @@ -4081,7 +4081,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); it('should be rejected given a backend error', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.CONFIGURATION_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.CONFIGURATION_NOT_FOUND); const expectedServerError = utils.errorFrom({ error: { message: 'CONFIGURATION_NOT_FOUND', @@ -4152,7 +4152,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given invalid parameters', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"SAMLAuthProviderConfig.callbackURL" must be a valid URL string.', ); const invalidOptions: SAMLAuthProviderConfig = deepCopy(configOptions); @@ -4169,7 +4169,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected when the backend returns a response missing name', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new SAML configuration', ); const stub = sinon.stub(HttpClient.prototype, 'send').resolves(utils.responseFrom({})); @@ -4192,7 +4192,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { message: 'INVALID_CONFIG', }, }); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_CONFIG); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_CONFIG); const stub = sinon.stub(HttpClient.prototype, 'send').rejects(expectedServerError); stubs.push(stub); @@ -4344,7 +4344,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { null, NaN, 0, 1, true, false, '', 'oidc.provider', ['saml.provider'], [], {}, { a: 1 }, _.noop]; invalidProviderIds.forEach((invalidProviderId) => { it('should be rejected given an invalid provider ID:' + JSON.stringify(invalidProviderId), () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_ID); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_PROVIDER_ID); const requestHandler = handler.init(mockApp); return requestHandler.updateInboundSamlConfig(invalidProviderId as any, configOptions) @@ -4358,7 +4358,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given invalid parameters', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_CONFIG, + authClientErrorCode.INVALID_CONFIG, '"SAMLAuthProviderConfig.ssoURL" must be a valid URL string.', ); const invalidOptions: SAMLUpdateAuthProviderRequest = deepCopy(configOptions); @@ -4376,7 +4376,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected when the backend returns a response missing name', () => { const expectedPath = path + `?updateMask=${fullUpadateMask}`; const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to update SAML configuration', ); const stub = sinon.stub(HttpClient.prototype, 'send').resolves(utils.responseFrom({})); @@ -4400,7 +4400,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { message: 'INVALID_CONFIG', }, }); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_CONFIG); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_CONFIG); const stub = sinon.stub(HttpClient.prototype, 'send').rejects(expectedServerError); stubs.push(stub); @@ -4440,7 +4440,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const invalidTenantIds = [null, NaN, 0, 1, true, false, '', ['tenant-id'], [], {}, { a: 1 }, _.noop]; invalidTenantIds.forEach((invalidTenantId) => { it('should be rejected given an invalid tenant ID:' + JSON.stringify(invalidTenantId), () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_TENANT_ID); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_TENANT_ID); const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.getTenant(invalidTenantId as any) @@ -4453,7 +4453,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); it('should be rejected given a backend error', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.TENANT_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.TENANT_NOT_FOUND); const expectedServerError = utils.errorFrom({ error: { message: 'TENANT_NOT_FOUND', @@ -4536,7 +4536,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given an invalid maxResults', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, 'Required "maxResults" must be a positive non-zero number that does not ' + 'exceed the allowed 1000.', ); @@ -4552,7 +4552,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given an invalid next page token', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_PAGE_TOKEN, + authClientErrorCode.INVALID_PAGE_TOKEN, ); const requestHandler = handler.init(mockApp) as AuthRequestHandler; @@ -4610,7 +4610,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const invalidTenantIds = [null, NaN, 0, 1, true, false, '', ['tenant-id'], [], {}, { a: 1 }, _.noop]; invalidTenantIds.forEach((invalidTenantId) => { it('should be rejected given an invalid tenant ID:' + JSON.stringify(invalidTenantId), () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_TENANT_ID); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_TENANT_ID); const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.deleteTenant(invalidTenantId as any) @@ -4623,7 +4623,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }); it('should be rejected given a backend error', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.TENANT_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.TENANT_NOT_FOUND); const expectedServerError = utils.errorFrom({ error: { message: 'TENANT_NOT_FOUND', @@ -4692,7 +4692,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given invalid parameters', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"EmailSignInConfig" must be a non-null object.', ); const invalidOptions = deepCopy(tenantOptions); @@ -4709,7 +4709,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected when the backend returns a response missing name', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new tenant', ); const stub = sinon.stub(HttpClient.prototype, 'send').resolves(utils.responseFrom({})); @@ -4727,7 +4727,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected when the backend returns a response missing tenant ID in response name', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new tenant', ); // Resource name should have /tenants/tenant-id in path. This should throw an error. @@ -4752,7 +4752,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }, }); const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'An internal error has occurred. Raw server response: ' + `"${JSON.stringify(expectedServerError.response.data)}"`, ); @@ -4864,7 +4864,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const invalidTenantIds = [null, NaN, 0, 1, true, false, '', ['tenant-id'], [], {}, { a: 1 }, _.noop]; invalidTenantIds.forEach((invalidTenantId) => { it('should be rejected given an invalid tenant ID:' + JSON.stringify(invalidTenantId), () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_TENANT_ID); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_TENANT_ID); const requestHandler = handler.init(mockApp) as AuthRequestHandler; return requestHandler.updateTenant(invalidTenantId as any, tenantOptions) @@ -4878,7 +4878,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { it('should be rejected given invalid parameters', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"EmailSignInConfig" must be a non-null object.', ); const invalidOptions = deepCopy(tenantOptions); @@ -4897,7 +4897,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const expectedPath = path + '?updateMask=allowPasswordSignup,enableEmailLinkSignin,displayName,' + 'mfaConfig.state,mfaConfig.enabledProviders,testPhoneNumbers'; const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to update tenant', ); const stub = sinon.stub(HttpClient.prototype, 'send').resolves(utils.responseFrom({})); @@ -4918,7 +4918,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { const expectedPath = path + '?updateMask=allowPasswordSignup,enableEmailLinkSignin,displayName,' + 'mfaConfig.state,mfaConfig.enabledProviders,testPhoneNumbers'; const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to update tenant', ); // Resource name should have /tenants/tenant-id in path. This should throw an error. @@ -4946,7 +4946,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => { }, }); const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'An internal error has occurred. Raw server response: ' + `"${JSON.stringify(expectedServerError.response.data)}"`, ); diff --git a/test/unit/auth/auth.spec.ts b/test/unit/auth/auth.spec.ts index 2fd3a3e0b4..f1c8202db9 100644 --- a/test/unit/auth/auth.spec.ts +++ b/test/unit/auth/auth.spec.ts @@ -31,7 +31,7 @@ import { FirebaseApp } from '../../../src/app/firebase-app'; import { AuthRequestHandler, TenantAwareAuthRequestHandler, AbstractAuthRequestHandler, } from '../../../src/auth/auth-api-request'; -import { AuthClientErrorCode, FirebaseAuthError } from '../../../src/utils/error'; +import { authClientErrorCode, FirebaseAuthError } from '../../../src/auth/error'; import * as validator from '../../../src/utils/validator'; import { DecodedAuthBlockingToken, FirebaseTokenVerifier } from '../../../src/auth/token-verifier'; @@ -497,7 +497,7 @@ AUTH_CONFIGS.forEach((testConfig) => { it('should reject when underlying idTokenVerifier.verifyJWT() rejects with expected error', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, 'Decoding Firebase ID token failed'); + authClientErrorCode.INVALID_ARGUMENT, 'Decoding Firebase ID token failed'); // Restore verifyIdToken stub. stub.restore(); // Simulate ID token is invalid. @@ -650,7 +650,7 @@ AUTH_CONFIGS.forEach((testConfig) => { }); it('should be rejected with checkRevoked set to true if underlying RPC fails', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.USER_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.USER_NOT_FOUND); const getUserStub = sinon.stub(testConfig.Auth.prototype, 'getUser') .rejects(expectedError); stubs.push(getUserStub); @@ -689,7 +689,7 @@ AUTH_CONFIGS.forEach((testConfig) => { }); it('should be rejected with checkRevoked set to true using an invalid ID token', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_CREDENTIAL); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_CREDENTIAL); // Restore verifyIdToken stub. stub.restore(); // Simulate ID token is invalid. @@ -709,7 +709,7 @@ AUTH_CONFIGS.forEach((testConfig) => { if (testConfig.Auth === TenantAwareAuth) { it('should be rejected with ID token missing tenant ID', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.MISMATCHING_TENANT_ID); + const expectedError = new FirebaseAuthError(authClientErrorCode.MISMATCHING_TENANT_ID); // Restore verifyIdToken stub. stub.restore(); // Simulate JWT does not contain tenant ID. @@ -726,7 +726,7 @@ AUTH_CONFIGS.forEach((testConfig) => { }); it('should be rejected with ID token containing mismatching tenant ID', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.MISMATCHING_TENANT_ID); + const expectedError = new FirebaseAuthError(authClientErrorCode.MISMATCHING_TENANT_ID); // Restore verifyIdToken stub. stub.restore(); // Simulate JWT does not contain matching tenant ID. @@ -788,7 +788,7 @@ AUTH_CONFIGS.forEach((testConfig) => { it('should reject when underlying sessionCookieVerifier.verifyJWT() rejects with expected error', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, 'Decoding Firebase session cookie failed'); + authClientErrorCode.INVALID_ARGUMENT, 'Decoding Firebase session cookie failed'); // Restore verifySessionCookie stub. stub.restore(); // Simulate session cookie is invalid. @@ -894,7 +894,7 @@ AUTH_CONFIGS.forEach((testConfig) => { }); it('should be rejected with checkRevoked set to true if underlying RPC fails', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.USER_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.USER_NOT_FOUND); const getUserStub = sinon.stub(testConfig.Auth.prototype, 'getUser') .rejects(expectedError); stubs.push(getUserStub); @@ -980,7 +980,7 @@ AUTH_CONFIGS.forEach((testConfig) => { }); it('should be rejected with checkRevoked set to true using an invalid session cookie', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_CREDENTIAL); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_CREDENTIAL); // Restore verifySessionCookie stub. stub.restore(); // Simulate session cookie is invalid. @@ -1000,7 +1000,7 @@ AUTH_CONFIGS.forEach((testConfig) => { if (testConfig.Auth === TenantAwareAuth) { it('should be rejected with session cookie missing tenant ID', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.MISMATCHING_TENANT_ID); + const expectedError = new FirebaseAuthError(authClientErrorCode.MISMATCHING_TENANT_ID); // Restore verifyIdToken stub. stub.restore(); // Simulate JWT does not contain tenant ID.. @@ -1017,7 +1017,7 @@ AUTH_CONFIGS.forEach((testConfig) => { }); it('should be rejected with ID token containing mismatching tenant ID', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.MISMATCHING_TENANT_ID); + const expectedError = new FirebaseAuthError(authClientErrorCode.MISMATCHING_TENANT_ID); // Restore verifyIdToken stub. stub.restore(); // Simulate JWT does not contain matching tenant ID.. @@ -1080,7 +1080,7 @@ AUTH_CONFIGS.forEach((testConfig) => { it('should reject when underlying idTokenVerifier._verifyAuthBlockingToken() rejects', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, 'Decoding Firebase Auth Blocking token failed'); + authClientErrorCode.INVALID_ARGUMENT, 'Decoding Firebase Auth Blocking token failed'); // Restore _verifyAuthBlockingToken stub. stub.restore(); // Simulate Auth Blocking token is invalid. @@ -1135,7 +1135,7 @@ AUTH_CONFIGS.forEach((testConfig) => { const tenantId = testConfig.supportsTenantManagement ? undefined : TENANT_ID; const expectedGetAccountInfoResult = getValidGetAccountInfoResponse(tenantId); const expectedUserRecord = getValidUserRecord(expectedGetAccountInfoResult); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.USER_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.USER_NOT_FOUND); // Stubs used to simulate underlying api calls. let stubs: sinon.SinonStub[] = []; @@ -1214,7 +1214,7 @@ AUTH_CONFIGS.forEach((testConfig) => { const tenantId = testConfig.supportsTenantManagement ? undefined : TENANT_ID; const expectedGetAccountInfoResult = getValidGetAccountInfoResponse(tenantId); const expectedUserRecord = getValidUserRecord(expectedGetAccountInfoResult); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.USER_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.USER_NOT_FOUND); // Stubs used to simulate underlying api calls. let stubs: sinon.SinonStub[] = []; @@ -1293,7 +1293,7 @@ AUTH_CONFIGS.forEach((testConfig) => { const tenantId = testConfig.supportsTenantManagement ? undefined : TENANT_ID; const expectedGetAccountInfoResult = getValidGetAccountInfoResponse(tenantId); const expectedUserRecord = getValidUserRecord(expectedGetAccountInfoResult); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.USER_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.USER_NOT_FOUND); // Stubs used to simulate underlying api calls. let stubs: sinon.SinonStub[] = []; @@ -1374,7 +1374,7 @@ AUTH_CONFIGS.forEach((testConfig) => { const tenantId = testConfig.supportsTenantManagement ? undefined : TENANT_ID; const expectedGetAccountInfoResult = getValidGetAccountInfoResponse(tenantId); const expectedUserRecord = getValidUserRecord(expectedGetAccountInfoResult); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.USER_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.USER_NOT_FOUND); // Stubs used to simulate underlying api calls. let stubs: sinon.SinonStub[] = []; @@ -1562,7 +1562,7 @@ AUTH_CONFIGS.forEach((testConfig) => { describe('deleteUser()', () => { const uid = 'abcdefghijklmnopqrstuvwxyz'; const expectedDeleteAccountResult = { kind: 'identitytoolkit#DeleteAccountResponse' }; - const expectedError = new FirebaseAuthError(AuthClientErrorCode.USER_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.USER_NOT_FOUND); // Stubs used to simulate underlying api calls. let stubs: sinon.SinonStub[] = []; @@ -1700,10 +1700,10 @@ AUTH_CONFIGS.forEach((testConfig) => { const expectedGetAccountInfoResult = getValidGetAccountInfoResponse(tenantId); const expectedUserRecord = getValidUserRecord(expectedGetAccountInfoResult); const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'Unable to create the user record provided.'); const unableToCreateUserError = new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'Unable to create the user record provided.'); const propertiesToCreate = { displayName: expectedUserRecord.displayName, @@ -1793,7 +1793,7 @@ AUTH_CONFIGS.forEach((testConfig) => { const createUserStub = sinon.stub(testConfig.RequestHandler.prototype, 'createNewAccount') .resolves(uid); // Stub getAccountInfoByUid to throw user not found error. - const userNotFoundError = new FirebaseAuthError(AuthClientErrorCode.USER_NOT_FOUND); + const userNotFoundError = new FirebaseAuthError(authClientErrorCode.USER_NOT_FOUND); const getUserStub = sinon.stub(testConfig.RequestHandler.prototype, 'getAccountInfoByUid') .rejects(userNotFoundError); stubs.push(createUserStub); @@ -1837,7 +1837,7 @@ AUTH_CONFIGS.forEach((testConfig) => { const tenantId = testConfig.supportsTenantManagement ? undefined : TENANT_ID; const expectedGetAccountInfoResult = getValidGetAccountInfoResponse(tenantId); const expectedUserRecord = getValidUserRecord(expectedGetAccountInfoResult); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.USER_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.USER_NOT_FOUND); const propertiesToEdit = { displayName: expectedUserRecord.displayName, photoURL: expectedUserRecord.photoURL, @@ -2258,7 +2258,7 @@ AUTH_CONFIGS.forEach((testConfig) => { describe('setCustomUserClaims()', () => { const uid = 'abcdefghijklmnopqrstuvwxyz'; - const expectedError = new FirebaseAuthError(AuthClientErrorCode.USER_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.USER_NOT_FOUND); const customClaims = { admin: true, groupId: '123456', @@ -2359,7 +2359,7 @@ AUTH_CONFIGS.forEach((testConfig) => { }); describe('listUsers()', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INTERNAL_ERROR); + const expectedError = new FirebaseAuthError(authClientErrorCode.INTERNAL_ERROR); const pageToken = 'PAGE_TOKEN'; const maxResult = 500; const downloadAccountResponse: any = { @@ -2502,7 +2502,7 @@ AUTH_CONFIGS.forEach((testConfig) => { describe('revokeRefreshTokens()', () => { const uid = 'abcdefghijklmnopqrstuvwxyz'; - const expectedError = new FirebaseAuthError(AuthClientErrorCode.USER_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.USER_NOT_FOUND); // Stubs used to simulate underlying api calls. let stubs: sinon.SinonStub[] = []; beforeEach(() => { @@ -2590,11 +2590,11 @@ AUTH_CONFIGS.forEach((testConfig) => { }, }; const expectedUserImportResultError = - new FirebaseAuthError(AuthClientErrorCode.INVALID_PHONE_NUMBER); + new FirebaseAuthError(authClientErrorCode.INVALID_PHONE_NUMBER); const expectedOptionsError = - new FirebaseAuthError(AuthClientErrorCode.INVALID_HASH_ALGORITHM); + new FirebaseAuthError(authClientErrorCode.INVALID_HASH_ALGORITHM); const expectedServerError = - new FirebaseAuthError(AuthClientErrorCode.INTERNAL_ERROR); + new FirebaseAuthError(authClientErrorCode.INTERNAL_ERROR); const expectedUserImportResult = { successCount: 1, failureCount: 1, @@ -2706,7 +2706,7 @@ AUTH_CONFIGS.forEach((testConfig) => { const idToken = 'ID_TOKEN'; const options = { expiresIn: 60 * 60 * 24 * 1000 }; const sessionCookie = 'SESSION_COOKIE'; - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_ID_TOKEN); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_ID_TOKEN); const expectedUserRecord = getValidUserRecord(getValidGetAccountInfoResponse(tenantId)); // Set auth_time of token to expected user's tokensValidAfterTime. if (!expectedUserRecord.tokensValidAfterTime) { @@ -2892,7 +2892,7 @@ AUTH_CONFIGS.forEach((testConfig) => { const expectedLink = 'https://custom.page.link?link=' + encodeURIComponent('https://projectId.firebaseapp.com/__/auth/action?oobCode=CODE') + '&apn=com.example.android&ibi=com.example.ios'; - const expectedError = new FirebaseAuthError(AuthClientErrorCode.USER_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.USER_NOT_FOUND); // Stubs used to simulate underlying api calls. let stubs: sinon.SinonStub[] = []; afterEach(() => { @@ -3090,7 +3090,7 @@ AUTH_CONFIGS.forEach((testConfig) => { issuer: 'https://oidc.com/issuer', }; const expectedConfig = new OIDCConfig(serverResponse); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.CONFIGURATION_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.CONFIGURATION_NOT_FOUND); it('should resolve with an OIDCConfig on success', () => { // Stub getOAuthIdpConfig to return expected result. @@ -3144,7 +3144,7 @@ AUTH_CONFIGS.forEach((testConfig) => { enabled: true, }; const expectedConfig = new SAMLConfig(serverResponse); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.CONFIGURATION_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.CONFIGURATION_NOT_FOUND); it('should resolve with a SAMLConfig on success', () => { // Stub getInboundSamlConfig to return expected result. @@ -3218,7 +3218,7 @@ AUTH_CONFIGS.forEach((testConfig) => { }); describe('using OIDC type filter', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INTERNAL_ERROR); + const expectedError = new FirebaseAuthError(authClientErrorCode.INTERNAL_ERROR); const pageToken = 'PAGE_TOKEN'; const maxResults = 50; const filterOptions: AuthProviderConfigFilter = { @@ -3313,7 +3313,7 @@ AUTH_CONFIGS.forEach((testConfig) => { }); describe('using SAML type filter', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INTERNAL_ERROR); + const expectedError = new FirebaseAuthError(authClientErrorCode.INTERNAL_ERROR); const pageToken = 'PAGE_TOKEN'; const maxResults = 50; const filterOptions: AuthProviderConfigFilter = { @@ -3453,7 +3453,7 @@ AUTH_CONFIGS.forEach((testConfig) => { describe('using OIDC configurations', () => { const providerId = 'oidc.provider'; - const expectedError = new FirebaseAuthError(AuthClientErrorCode.CONFIGURATION_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.CONFIGURATION_NOT_FOUND); it('should resolve with void on success', () => { // Stub deleteOAuthIdpConfig to resolve. @@ -3488,7 +3488,7 @@ AUTH_CONFIGS.forEach((testConfig) => { describe('using SAML configurations', () => { const providerId = 'saml.provider'; - const expectedError = new FirebaseAuthError(AuthClientErrorCode.CONFIGURATION_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.CONFIGURATION_NOT_FOUND); it('should resolve with void on success', () => { // Stub deleteInboundSamlConfig to resolve. @@ -3598,7 +3598,7 @@ AUTH_CONFIGS.forEach((testConfig) => { issuer: 'https://oidc.com/issuer', }; const expectedConfig = new OIDCConfig(serverResponse); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_CONFIG); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_CONFIG); it('should resolve with an OIDCConfig on updateOAuthIdpConfig request success', () => { // Stub updateOAuthIdpConfig to return expected server response. @@ -3664,7 +3664,7 @@ AUTH_CONFIGS.forEach((testConfig) => { enabled: true, }; const expectedConfig = new SAMLConfig(serverResponse); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_CONFIG); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_CONFIG); it('should resolve with a SAMLConfig on updateInboundSamlConfig request success', () => { // Stub updateInboundSamlConfig to return expected server response. @@ -3764,7 +3764,7 @@ AUTH_CONFIGS.forEach((testConfig) => { issuer: 'https://oidc.com/issuer', }; const expectedConfig = new OIDCConfig(serverResponse); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_CONFIG); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_CONFIG); it('should resolve with an OIDCConfig on createOAuthIdpConfig request success', () => { // Stub createOAuthIdpConfig to return expected server response. @@ -3831,7 +3831,7 @@ AUTH_CONFIGS.forEach((testConfig) => { enabled: true, }; const expectedConfig = new SAMLConfig(serverResponse); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_CONFIG); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_CONFIG); it('should resolve with a SAMLConfig on createInboundSamlConfig request success', () => { // Stub createInboundSamlConfig to return expected server response. diff --git a/test/unit/auth/project-config-manager.spec.ts b/test/unit/auth/project-config-manager.spec.ts index be7df54d99..8d56c34c0d 100644 --- a/test/unit/auth/project-config-manager.spec.ts +++ b/test/unit/auth/project-config-manager.spec.ts @@ -25,7 +25,7 @@ import * as chaiAsPromised from 'chai-as-promised'; import * as mocks from '../../resources/mocks'; import { FirebaseApp } from '../../../src/app/firebase-app'; import { AuthRequestHandler } from '../../../src/auth/auth-api-request'; -import { AuthClientErrorCode, FirebaseAuthError } from '../../../src/utils/error'; +import { authClientErrorCode, FirebaseAuthError } from '../../../src/auth/error'; import { ProjectConfigManager } from '../../../src/auth/project-config-manager'; import { ProjectConfig, @@ -81,7 +81,7 @@ describe('ProjectConfigManager', () => { describe('getProjectConfig()', () => { const expectedProjectConfig = new ProjectConfig(GET_CONFIG_RESPONSE); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_CONFIG); + const expectedError = new FirebaseAuthError(authClientErrorCode.INVALID_CONFIG); // Stubs used to simulate underlying API calls. let stubs: sinon.SinonStub[] = []; afterEach(() => { @@ -152,7 +152,7 @@ describe('ProjectConfigManager', () => { }; const expectedProjectConfig = new ProjectConfig(GET_CONFIG_RESPONSE); const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INTERNAL_ERROR, + authClientErrorCode.INTERNAL_ERROR, 'Unable to update the config provided.'); // Stubs used to simulate underlying API calls. let stubs: sinon.SinonStub[] = []; diff --git a/test/unit/auth/tenant-manager.spec.ts b/test/unit/auth/tenant-manager.spec.ts index 30bf3082dd..dd041419b7 100644 --- a/test/unit/auth/tenant-manager.spec.ts +++ b/test/unit/auth/tenant-manager.spec.ts @@ -26,7 +26,7 @@ import * as mocks from '../../resources/mocks'; import { FirebaseApp } from '../../../src/app/firebase-app'; import { AuthRequestHandler } from '../../../src/auth/auth-api-request'; import { TenantServerResponse } from '../../../src/auth/tenant'; -import { AuthClientErrorCode, FirebaseAuthError } from '../../../src/utils/error'; +import { authClientErrorCode, FirebaseAuthError } from '../../../src/auth/error'; import { CreateTenantRequest, UpdateTenantRequest, ListTenantsResult, Tenant, TenantManager, } from '../../../src/auth/index'; @@ -102,7 +102,7 @@ describe('TenantManager', () => { describe('getTenant()', () => { const tenantId = 'tenant-id'; const expectedTenant = new Tenant(GET_TENANT_RESPONSE); - const expectedError = new FirebaseAuthError(AuthClientErrorCode.TENANT_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.TENANT_NOT_FOUND); // Stubs used to simulate underlying API calls. let stubs: sinon.SinonStub[] = []; afterEach(() => { @@ -173,7 +173,7 @@ describe('TenantManager', () => { }); describe('listTenants()', () => { - const expectedError = new FirebaseAuthError(AuthClientErrorCode.INTERNAL_ERROR); + const expectedError = new FirebaseAuthError(authClientErrorCode.INTERNAL_ERROR); const pageToken = 'PAGE_TOKEN'; const maxResult = 500; const listTenantsResponse: any = { @@ -307,7 +307,7 @@ describe('TenantManager', () => { describe('deleteTenant()', () => { const tenantId = 'tenant-id'; - const expectedError = new FirebaseAuthError(AuthClientErrorCode.TENANT_NOT_FOUND); + const expectedError = new FirebaseAuthError(authClientErrorCode.TENANT_NOT_FOUND); // Stubs used to simulate underlying API calls. let stubs: sinon.SinonStub[] = []; afterEach(() => { @@ -390,7 +390,7 @@ describe('TenantManager', () => { }; const expectedTenant = new Tenant(GET_TENANT_RESPONSE); const expectedError = new FirebaseAuthError({ - code: AuthClientErrorCode.INTERNAL_ERROR.code, + code: authClientErrorCode.INTERNAL_ERROR.code, message: 'Unable to create the tenant provided.' }); // Stubs used to simulate underlying API calls. @@ -484,7 +484,7 @@ describe('TenantManager', () => { }; const expectedTenant = new Tenant(GET_TENANT_RESPONSE); const expectedError = new FirebaseAuthError({ - code: AuthClientErrorCode.INTERNAL_ERROR.code, + code: authClientErrorCode.INTERNAL_ERROR.code, message: 'Unable to update the tenant provided.' }); // Stubs used to simulate underlying API calls. diff --git a/test/unit/auth/token-generator.spec.ts b/test/unit/auth/token-generator.spec.ts index c9dce78088..3b2ac86c9a 100644 --- a/test/unit/auth/token-generator.spec.ts +++ b/test/unit/auth/token-generator.spec.ts @@ -31,7 +31,7 @@ import { import { CryptoSignerError, CryptoSignerErrorCode, ServiceAccountSigner } from '../../../src/utils/crypto-signer'; import { ServiceAccountCredential } from '../../../src/app/credential-internal'; -import { FirebaseAuthError } from '../../../src/utils/error'; +import { FirebaseAuthError } from '../../../src/auth/error'; import * as utils from '../utils'; chai.should(); diff --git a/test/unit/auth/token-verifier.spec.ts b/test/unit/auth/token-verifier.spec.ts index 3dae6817cd..d90c055a2a 100644 --- a/test/unit/auth/token-verifier.spec.ts +++ b/test/unit/auth/token-verifier.spec.ts @@ -32,7 +32,7 @@ import { ServiceAccountSigner } from '../../../src/utils/crypto-signer'; import * as verifier from '../../../src/auth/token-verifier'; import { ServiceAccountCredential } from '../../../src/app/credential-internal'; import { FirebaseApp } from '../../../src/app/firebase-app'; -import { AuthClientErrorCode } from '../../../src/utils/error'; +import { authClientErrorCode } from '../../../src/auth/error'; import { JwtError, JwtErrorCode, PublicKeySignatureVerifier } from '../../../src/utils/jwt'; chai.should(); @@ -104,7 +104,7 @@ describe('FirebaseTokenVerifier', () => { verifyApiName: 'verifyToken()', jwtName: 'Important Token', shortName: 'token', - expiredErrorCode: AuthClientErrorCode.INVALID_ARGUMENT, + expiredErrorCode: authClientErrorCode.INVALID_ARGUMENT, }, app, ); @@ -151,7 +151,7 @@ describe('FirebaseTokenVerifier', () => { verifyApiName: invalidVerifyApiName as any, jwtName: 'Important Token', shortName: 'token', - expiredErrorCode: AuthClientErrorCode.INVALID_ARGUMENT, + expiredErrorCode: authClientErrorCode.INVALID_ARGUMENT, }, app, ); @@ -171,7 +171,7 @@ describe('FirebaseTokenVerifier', () => { verifyApiName: 'verifyToken()', jwtName: invalidJwtName as any, shortName: 'token', - expiredErrorCode: AuthClientErrorCode.INVALID_ARGUMENT, + expiredErrorCode: authClientErrorCode.INVALID_ARGUMENT, }, app, ); @@ -191,7 +191,7 @@ describe('FirebaseTokenVerifier', () => { verifyApiName: 'verifyToken()', jwtName: 'Important Token', shortName: invalidShortName as any, - expiredErrorCode: AuthClientErrorCode.INVALID_ARGUMENT, + expiredErrorCode: authClientErrorCode.INVALID_ARGUMENT, }, app, ); diff --git a/test/unit/auth/user-import-builder.spec.ts b/test/unit/auth/user-import-builder.spec.ts index 6be38ebe28..d6988b7c9a 100644 --- a/test/unit/auth/user-import-builder.spec.ts +++ b/test/unit/auth/user-import-builder.spec.ts @@ -22,7 +22,7 @@ import { deepCopy } from '../../../src/utils/deep-copy'; import { UserImportBuilder, ValidatorFunction, UploadAccountRequest, } from '../../../src/auth/user-import-builder'; -import { AuthClientErrorCode, FirebaseAuthError } from '../../../src/utils/error'; +import { authClientErrorCode, FirebaseAuthError } from '../../../src/auth/error'; import { toWebSafeBase64 } from '../../../src/utils'; import { UpdatePhoneMultiFactorInfoRequest, UserImportResult, UserImportRecord, @@ -55,7 +55,7 @@ describe('UserImportBuilder', () => { // Simulate a validation error is thrown for a specific user. if (request.localId === '5678') { throw new FirebaseAuthError( - AuthClientErrorCode.INVALID_PHONE_NUMBER, + authClientErrorCode.INVALID_PHONE_NUMBER, ); } }; @@ -176,7 +176,7 @@ describe('UserImportBuilder', () => { invalidUserImportOptions.forEach((invalidOption) => { it(`should throw when non-object ${JSON.stringify(invalidOption)} UserImportOptions is provided`, () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_ARGUMENT, + authClientErrorCode.INVALID_ARGUMENT, '"UserImportOptions" are required when importing users with passwords.', ); expect(() => { @@ -187,7 +187,7 @@ describe('UserImportBuilder', () => { it('should throw when an empty hash algorithm is provided', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.MISSING_HASH_ALGORITHM, + authClientErrorCode.MISSING_HASH_ALGORITHM, '"hash.algorithm" is missing from the provided "UserImportOptions".', ); expect(() => { @@ -197,7 +197,7 @@ describe('UserImportBuilder', () => { it('should throw when an invalid hash algorithm is provided', () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_ALGORITHM, + authClientErrorCode.INVALID_HASH_ALGORITHM, 'Unsupported hash algorithm provider "invalid".', ); const invalidOptions = { @@ -226,7 +226,7 @@ describe('UserImportBuilder', () => { invalidKeys.forEach((key) => { it(`should throw when non-Buffer ${JSON.stringify(key)} hash key is provided`, () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_KEY, + authClientErrorCode.INVALID_HASH_KEY, 'A non-empty "hash.key" byte buffer must be provided for ' + `hash algorithm ${algorithm}.`, ); @@ -289,7 +289,7 @@ describe('UserImportBuilder', () => { invalidRounds.forEach((rounds) => { it(`should throw when ${JSON.stringify(rounds)} rounds provided`, () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_ROUNDS, + authClientErrorCode.INVALID_HASH_ROUNDS, `A valid "hash.rounds" number between ${minRounds} and ${maxRounds} must be provided for ` + `hash algorithm ${algorithm}.`, ); @@ -334,7 +334,7 @@ describe('UserImportBuilder', () => { invalidKeys.forEach((key) => { it(`should throw when ${JSON.stringify(key)} key provided`, () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_KEY, + authClientErrorCode.INVALID_HASH_KEY, 'A "hash.key" byte buffer must be provided for ' + `hash algorithm ${algorithm}.`, ); @@ -355,7 +355,7 @@ describe('UserImportBuilder', () => { invalidRounds.forEach((rounds) => { it(`should throw when ${JSON.stringify(rounds)} rounds provided`, () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_ROUNDS, + authClientErrorCode.INVALID_HASH_ROUNDS, 'A valid "hash.rounds" number between 1 and 8 must be provided for ' + `hash algorithm ${algorithm}.`, ); @@ -376,7 +376,7 @@ describe('UserImportBuilder', () => { invalidMemoryCost.forEach((memoryCost) => { it(`should throw when ${JSON.stringify(memoryCost)} memoryCost provided`, () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_MEMORY_COST, + authClientErrorCode.INVALID_HASH_MEMORY_COST, 'A valid "hash.memoryCost" number between 1 and 14 must be provided for ' + `hash algorithm ${algorithm}.`, ); @@ -397,7 +397,7 @@ describe('UserImportBuilder', () => { invalidSaltSeparator.forEach((saltSeparator) => { it(`should throw when ${JSON.stringify(saltSeparator)} saltSeparator provided`, () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_SALT_SEPARATOR, + authClientErrorCode.INVALID_HASH_SALT_SEPARATOR, '"hash.saltSeparator" must be a byte buffer.', ); const invalidOptions = { @@ -465,7 +465,7 @@ describe('UserImportBuilder', () => { invalidMemoryCost.forEach((memoryCost) => { it(`should throw when ${JSON.stringify(memoryCost)} memoryCost provided`, () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_MEMORY_COST, + authClientErrorCode.INVALID_HASH_MEMORY_COST, 'A valid "hash.memoryCost" number must be provided for ' + `hash algorithm ${algorithm}.`, ); @@ -487,7 +487,7 @@ describe('UserImportBuilder', () => { invalidParallelization.forEach((parallelization) => { it(`should throw when ${JSON.stringify(parallelization)} parallelization provided`, () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_MEMORY_COST, + authClientErrorCode.INVALID_HASH_MEMORY_COST, 'A valid "hash.parallelization" number must be provided for ' + `hash algorithm ${algorithm}.`, ); @@ -509,7 +509,7 @@ describe('UserImportBuilder', () => { invalidBlockSize.forEach((blockSize) => { it(`should throw when ${JSON.stringify(blockSize)} blockSize provided`, () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_BLOCK_SIZE, + authClientErrorCode.INVALID_HASH_BLOCK_SIZE, 'A valid "hash.blockSize" number must be provided for ' + `hash algorithm ${algorithm}.`, ); @@ -531,7 +531,7 @@ describe('UserImportBuilder', () => { invalidDerivedKeyLength.forEach((derivedKeyLength) => { it(`should throw when ${JSON.stringify(derivedKeyLength)} dkLen provided`, () => { const expectedError = new FirebaseAuthError( - AuthClientErrorCode.INVALID_HASH_DERIVED_KEY_LENGTH, + authClientErrorCode.INVALID_HASH_DERIVED_KEY_LENGTH, 'A valid "hash.derivedKeyLength" number must be provided for ' + `hash algorithm ${algorithm}.`, ); @@ -741,7 +741,7 @@ describe('UserImportBuilder', () => { // Index should match server error index. index: 1, error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_USER_IMPORT, + authClientErrorCode.INVALID_USER_IMPORT, 'Some error occurred!', ), }, @@ -760,7 +760,7 @@ describe('UserImportBuilder', () => { successCount: 3, failureCount: 1, errors: [ - { index: 2, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_PHONE_NUMBER) }, + { index: 2, error: new FirebaseAuthError(authClientErrorCode.INVALID_PHONE_NUMBER) }, ], }; // userRequestValidatorWithError will throw on the 3rd user (index = 2). @@ -780,9 +780,9 @@ describe('UserImportBuilder', () => { const userRequestValidatorWithMultipleErrors: ValidatorFunction = (request) => { // Simulate a validation error is thrown for specific users. if (request.localId === 'USER2') { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_EMAIL); + throw new FirebaseAuthError(authClientErrorCode.INVALID_EMAIL); } else if (request.localId === 'USER4') { - throw new FirebaseAuthError(AuthClientErrorCode.INVALID_PHONE_NUMBER); + throw new FirebaseAuthError(authClientErrorCode.INVALID_PHONE_NUMBER); } }; @@ -836,39 +836,39 @@ describe('UserImportBuilder', () => { failureCount: 8, errors: [ // Client side detected error. - { index: 1, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_EMAIL) }, + { index: 1, error: new FirebaseAuthError(authClientErrorCode.INVALID_EMAIL) }, // Server side detected error. { index: 2, error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_USER_IMPORT, + authClientErrorCode.INVALID_USER_IMPORT, 'Some error occurred in USER3!', ), }, // Client side detected error. - { index: 3, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_PHONE_NUMBER) }, + { index: 3, error: new FirebaseAuthError(authClientErrorCode.INVALID_PHONE_NUMBER) }, // Server side detected error. { index: 5, error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_USER_IMPORT, + authClientErrorCode.INVALID_USER_IMPORT, 'Another error occurred in USER6!', ), }, // Client side errors. - { index: 6, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_PASSWORD_HASH) }, - { index: 7, error: new FirebaseAuthError(AuthClientErrorCode.INVALID_PASSWORD_SALT) }, + { index: 6, error: new FirebaseAuthError(authClientErrorCode.INVALID_PASSWORD_HASH) }, + { index: 7, error: new FirebaseAuthError(authClientErrorCode.INVALID_PASSWORD_SALT) }, { index: 8, error: new FirebaseAuthError( - AuthClientErrorCode.INVALID_ENROLLMENT_TIME, + authClientErrorCode.INVALID_ENROLLMENT_TIME, 'The second factor "enrollmentTime" for "enrollmentId1" must be a valid ' + 'UTC date string.'), }, { index: 9, error: new FirebaseAuthError( - AuthClientErrorCode.UNSUPPORTED_SECOND_FACTOR, + authClientErrorCode.UNSUPPORTED_SECOND_FACTOR, `Unsupported second factor "${JSON.stringify(testUsers[9].multiFactor!.enrolledFactors[0])}" provided.`), }, ], diff --git a/test/unit/data-connect/data-connect-api-client-internal.spec.ts b/test/unit/data-connect/data-connect-api-client-internal.spec.ts index a50c7e30be..5951c29264 100644 --- a/test/unit/data-connect/data-connect-api-client-internal.spec.ts +++ b/test/unit/data-connect/data-connect-api-client-internal.spec.ts @@ -24,8 +24,11 @@ import { } from '../../../src/utils/api-request'; import * as utils from '../utils'; import * as mocks from '../../resources/mocks'; -import { DATA_CONNECT_ERROR_CODE_MAPPING, DataConnectApiClient, FirebaseDataConnectError } - from '../../../src/data-connect/data-connect-api-client-internal'; +import { DataConnectApiClient } from '../../../src/data-connect/data-connect-api-client-internal'; +import { + FirebaseDataConnectError, + DATA_CONNECT_ERROR_CODE_MAPPING, +} from '../../../src/data-connect/error'; import { FirebaseApp } from '../../../src/app/firebase-app'; import { ConnectorConfig } from '../../../src/data-connect'; import { getMetricsHeader, getSdkVersion } from '../../../src/utils'; diff --git a/test/unit/data-connect/validate-admin-args.spec.ts b/test/unit/data-connect/validate-admin-args.spec.ts index 79a0d931b3..54248aa846 100644 --- a/test/unit/data-connect/validate-admin-args.spec.ts +++ b/test/unit/data-connect/validate-admin-args.spec.ts @@ -22,7 +22,7 @@ import { OperationOptions } from '../../../src/data-connect'; import { DATA_CONNECT_ERROR_CODE_MAPPING, FirebaseDataConnectError -} from '../../../src/data-connect/data-connect-api-client-internal'; +} from '../../../src/data-connect/error'; import * as dataConnectIndex from '../../../src/data-connect/index'; interface IdVars { diff --git a/test/unit/eventarc/eventarc.spec.ts b/test/unit/eventarc/eventarc.spec.ts index 93ca71d28a..55cc853b4a 100644 --- a/test/unit/eventarc/eventarc.spec.ts +++ b/test/unit/eventarc/eventarc.spec.ts @@ -27,10 +27,17 @@ import * as mocks from '../../resources/mocks'; import * as utils from '../utils'; import * as chai from 'chai'; import chaiExclude from 'chai-exclude'; +import * as sinonChai from 'sinon-chai'; +import * as chaiAsPromised from 'chai-as-promised'; + +chai.should(); +chai.use(sinonChai); +chai.use(chaiAsPromised); +chai.use(chaiExclude); + import { getMetricsHeader, getSdkVersion } from '../../../src/utils/index'; const expect = chai.expect; -chai.use(chaiExclude); const TEST_EVENT1 : CloudEvent = { type: 'some.custom.event1', diff --git a/test/unit/extensions/extensions-api-client-internal.spec.ts b/test/unit/extensions/extensions-api-client-internal.spec.ts index 62cb8eafca..f331f6651b 100644 --- a/test/unit/extensions/extensions-api-client-internal.spec.ts +++ b/test/unit/extensions/extensions-api-client-internal.spec.ts @@ -15,13 +15,22 @@ * limitations under the License. */ -import { expect } from 'chai'; +import * as chai from 'chai'; import * as sinon from 'sinon'; +import * as sinonChai from 'sinon-chai'; +import * as chaiAsPromised from 'chai-as-promised'; + +chai.should(); +chai.use(sinonChai); +chai.use(chaiAsPromised); + +const expect = chai.expect; import * as utils from '../utils'; import * as mocks from '../../resources/mocks'; import { FirebaseApp } from '../../../src/app/firebase-app'; -import { ExtensionsApiClient, FirebaseExtensionsError } from '../../../src/extensions/extensions-api-client-internal'; +import { ExtensionsApiClient } from '../../../src/extensions/extensions-api-client-internal'; +import { FirebaseExtensionsError } from '../../../src/extensions/error'; import { HttpClient } from '../../../src/utils/api-request'; import { SettableProcessingState } from '../../../src/extensions/extensions-api'; import { getMetricsHeader, getSdkVersion } from '../../../src/utils'; diff --git a/test/unit/extensions/extensions.spec.ts b/test/unit/extensions/extensions.spec.ts index 6f2d75a2de..6e9f4f5fbc 100644 --- a/test/unit/extensions/extensions.spec.ts +++ b/test/unit/extensions/extensions.spec.ts @@ -15,17 +15,25 @@ * limitations under the License. */ +import * as chai from 'chai'; import * as sinon from 'sinon'; -import { expect } from 'chai'; +import * as sinonChai from 'sinon-chai'; +import * as chaiAsPromised from 'chai-as-promised'; + +chai.should(); +chai.use(sinonChai); +chai.use(chaiAsPromised); + +const expect = chai.expect; import * as mocks from '../../resources/mocks'; import * as utils from '../utils'; import { FirebaseApp } from '../../../src/app/firebase-app'; import { Extensions } from '../../../src/extensions/extensions'; -import { FirebaseAppError } from '../../../src/utils/error'; +import { FirebaseAppError } from '../../../src/app/error'; import { HttpClient, HttpRequestConfig } from '../../../src/utils/api-request'; import { SettableProcessingState } from '../../../src/extensions/extensions-api'; -import { FirebaseExtensionsError } from '../../../src/extensions/extensions-api-client-internal'; +import { FirebaseExtensionsError } from '../../../src/extensions/error'; describe('Extensions', () => { const mockOptions = { diff --git a/test/unit/functions/functions-api-client-internal.spec.ts b/test/unit/functions/functions-api-client-internal.spec.ts index 1193615330..8a6a42080f 100644 --- a/test/unit/functions/functions-api-client-internal.spec.ts +++ b/test/unit/functions/functions-api-client-internal.spec.ts @@ -25,9 +25,11 @@ import * as mocks from '../../resources/mocks'; import { getSdkVersion, getMetricsHeader } from '../../../src/utils'; import { FirebaseApp } from '../../../src/app/firebase-app'; -import { FirebaseFunctionsError, FunctionsApiClient, Task } from '../../../src/functions/functions-api-client-internal'; +import { FunctionsApiClient, Task } from '../../../src/functions/functions-api-client-internal'; +import { FirebaseFunctionsError } from '../../../src/functions/error'; import { HttpClient } from '../../../src/utils/api-request'; -import { FirebaseAppError, toHttpResponse } from '../../../src/utils/error'; +import { toHttpResponse } from '../../../src/utils/error'; +import { FirebaseAppError } from '../../../src/app/error'; import { deepCopy } from '../../../src/utils/deep-copy'; import { EMULATED_SERVICE_ACCOUNT_DEFAULT } from '../../../src/functions/functions-api-client-internal'; diff --git a/test/unit/functions/functions.spec.ts b/test/unit/functions/functions.spec.ts index 9d650e51a0..210a4905e8 100644 --- a/test/unit/functions/functions.spec.ts +++ b/test/unit/functions/functions.spec.ts @@ -23,7 +23,8 @@ import * as sinon from 'sinon'; import * as mocks from '../../resources/mocks'; import { FirebaseApp } from '../../../src/app/firebase-app'; -import { FunctionsApiClient, FirebaseFunctionsError } from '../../../src/functions/functions-api-client-internal'; +import { FunctionsApiClient } from '../../../src/functions/functions-api-client-internal'; +import { FirebaseFunctionsError } from '../../../src/functions/error'; import { HttpClient } from '../../../src/utils/api-request'; import { Functions, TaskQueue } from '../../../src/functions/functions'; diff --git a/test/unit/installations/installations.spec.ts b/test/unit/installations/installations.spec.ts index 71d0eeae9d..1b78f70553 100644 --- a/test/unit/installations/installations.spec.ts +++ b/test/unit/installations/installations.spec.ts @@ -29,7 +29,10 @@ import * as mocks from '../../resources/mocks'; import { Installations } from '../../../src/installations/installations'; import { FirebaseInstallationsRequestHandler } from '../../../src/installations/installations-request-handler'; import { FirebaseApp } from '../../../src/app/firebase-app'; -import { FirebaseInstallationsError, InstallationsClientErrorCode } from '../../../src/utils/error'; +import { + FirebaseInstallationsError, + installationsClientErrorCode, +} from '../../../src/installations/error'; chai.should(); chai.use(sinonChai); @@ -127,7 +130,7 @@ describe('Installations', () => { // Stubs used to simulate underlying api calls. let stubs: sinon.SinonStub[] = []; - const expectedError = new FirebaseInstallationsError(InstallationsClientErrorCode.API_ERROR); + const expectedError = new FirebaseInstallationsError(installationsClientErrorCode.API_ERROR); const testInstallationId = 'test-iid'; afterEach(() => { diff --git a/test/unit/instance-id/instance-id.spec.ts b/test/unit/instance-id/instance-id.spec.ts index f43d19d9d0..34eea41101 100644 --- a/test/unit/instance-id/instance-id.spec.ts +++ b/test/unit/instance-id/instance-id.spec.ts @@ -30,9 +30,13 @@ import { InstanceId } from '../../../src/instance-id/index'; import { Installations } from '../../../src/installations/index'; import { FirebaseApp } from '../../../src/app/firebase-app'; import { - FirebaseInstanceIdError, InstanceIdClientErrorCode, - FirebaseInstallationsError, InstallationsClientErrorCode, -} from '../../../src/utils/error'; + FirebaseInstanceIdError, + instanceIdClientErrorCode, +} from '../../../src/instance-id/error'; +import { + FirebaseInstallationsError, + installationsClientErrorCode, +} from '../../../src/installations/error'; chai.should(); chai.use(sinonChai); @@ -175,7 +179,7 @@ describe('InstanceId', () => { it('should throw a FirebaseInstanceIdError error when the backend returns an error', () => { // Stub deleteInstanceId to throw a backend error. - const originalError = new FirebaseInstallationsError(InstallationsClientErrorCode.API_ERROR); + const originalError = new FirebaseInstallationsError(installationsClientErrorCode.API_ERROR); const stub = sinon.stub(Installations.prototype, 'deleteInstallation') .rejects(originalError); stubs.push(stub); @@ -186,7 +190,7 @@ describe('InstanceId', () => { // Confirm underlying API called with expected parameters. expect(stub).to.have.been.calledOnce.and.calledWith(testInstanceId); // Confirm expected error returned. - const expectedError = new FirebaseInstanceIdError(InstanceIdClientErrorCode.API_ERROR); + const expectedError = new FirebaseInstanceIdError(instanceIdClientErrorCode.API_ERROR); expect(error).to.be.instanceOf(FirebaseInstanceIdError) expect(error).to.deep.include(expectedError); }); diff --git a/test/unit/machine-learning/machine-learning-api-client.spec.ts b/test/unit/machine-learning/machine-learning-api-client.spec.ts index 462a2a3087..619f98edc2 100644 --- a/test/unit/machine-learning/machine-learning-api-client.spec.ts +++ b/test/unit/machine-learning/machine-learning-api-client.spec.ts @@ -19,11 +19,12 @@ import * as _ from 'lodash'; import * as chai from 'chai'; import * as sinon from 'sinon'; -import { FirebaseMachineLearningError } from '../../../src/machine-learning/machine-learning-utils'; +import { FirebaseMachineLearningError } from '../../../src/machine-learning/error'; import { HttpClient } from '../../../src/utils/api-request'; import * as utils from '../utils'; import * as mocks from '../../resources/mocks'; -import { FirebaseAppError, toHttpResponse } from '../../../src/utils/error'; +import { toHttpResponse } from '../../../src/utils/error'; +import { FirebaseAppError } from '../../../src/app/error'; import { FirebaseApp } from '../../../src/app/firebase-app'; import { getMetricsHeader, getSdkVersion } from '../../../src/utils/index'; import { MachineLearningApiClient } from '../../../src/machine-learning/machine-learning-api-client'; diff --git a/test/unit/machine-learning/machine-learning.spec.ts b/test/unit/machine-learning/machine-learning.spec.ts index 5666cfbd39..9394882dfa 100644 --- a/test/unit/machine-learning/machine-learning.spec.ts +++ b/test/unit/machine-learning/machine-learning.spec.ts @@ -27,7 +27,7 @@ import { ModelResponse, OperationResponse } from '../../../src/machine-learning/machine-learning-api-client'; -import { FirebaseMachineLearningError } from '../../../src/machine-learning/machine-learning-utils'; +import { FirebaseMachineLearningError } from '../../../src/machine-learning/error'; import { deepCopy } from '../../../src/utils/deep-copy'; import { MachineLearning, Model, ModelOptions } from '../../../src/machine-learning/index'; diff --git a/test/unit/messaging/messaging-errors-internal.spec.ts b/test/unit/messaging/messaging-errors-internal.spec.ts index 0ea48023e1..360fe0716b 100644 --- a/test/unit/messaging/messaging-errors-internal.spec.ts +++ b/test/unit/messaging/messaging-errors-internal.spec.ts @@ -22,7 +22,7 @@ import * as chaiAsPromised from 'chai-as-promised'; import { createFirebaseError } from '../../../src/messaging/messaging-errors-internal'; import { RequestResponseError, RequestResponse } from '../../../src/utils/api-request'; -import { FirebaseMessagingError } from '../../../src/utils/error'; +import { FirebaseMessagingError } from '../../../src/messaging/error'; chai.should(); chai.use(sinonChai); diff --git a/test/unit/messaging/messaging.spec.ts b/test/unit/messaging/messaging.spec.ts index af4dad1d4d..e3daa656e6 100644 --- a/test/unit/messaging/messaging.spec.ts +++ b/test/unit/messaging/messaging.spec.ts @@ -34,7 +34,7 @@ import { import { HttpClient } from '../../../src/utils/api-request'; import { getMetricsHeader, getSdkVersion } from '../../../src/utils/index'; import * as utils from '../utils'; -import { FirebaseMessagingSessionError } from '../../../src/utils/error'; +import { FirebaseMessagingSessionError } from '../../../src/messaging/error'; chai.should(); chai.use(sinonChai); diff --git a/test/unit/phone-number-verification/phone-number-verification-api-client-internal.spec.ts b/test/unit/phone-number-verification/phone-number-verification-api-client-internal.spec.ts index 123f13f9ef..021da9524b 100644 --- a/test/unit/phone-number-verification/phone-number-verification-api-client-internal.spec.ts +++ b/test/unit/phone-number-verification/phone-number-verification-api-client-internal.spec.ts @@ -19,11 +19,13 @@ import { expect } from 'chai'; import { - FirebasePhoneNumberVerificationError, JWKS_URL, - FPNV_TOKEN_INFO, - FPNV_ERROR_CODE_MAPPING + FPNV_TOKEN_INFO } from '../../../src/phone-number-verification/phone-number-verification-api-client-internal'; +import { + FirebasePhoneNumberVerificationError, + FPNV_ERROR_CODE_MAPPING +} from '../../../src/phone-number-verification/error'; import { PrefixedFirebaseError, FirebaseError } from '../../../src/utils/error'; const FPNV_PREFIX = 'phone-number-verification'; diff --git a/test/unit/phone-number-verification/token-verifier.spec.ts b/test/unit/phone-number-verification/token-verifier.spec.ts index 9cc68935e1..a7fa8d2ce7 100644 --- a/test/unit/phone-number-verification/token-verifier.spec.ts +++ b/test/unit/phone-number-verification/token-verifier.spec.ts @@ -27,8 +27,8 @@ import * as util from '../../../src/utils/index'; import { PhoneNumberTokenVerifier } from '../../../src/phone-number-verification/token-verifier'; import { FirebasePhoneNumberTokenInfo, - FPNV_ERROR_CODE_MAPPING } from '../../../src/phone-number-verification/phone-number-verification-api-client-internal'; +import { FPNV_ERROR_CODE_MAPPING } from '../../../src/phone-number-verification/error'; import * as mocks from '../../resources/mocks'; chai.use(chaiAsPromised); diff --git a/test/unit/project-management/android-app.spec.ts b/test/unit/project-management/android-app.spec.ts index 76e9df01df..1b8898258d 100644 --- a/test/unit/project-management/android-app.spec.ts +++ b/test/unit/project-management/android-app.spec.ts @@ -24,7 +24,7 @@ import { ProjectManagementRequestHandler } from '../../../src/project-management/project-management-api-request-internal'; import { deepCopy } from '../../../src/utils/deep-copy'; -import { FirebaseProjectManagementError } from '../../../src/utils/error'; +import { FirebaseProjectManagementError } from '../../../src/project-management/error'; import * as mocks from '../../resources/mocks'; import { AndroidApp, AndroidAppMetadata, AppPlatform, ShaCertificate, diff --git a/test/unit/project-management/ios-app.spec.ts b/test/unit/project-management/ios-app.spec.ts index e3a090b766..09e214a52b 100644 --- a/test/unit/project-management/ios-app.spec.ts +++ b/test/unit/project-management/ios-app.spec.ts @@ -24,7 +24,7 @@ import { ProjectManagementRequestHandler } from '../../../src/project-management/project-management-api-request-internal'; import { deepCopy } from '../../../src/utils/deep-copy'; -import { FirebaseProjectManagementError } from '../../../src/utils/error'; +import { FirebaseProjectManagementError } from '../../../src/project-management/error'; import * as mocks from '../../resources/mocks'; import { AppPlatform, IosApp, IosAppMetadata } from '../../../src/project-management/index'; diff --git a/test/unit/project-management/project-management.spec.ts b/test/unit/project-management/project-management.spec.ts index 23ec2b8584..25d1283f9b 100644 --- a/test/unit/project-management/project-management.spec.ts +++ b/test/unit/project-management/project-management.spec.ts @@ -23,7 +23,7 @@ import { FirebaseApp } from '../../../src/app/firebase-app'; import { ProjectManagementRequestHandler } from '../../../src/project-management/project-management-api-request-internal'; -import { FirebaseProjectManagementError } from '../../../src/utils/error'; +import { FirebaseProjectManagementError } from '../../../src/project-management/error'; import * as mocks from '../../resources/mocks'; import { AndroidApp, AppMetadata, AppPlatform, IosApp, ProjectManagement, diff --git a/test/unit/remote-config/remote-config-api-client.spec.ts b/test/unit/remote-config/remote-config-api-client.spec.ts index f87c7f22f2..f515a76130 100644 --- a/test/unit/remote-config/remote-config-api-client.spec.ts +++ b/test/unit/remote-config/remote-config-api-client.spec.ts @@ -19,14 +19,13 @@ import * as _ from 'lodash'; import * as chai from 'chai'; import * as sinon from 'sinon'; -import { - FirebaseRemoteConfigError, - RemoteConfigApiClient -} from '../../../src/remote-config/remote-config-api-client-internal'; +import { RemoteConfigApiClient } from '../../../src/remote-config/remote-config-api-client-internal'; +import { FirebaseRemoteConfigError } from '../../../src/remote-config/error'; import { HttpClient } from '../../../src/utils/api-request'; import * as utils from '../utils'; import * as mocks from '../../resources/mocks'; -import { FirebaseAppError, toHttpResponse } from '../../../src/utils/error'; +import { toHttpResponse } from '../../../src/utils/error'; +import { FirebaseAppError } from '../../../src/app/error'; import { FirebaseApp } from '../../../src/app/firebase-app'; import { deepCopy } from '../../../src/utils/deep-copy'; import { getMetricsHeader, getSdkVersion } from '../../../src/utils/index'; diff --git a/test/unit/remote-config/remote-config.spec.ts b/test/unit/remote-config/remote-config.spec.ts index 34145c2bdc..7c923d8ec1 100644 --- a/test/unit/remote-config/remote-config.spec.ts +++ b/test/unit/remote-config/remote-config.spec.ts @@ -33,10 +33,8 @@ import { } from '../../../src/remote-config/index'; import { FirebaseApp } from '../../../src/app/firebase-app'; import * as mocks from '../../resources/mocks'; -import { - FirebaseRemoteConfigError, - RemoteConfigApiClient -} from '../../../src/remote-config/remote-config-api-client-internal'; +import { RemoteConfigApiClient } from '../../../src/remote-config/remote-config-api-client-internal'; +import { FirebaseRemoteConfigError } from '../../../src/remote-config/error'; import { deepCopy } from '../../../src/utils/deep-copy'; import { NamedCondition, ServerTemplate, ServerTemplateData, Version diff --git a/test/unit/security-rules/security-rules-api-client.spec.ts b/test/unit/security-rules/security-rules-api-client.spec.ts index f8299ae31f..d0dcf69b09 100644 --- a/test/unit/security-rules/security-rules-api-client.spec.ts +++ b/test/unit/security-rules/security-rules-api-client.spec.ts @@ -20,11 +20,12 @@ import * as _ from 'lodash'; import * as chai from 'chai'; import * as sinon from 'sinon'; import { SecurityRulesApiClient, RulesetContent } from '../../../src/security-rules/security-rules-api-client-internal'; -import { FirebaseSecurityRulesError } from '../../../src/security-rules/security-rules-internal'; +import { FirebaseSecurityRulesError } from '../../../src/security-rules/error'; import { HttpClient } from '../../../src/utils/api-request'; import * as utils from '../utils'; import * as mocks from '../../resources/mocks'; -import { FirebaseAppError, toHttpResponse } from '../../../src/utils/error'; +import { toHttpResponse } from '../../../src/utils/error'; +import { FirebaseAppError } from '../../../src/app/error'; import { FirebaseApp } from '../../../src/app/firebase-app'; import { getSdkVersion, getMetricsHeader } from '../../../src/utils/index'; diff --git a/test/unit/security-rules/security-rules.spec.ts b/test/unit/security-rules/security-rules.spec.ts index 06aaf1d970..7b683ce96d 100644 --- a/test/unit/security-rules/security-rules.spec.ts +++ b/test/unit/security-rules/security-rules.spec.ts @@ -23,7 +23,7 @@ import { SecurityRules } from '../../../src/security-rules/index'; import { FirebaseApp } from '../../../src/app/firebase-app'; import * as mocks from '../../resources/mocks'; import { SecurityRulesApiClient, RulesetContent } from '../../../src/security-rules/security-rules-api-client-internal'; -import { FirebaseSecurityRulesError } from '../../../src/security-rules/security-rules-internal'; +import { FirebaseSecurityRulesError } from '../../../src/security-rules/error'; import { deepCopy } from '../../../src/utils/deep-copy'; const expect = chai.expect; diff --git a/test/unit/utils/error.spec.ts b/test/unit/utils/error.spec.ts index c494a0103e..72b65392e1 100644 --- a/test/unit/utils/error.spec.ts +++ b/test/unit/utils/error.spec.ts @@ -21,9 +21,12 @@ import * as chai from 'chai'; import * as sinonChai from 'sinon-chai'; import * as chaiAsPromised from 'chai-as-promised'; +import { FirebaseError } from '../../../src/utils/error'; +import { FirebaseAuthError } from '../../../src/auth/error'; import { - FirebaseError, FirebaseAuthError, FirebaseMessagingError, MessagingClientErrorCode, -} from '../../../src/utils/error'; + FirebaseMessagingError, + messagingClientErrorCode, +} from '../../../src/messaging/error'; chai.should(); chai.use(sinonChai); @@ -85,7 +88,7 @@ describe('FirebaseError', () => { message: 'message', httpResponse: mockHttpResponse as any }); - + const json = error.toJSON() as any; expect(json.httpResponse).to.deep.equal({ status: 200, @@ -235,7 +238,7 @@ describe('FirebaseAuthError', () => { const mockError: any = { response: mockRequestResponse }; const error = FirebaseAuthError.fromServerError( 'USER_NOT_FOUND', 'Invalid uid', mockError); - + const json = error.toJSON() as any; expect(json.httpResponse).to.deep.equal({ status: 400, @@ -276,14 +279,14 @@ describe('FirebaseMessagingError', () => { describe('without message specified', () => { it('should initialize an error from an expected server code', () => { const error = FirebaseMessagingError.fromServerError('InvalidRegistration'); - const expectedError = MessagingClientErrorCode.INVALID_REGISTRATION_TOKEN; + const expectedError = messagingClientErrorCode.INVALID_REGISTRATION_TOKEN; expect(error.code).to.equal('messaging/' + expectedError.code); expect(error.message).to.equal(expectedError.message); }); it('should initialize an error from an unexpected server code', () => { const error = FirebaseMessagingError.fromServerError('UNEXPECTED_ERROR'); - const expectedError = MessagingClientErrorCode.UNKNOWN_ERROR; + const expectedError = messagingClientErrorCode.UNKNOWN_ERROR; expect(error.code).to.equal('messaging/' + expectedError.code); expect(error.message).to.equal(expectedError.message); }); @@ -292,14 +295,14 @@ describe('FirebaseMessagingError', () => { describe('with message specified', () => { it('should initialize an error from an expected server code', () => { const error = FirebaseMessagingError.fromServerError('InvalidRegistration', 'Message override.'); - const expectedError = MessagingClientErrorCode.INVALID_REGISTRATION_TOKEN; + const expectedError = messagingClientErrorCode.INVALID_REGISTRATION_TOKEN; expect(error.code).to.equal('messaging/' + expectedError.code); expect(error.message).to.equal('Message override.'); }); it('should initialize an error from an unexpected server code', () => { const error = FirebaseMessagingError.fromServerError('UNEXPECTED_ERROR', 'Message override.'); - const expectedError = MessagingClientErrorCode.UNKNOWN_ERROR; + const expectedError = messagingClientErrorCode.UNKNOWN_ERROR; expect(error.code).to.equal('messaging/' + expectedError.code); expect(error.message).to.equal('Message override.'); }); @@ -323,7 +326,7 @@ describe('FirebaseMessagingError', () => { const error = FirebaseMessagingError.fromServerError( 'InvalidRegistration', /* message */ undefined, mockError, ); - const expectedError = MessagingClientErrorCode.INVALID_REGISTRATION_TOKEN; + const expectedError = messagingClientErrorCode.INVALID_REGISTRATION_TOKEN; expect(error.code).to.equal('messaging/' + expectedError.code); expect(error.message).to.equal(expectedError.message); }); @@ -332,10 +335,10 @@ describe('FirebaseMessagingError', () => { const error = FirebaseMessagingError.fromServerError( 'UNEXPECTED_ERROR', /* message */ undefined, mockError, ); - const expectedError = MessagingClientErrorCode.UNKNOWN_ERROR; + const expectedError = messagingClientErrorCode.UNKNOWN_ERROR; expect(error.code).to.equal('messaging/' + expectedError.code); expect(error.message).to.be.equal( - `${ expectedError.message } Raw server response: "${ JSON.stringify(mockHttpResponse.data) }"`, + `${expectedError.message} Raw server response: "${JSON.stringify(mockHttpResponse.data)}"`, ); }); });