diff --git a/.github/scripts/compare-types/packages/auth/auth-js-sdk.d.ts b/.github/scripts/compare-types/packages/auth/auth-js-sdk.d.ts new file mode 100644 index 0000000000..a237bcb009 --- /dev/null +++ b/.github/scripts/compare-types/packages/auth/auth-js-sdk.d.ts @@ -0,0 +1,4067 @@ +/** + * Firebase Authentication + * + * @packageDocumentation + */ + +import { CompleteFn } from '@firebase/util'; +import { ErrorFactory } from '@firebase/util'; +import { ErrorFn } from '@firebase/util'; +import { FirebaseApp } from '@firebase/app'; +import { FirebaseError } from '@firebase/util'; +import { NextFn } from '@firebase/util'; +import { Observer } from '@firebase/util'; +import { Unsubscribe } from '@firebase/util'; + +/** + * A response from {@link checkActionCode}. + * + * @public + */ +export declare interface ActionCodeInfo { + /** + * The data associated with the action code. + * + * @remarks + * For the {@link ActionCodeOperation}.PASSWORD_RESET, {@link ActionCodeOperation}.VERIFY_EMAIL, and + * {@link ActionCodeOperation}.RECOVER_EMAIL actions, this object contains an email field with the address + * the email was sent to. + * + * For the {@link ActionCodeOperation}.RECOVER_EMAIL action, which allows a user to undo an email address + * change, this object also contains a `previousEmail` field with the user account's current + * email address. After the action completes, the user's email address will revert to the value + * in the `email` field from the value in `previousEmail` field. + * + * For the {@link ActionCodeOperation}.VERIFY_AND_CHANGE_EMAIL action, which allows a user to verify the + * email before updating it, this object contains a `previousEmail` field with the user account's + * email address before updating. After the action completes, the user's email address will be + * updated to the value in the `email` field from the value in `previousEmail` field. + * + * For the {@link ActionCodeOperation}.REVERT_SECOND_FACTOR_ADDITION action, which allows a user to + * unenroll a newly added second factor, this object contains a `multiFactorInfo` field with + * the information about the second factor. For phone second factor, the `multiFactorInfo` + * is a {@link MultiFactorInfo} object, which contains the phone number. + */ + data: { + email?: string | null; + multiFactorInfo?: MultiFactorInfo | null; + previousEmail?: string | null; + }; + /** + * The type of operation that generated the action code. + */ + operation: (typeof ActionCodeOperation)[keyof typeof ActionCodeOperation]; +} + +/** + * An enumeration of the possible email action types. + * + * @public + */ +export declare const ActionCodeOperation: { + /** The email link sign-in action. */ + readonly EMAIL_SIGNIN: "EMAIL_SIGNIN"; + /** The password reset action. */ + readonly PASSWORD_RESET: "PASSWORD_RESET"; + /** The email revocation action. */ + readonly RECOVER_EMAIL: "RECOVER_EMAIL"; + /** The revert second factor addition email action. */ + readonly REVERT_SECOND_FACTOR_ADDITION: "REVERT_SECOND_FACTOR_ADDITION"; + /** The revert second factor addition email action. */ + readonly VERIFY_AND_CHANGE_EMAIL: "VERIFY_AND_CHANGE_EMAIL"; + /** The email verification action. */ + readonly VERIFY_EMAIL: "VERIFY_EMAIL"; +}; + +/** + * An interface that defines the required continue/state URL with optional Android and iOS + * bundle identifiers. + * + * @public + */ +export declare interface ActionCodeSettings { + /** + * Sets the Android package name. + * + * @remarks + * This will try to open the link in an Android app if it is installed. + */ + android?: { + installApp?: boolean; + minimumVersion?: string; + packageName: string; + }; + /** + * When set to true, the action code link will be be sent as a Universal Link or Android App + * Link and will be opened by the app if installed. + * + * @remarks + * In the false case, the code will be sent to the web widget first and then on continue will + * redirect to the app if installed. + * + * @defaultValue false + */ + handleCodeInApp?: boolean; + /** + * Sets the iOS bundle ID. + * + * @remarks + * This will try to open the link in an iOS app if it is installed. + */ + iOS?: { + bundleId: string; + }; + /** + * Sets the link continue/state URL. + * + * @remarks + * This has different meanings in different contexts: + * - When the link is handled in the web action widgets, this is the deep link in the + * `continueUrl` query parameter. + * - When the link is handled in the app directly, this is the `continueUrl` query parameter in + * the deep link of the Dynamic Link or Hosting link. + */ + url: string; + /** + * When multiple custom dynamic link domains are defined for a project, specify which one to use + * when the link is to be opened via a specified mobile app (for example, `example.page.link`). + * + * + * @defaultValue The first domain is automatically selected. + * + * @deprecated Firebase Dynamic Links is deprecated and will be shut down as early as August + * 2025. Instead, use {@link ActionCodeSettings.linkDomain} to set a custom domain for mobile + * links. Learn more in the {@link https://firebase.google.com/support/dynamic-links-faq | Dynamic Links deprecation FAQ}. + */ + dynamicLinkDomain?: string; + /** + * The optional custom Firebase Hosting domain to use when the link is to be opened via + * a specified mobile app. The domain must be configured in Firebase Hosting and owned + * by the project. This cannot be a default Hosting domain (`web.app` or `firebaseapp.com`). + * + * @defaultValue The default Hosting domain will be used (for example, `example.firebaseapp.com`). + */ + linkDomain?: string; +} + +/** + * @license + * Copyright 2020 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. + */ +/** + * A utility class to parse email action URLs such as password reset, email verification, + * email link sign in, etc. + * + * @public + */ +export declare class ActionCodeURL { + /** + * The API key of the email action link. + */ + readonly apiKey: string; + /** + * The action code of the email action link. + */ + readonly code: string; + /** + * The continue URL of the email action link. Null if not provided. + */ + readonly continueUrl: string | null; + /** + * The language code of the email action link. Null if not provided. + */ + readonly languageCode: string | null; + /** + * The action performed by the email action link. It returns from one of the types from + * {@link ActionCodeInfo} + */ + readonly operation: string; + /** + * The tenant ID of the email action link. Null if the email action is from the parent project. + */ + readonly tenantId: string | null; + /* Excluded from this release type: __constructor */ + /** + * Parses the email action link string and returns an {@link ActionCodeURL} if the link is valid, + * otherwise returns null. + * + * @param link - The email action link string. + * @returns The {@link ActionCodeURL} object, or null if the link is invalid. + * + * @public + */ + static parseLink(link: string): ActionCodeURL | null; +} + +/** + * A structure containing additional user information from a federated identity provider. + * + * @public + */ +export declare interface AdditionalUserInfo { + /** + * Whether the user is new (created via sign-up) or existing (authenticated using sign-in). + */ + readonly isNewUser: boolean; + /** + * Map containing IDP-specific user data. + */ + readonly profile: Record | null; + /** + * Identifier for the provider used to authenticate this user. + */ + readonly providerId: string | null; + /** + * The username if the provider is GitHub or Twitter. + */ + readonly username?: string | null; +} + +declare interface APIUserInfo { + localId?: string; + displayName?: string; + photoUrl?: string; + email?: string; + emailVerified?: boolean; + phoneNumber?: string; + lastLoginAt?: number; + createdAt?: number; + tenantId?: string; + passwordHash?: string; + providerUserInfo?: ProviderUserInfo[]; + mfaInfo?: MfaEnrollment[]; +} + +/** + * A verifier for domain verification and abuse prevention. + * + * @remarks + * Currently, the only implementation is {@link RecaptchaVerifier}. + * + * @public + */ +export declare interface ApplicationVerifier { + /** + * Identifies the type of application verifier (e.g. "recaptcha"). + */ + readonly type: string; + /** + * Executes the verification process. + * + * @returns A Promise for a token that can be used to assert the validity of a request. + */ + verify(): Promise; +} + +declare interface ApplicationVerifierInternal extends ApplicationVerifier { + /* Excluded from this release type: _reset */ +} + +/** + * Applies a verification code sent to the user by email or other out-of-band mechanism. + * + * @param auth - The {@link Auth} instance. + * @param oobCode - A verification code sent to the user. + * + * @public + */ +export declare function applyActionCode(auth: Auth, oobCode: string): Promise; + +declare type AppName = string; + +/** + * Interface representing Firebase Auth service. + * + * @remarks + * See {@link https://firebase.google.com/docs/auth/ | Firebase Authentication} for a full guide + * on how to use the Firebase Auth service. + * + * @public + */ +export declare interface Auth { + /** The {@link @firebase/app#FirebaseApp} associated with the `Auth` service instance. */ + readonly app: FirebaseApp; + /** The name of the app associated with the `Auth` service instance. */ + readonly name: string; + /** The {@link Config} used to initialize this instance. */ + readonly config: Config; + /** + * Changes the type of persistence on the `Auth` instance. + * + * @remarks + * This will affect the currently saved Auth session and applies this type of persistence for + * future sign-in requests, including sign-in with redirect requests. + * + * This makes it easy for a user signing in to specify whether their session should be + * remembered or not. It also makes it easier to never persist the Auth state for applications + * that are shared by other users or have sensitive data. + * + * This method does not work in a Node.js environment. + * + * @example + * ```javascript + * auth.setPersistence(browserSessionPersistence); + * ``` + * + * @param persistence - The {@link Persistence} to use. + */ + setPersistence(persistence: Persistence): Promise; + /** + * The {@link Auth} instance's language code. + * + * @remarks + * This is a readable/writable property. When set to null, the default Firebase Console language + * setting is applied. The language code will propagate to email action templates (password + * reset, email verification and email change revocation), SMS templates for phone authentication, + * reCAPTCHA verifier and OAuth popup/redirect operations provided the specified providers support + * localization with the language code specified. + */ + languageCode: string | null; + /** + * The {@link Auth} instance's tenant ID. + * + * @remarks + * This is a readable/writable property. When you set the tenant ID of an {@link Auth} instance, all + * future sign-in/sign-up operations will pass this tenant ID and sign in or sign up users to + * the specified tenant project. When set to null, users are signed in to the parent project. + * + * @example + * ```javascript + * // Set the tenant ID on Auth instance. + * auth.tenantId = 'TENANT_PROJECT_ID'; + * + * // All future sign-in request now include tenant ID. + * const result = await signInWithEmailAndPassword(auth, email, password); + * // result.user.tenantId should be 'TENANT_PROJECT_ID'. + * ``` + * + * @defaultValue null + */ + tenantId: string | null; + /** + * The {@link Auth} instance's settings. + * + * @remarks + * This is used to edit/read configuration related options such as app verification mode for + * phone authentication. + */ + readonly settings: AuthSettings; + /** + * Adds an observer for changes to the user's sign-in state. + * + * @remarks + * To keep the old behavior, see {@link Auth.onIdTokenChanged}. + * + * @param nextOrObserver - callback triggered on change. + * @param error - Deprecated. This callback is never triggered. Errors + * on signing in/out can be caught in promises returned from + * sign-in/sign-out functions. + * @param completed - Deprecated. This callback is never triggered. + */ + onAuthStateChanged(nextOrObserver: NextOrObserver, error?: ErrorFn, completed?: CompleteFn): Unsubscribe; + /** + * Adds a blocking callback that runs before an auth state change + * sets a new user. + * + * @param callback - callback triggered before new user value is set. + * If this throws, it blocks the user from being set. + * @param onAbort - callback triggered if a later `beforeAuthStateChanged()` + * callback throws, allowing you to undo any side effects. + */ + beforeAuthStateChanged(callback: (user: User | null) => void | Promise, onAbort?: () => void): Unsubscribe; + /** + * Adds an observer for changes to the signed-in user's ID token. + * + * @remarks + * This includes sign-in, sign-out, and token refresh events. + * + * @param nextOrObserver - callback triggered on change. + * @param error - Deprecated. This callback is never triggered. Errors + * on signing in/out can be caught in promises returned from + * sign-in/sign-out functions. + * @param completed - Deprecated. This callback is never triggered. + */ + onIdTokenChanged(nextOrObserver: NextOrObserver, error?: ErrorFn, completed?: CompleteFn): Unsubscribe; + /** + * returns a promise that resolves immediately when the initial + * auth state is settled. When the promise resolves, the current user might be a valid user + * or `null` if the user signed out. + */ + authStateReady(): Promise; + /** The currently signed-in user (or null). */ + readonly currentUser: User | null; + /** The current emulator configuration (or null). */ + readonly emulatorConfig: EmulatorConfig | null; + /** + * Asynchronously sets the provided user as {@link Auth.currentUser} on the {@link Auth} instance. + * + * @remarks + * A new instance copy of the user provided will be made and set as currentUser. + * + * This will trigger {@link Auth.onAuthStateChanged} and {@link Auth.onIdTokenChanged} listeners + * like other sign in methods. + * + * The operation fails with an error if the user to be updated belongs to a different Firebase + * project. + * + * @param user - The new {@link User}. + */ + updateCurrentUser(user: User | null): Promise; + /** + * Sets the current language to the default device/browser preference. + */ + useDeviceLanguage(): void; + /** + * Signs out the current user. This does not automatically revoke the user's ID token. + * + * @remarks + * This method is not supported by {@link Auth} instances created with a + * {@link @firebase/app#FirebaseServerApp}. + */ + signOut(): Promise; +} + +/** + * Interface that represents the credentials returned by an {@link AuthProvider}. + * + * @remarks + * Implementations specify the details about each auth provider's credential requirements. + * + * @public + */ +export declare class AuthCredential { + /** + * The authentication provider ID for the credential. + * + * @remarks + * For example, 'facebook.com', or 'google.com'. + */ + readonly providerId: string; + /** + * The authentication sign in method for the credential. + * + * @remarks + * For example, {@link SignInMethod}.EMAIL_PASSWORD, or + * {@link SignInMethod}.EMAIL_LINK. This corresponds to the sign-in method + * identifier as returned in {@link fetchSignInMethodsForEmail}. + */ + readonly signInMethod: string; + /* Excluded from this release type: __constructor */ + /** + * Returns a JSON-serializable representation of this object. + * + * @returns a JSON-serializable representation of this object. + */ + toJSON(): object; + /* Excluded from this release type: _getIdTokenResponse */ + /* Excluded from this release type: _linkToIdToken */ + /* Excluded from this release type: _getReauthenticationResolver */ +} + +/** + * Interface for an `Auth` error. + * + * @public + */ +export declare interface AuthError extends FirebaseError { + /** Details about the Firebase Auth error. */ + readonly customData: { + /** The name of the Firebase App which triggered this error. */ + readonly appName: string; + /** The email address of the user's account, used for sign-in and linking. */ + readonly email?: string; + /** The phone number of the user's account, used for sign-in and linking. */ + readonly phoneNumber?: string; + /** + * The tenant ID being used for sign-in and linking. + * + * @remarks + * If you use {@link signInWithRedirect} to sign in, + * you have to set the tenant ID on the {@link Auth} instance again as the tenant ID is not persisted + * after redirection. + */ + readonly tenantId?: string; + }; +} + +/* Excluded from this release type: AuthErrorCode */ + +/** + * A map of potential `Auth` error codes, for easier comparison with errors + * thrown by the SDK. + * + * @remarks + * Note that you can't tree-shake individual keys + * in the map, so by using the map you might substantially increase your + * bundle size. + * + * @public + */ +export declare const AuthErrorCodes: { + readonly ADMIN_ONLY_OPERATION: "auth/admin-restricted-operation"; + readonly ARGUMENT_ERROR: "auth/argument-error"; + readonly APP_NOT_AUTHORIZED: "auth/app-not-authorized"; + readonly APP_NOT_INSTALLED: "auth/app-not-installed"; + readonly CAPTCHA_CHECK_FAILED: "auth/captcha-check-failed"; + readonly CODE_EXPIRED: "auth/code-expired"; + readonly CORDOVA_NOT_READY: "auth/cordova-not-ready"; + readonly CORS_UNSUPPORTED: "auth/cors-unsupported"; + readonly CREDENTIAL_ALREADY_IN_USE: "auth/credential-already-in-use"; + readonly CREDENTIAL_MISMATCH: "auth/custom-token-mismatch"; + readonly CREDENTIAL_TOO_OLD_LOGIN_AGAIN: "auth/requires-recent-login"; + readonly DEPENDENT_SDK_INIT_BEFORE_AUTH: "auth/dependent-sdk-initialized-before-auth"; + readonly DYNAMIC_LINK_NOT_ACTIVATED: "auth/dynamic-link-not-activated"; + readonly EMAIL_CHANGE_NEEDS_VERIFICATION: "auth/email-change-needs-verification"; + readonly EMAIL_EXISTS: "auth/email-already-in-use"; + readonly EMULATOR_CONFIG_FAILED: "auth/emulator-config-failed"; + readonly EXPIRED_OOB_CODE: "auth/expired-action-code"; + readonly EXPIRED_POPUP_REQUEST: "auth/cancelled-popup-request"; + readonly INTERNAL_ERROR: "auth/internal-error"; + readonly INVALID_API_KEY: "auth/invalid-api-key"; + readonly INVALID_APP_CREDENTIAL: "auth/invalid-app-credential"; + readonly INVALID_APP_ID: "auth/invalid-app-id"; + readonly INVALID_AUTH: "auth/invalid-user-token"; + readonly INVALID_AUTH_EVENT: "auth/invalid-auth-event"; + readonly INVALID_CERT_HASH: "auth/invalid-cert-hash"; + readonly INVALID_CODE: "auth/invalid-verification-code"; + readonly INVALID_CONTINUE_URI: "auth/invalid-continue-uri"; + readonly INVALID_CORDOVA_CONFIGURATION: "auth/invalid-cordova-configuration"; + readonly INVALID_CUSTOM_TOKEN: "auth/invalid-custom-token"; + readonly INVALID_DYNAMIC_LINK_DOMAIN: "auth/invalid-dynamic-link-domain"; + readonly INVALID_EMAIL: "auth/invalid-email"; + readonly INVALID_EMULATOR_SCHEME: "auth/invalid-emulator-scheme"; + readonly INVALID_IDP_RESPONSE: "auth/invalid-credential"; + readonly INVALID_LOGIN_CREDENTIALS: "auth/invalid-credential"; + readonly INVALID_MESSAGE_PAYLOAD: "auth/invalid-message-payload"; + readonly INVALID_MFA_SESSION: "auth/invalid-multi-factor-session"; + readonly INVALID_OAUTH_CLIENT_ID: "auth/invalid-oauth-client-id"; + readonly INVALID_OAUTH_PROVIDER: "auth/invalid-oauth-provider"; + readonly INVALID_OOB_CODE: "auth/invalid-action-code"; + readonly INVALID_ORIGIN: "auth/unauthorized-domain"; + readonly INVALID_PASSWORD: "auth/wrong-password"; + readonly INVALID_PERSISTENCE: "auth/invalid-persistence-type"; + readonly INVALID_PHONE_NUMBER: "auth/invalid-phone-number"; + readonly INVALID_PROVIDER_ID: "auth/invalid-provider-id"; + readonly INVALID_RECIPIENT_EMAIL: "auth/invalid-recipient-email"; + readonly INVALID_SENDER: "auth/invalid-sender"; + readonly INVALID_SESSION_INFO: "auth/invalid-verification-id"; + readonly INVALID_TENANT_ID: "auth/invalid-tenant-id"; + readonly MFA_INFO_NOT_FOUND: "auth/multi-factor-info-not-found"; + readonly MFA_REQUIRED: "auth/multi-factor-auth-required"; + readonly MISSING_ANDROID_PACKAGE_NAME: "auth/missing-android-pkg-name"; + readonly MISSING_APP_CREDENTIAL: "auth/missing-app-credential"; + readonly MISSING_AUTH_DOMAIN: "auth/auth-domain-config-required"; + readonly MISSING_CODE: "auth/missing-verification-code"; + readonly MISSING_CONTINUE_URI: "auth/missing-continue-uri"; + readonly MISSING_IFRAME_START: "auth/missing-iframe-start"; + readonly MISSING_IOS_BUNDLE_ID: "auth/missing-ios-bundle-id"; + readonly MISSING_OR_INVALID_NONCE: "auth/missing-or-invalid-nonce"; + readonly MISSING_MFA_INFO: "auth/missing-multi-factor-info"; + readonly MISSING_MFA_SESSION: "auth/missing-multi-factor-session"; + readonly MISSING_PHONE_NUMBER: "auth/missing-phone-number"; + readonly MISSING_PASSWORD: "auth/missing-password"; + readonly MISSING_SESSION_INFO: "auth/missing-verification-id"; + readonly MODULE_DESTROYED: "auth/app-deleted"; + readonly NEED_CONFIRMATION: "auth/account-exists-with-different-credential"; + readonly NETWORK_REQUEST_FAILED: "auth/network-request-failed"; + readonly NULL_USER: "auth/null-user"; + readonly NO_AUTH_EVENT: "auth/no-auth-event"; + readonly NO_SUCH_PROVIDER: "auth/no-such-provider"; + readonly OPERATION_NOT_ALLOWED: "auth/operation-not-allowed"; + readonly OPERATION_NOT_SUPPORTED: "auth/operation-not-supported-in-this-environment"; + readonly POPUP_BLOCKED: "auth/popup-blocked"; + readonly POPUP_CLOSED_BY_USER: "auth/popup-closed-by-user"; + readonly PROVIDER_ALREADY_LINKED: "auth/provider-already-linked"; + readonly QUOTA_EXCEEDED: "auth/quota-exceeded"; + readonly REDIRECT_CANCELLED_BY_USER: "auth/redirect-cancelled-by-user"; + readonly REDIRECT_OPERATION_PENDING: "auth/redirect-operation-pending"; + readonly REJECTED_CREDENTIAL: "auth/rejected-credential"; + readonly SECOND_FACTOR_ALREADY_ENROLLED: "auth/second-factor-already-in-use"; + readonly SECOND_FACTOR_LIMIT_EXCEEDED: "auth/maximum-second-factor-count-exceeded"; + readonly TENANT_ID_MISMATCH: "auth/tenant-id-mismatch"; + readonly TIMEOUT: "auth/timeout"; + readonly TOKEN_EXPIRED: "auth/user-token-expired"; + readonly TOO_MANY_ATTEMPTS_TRY_LATER: "auth/too-many-requests"; + readonly UNAUTHORIZED_DOMAIN: "auth/unauthorized-continue-uri"; + readonly UNSUPPORTED_FIRST_FACTOR: "auth/unsupported-first-factor"; + readonly UNSUPPORTED_PERSISTENCE: "auth/unsupported-persistence-type"; + readonly UNSUPPORTED_TENANT_OPERATION: "auth/unsupported-tenant-operation"; + readonly UNVERIFIED_EMAIL: "auth/unverified-email"; + readonly USER_CANCELLED: "auth/user-cancelled"; + readonly USER_DELETED: "auth/user-not-found"; + readonly USER_DISABLED: "auth/user-disabled"; + readonly USER_MISMATCH: "auth/user-mismatch"; + readonly USER_SIGNED_OUT: "auth/user-signed-out"; + readonly WEAK_PASSWORD: "auth/weak-password"; + readonly WEB_STORAGE_UNSUPPORTED: "auth/web-storage-unsupported"; + readonly ALREADY_INITIALIZED: "auth/already-initialized"; + readonly RECAPTCHA_NOT_ENABLED: "auth/recaptcha-not-enabled"; + readonly MISSING_RECAPTCHA_TOKEN: "auth/missing-recaptcha-token"; + readonly INVALID_RECAPTCHA_TOKEN: "auth/invalid-recaptcha-token"; + readonly INVALID_RECAPTCHA_ACTION: "auth/invalid-recaptcha-action"; + readonly MISSING_CLIENT_TYPE: "auth/missing-client-type"; + readonly MISSING_RECAPTCHA_VERSION: "auth/missing-recaptcha-version"; + readonly INVALID_RECAPTCHA_VERSION: "auth/invalid-recaptcha-version"; + readonly INVALID_REQ_TYPE: "auth/invalid-req-type"; + readonly INVALID_HOSTING_LINK_DOMAIN: "auth/invalid-hosting-link-domain"; +}; + +/** + * A mapping of error codes to error messages. + * + * @remarks + * + * While error messages are useful for debugging (providing verbose textual + * context around what went wrong), these strings take up a lot of space in the + * compiled code. When deploying code in production, using {@link prodErrorMap} + * will save you roughly 10k compressed/gzipped over {@link debugErrorMap}. You + * can select the error map during initialization: + * + * ```javascript + * initializeAuth(app, {errorMap: debugErrorMap}) + * ``` + * + * When initializing Auth, {@link prodErrorMap} is default. + * + * @public + */ +export declare interface AuthErrorMap { +} + +/* Excluded from this release type: AuthErrorParams */ + +/* Excluded from this release type: AuthEvent */ + +/* Excluded from this release type: AuthEventConsumer */ + +declare interface AuthEventError extends Error { + code: string; + message: string; +} + +/* Excluded from this release type: AuthEventType */ + +/* Excluded from this release type: AuthInternal */ + +declare class AuthPopup { + readonly window: Window | null; + associatedEvent: string | null; + constructor(window: Window | null); + close(): void; +} + +/** + * Interface that represents an auth provider, used to facilitate creating {@link AuthCredential}. + * + * @public + */ +export declare interface AuthProvider { + /** + * Provider for which credentials can be constructed. + */ + readonly providerId: string; +} + +/** + * Interface representing an {@link Auth} instance's settings. + * + * @remarks Currently used for enabling/disabling app verification for phone Auth testing. + * + * @public + */ +export declare interface AuthSettings { + /** + * When set, this property disables app verification for the purpose of testing phone + * authentication. For this property to take effect, it needs to be set before rendering a + * reCAPTCHA app verifier. When this is disabled, a mock reCAPTCHA is rendered instead. This is + * useful for manual testing during development or for automated integration tests. + * + * In order to use this feature, you will need to + * {@link https://firebase.google.com/docs/auth/web/phone-auth#test-with-whitelisted-phone-numbers | whitelist your phone number} + * via the Firebase Console. + * + * The default value is false (app verification is enabled). + */ + appVerificationDisabledForTesting: boolean; +} + +/** + * MFA Info as returned by the API. + */ +declare interface BaseMfaEnrollment { + mfaEnrollmentId: string; + enrolledAt: number; + displayName?: string; +} + +/** + * Common code to all OAuth providers. This is separate from the + * {@link OAuthProvider} so that child providers (like + * {@link GoogleAuthProvider}) don't inherit the `credential` instance method. + * Instead, they rely on a static `credential` method. + */ +declare abstract class BaseOAuthProvider extends FederatedAuthProvider implements AuthProvider { + /* Excluded from this release type: scopes */ + /** + * Add an OAuth scope to the credential. + * + * @param scope - Provider OAuth scope to add. + */ + addScope(scope: string): AuthProvider; + /** + * Retrieve the current list of OAuth scopes. + */ + getScopes(): string[]; +} + +/** + * Adds a blocking callback that runs before an auth state change + * sets a new user. + * + * @param auth - The {@link Auth} instance. + * @param callback - callback triggered before new user value is set. + * If this throws, it blocks the user from being set. + * @param onAbort - callback triggered if a later `beforeAuthStateChanged()` + * callback throws, allowing you to undo any side effects. + */ +export declare function beforeAuthStateChanged(auth: Auth, callback: (user: User | null) => void | Promise, onAbort?: () => void): Unsubscribe; + +/** + * An implementation of {@link Persistence} of type `COOKIE`, for use on the client side in + * applications leveraging hybrid rendering and middleware. + * + * @remarks This persistence method requires companion middleware to function, such as that provided + * by {@link https://firebaseopensource.com/projects/firebaseextended/reactfire/ | ReactFire} for + * NextJS. + * @beta + */ +export declare const browserCookiePersistence: Persistence; + +/** + * An implementation of {@link Persistence} of type `LOCAL` using `localStorage` + * for the underlying storage. + * + * @public + */ +export declare const browserLocalPersistence: Persistence; + +/** + * An implementation of {@link PopupRedirectResolver} suitable for browser + * based applications. + * + * @remarks + * This method does not work in a Node.js environment. + * + * @public + */ +export declare const browserPopupRedirectResolver: PopupRedirectResolver; + +/** + * An implementation of {@link Persistence} of `SESSION` using `sessionStorage` + * for the underlying storage. + * + * @public + */ +export declare const browserSessionPersistence: Persistence; + +/** + * Checks a verification code sent to the user by email or other out-of-band mechanism. + * + * @returns metadata about the code. + * + * @param auth - The {@link Auth} instance. + * @param oobCode - A verification code sent to the user. + * + * @public + */ +export declare function checkActionCode(auth: Auth, oobCode: string): Promise; + +/* Excluded from this release type: ClientPlatform */ +export { CompleteFn } + +/** + * Interface representing the `Auth` config. + * + * @public + */ +export declare interface Config { + /** + * The API Key used to communicate with the Firebase Auth backend. + */ + apiKey: string; + /** + * The host at which the Firebase Auth backend is running. + */ + apiHost: string; + /** + * The scheme used to communicate with the Firebase Auth backend. + */ + apiScheme: string; + /** + * The host at which the Secure Token API is running. + */ + tokenApiHost: string; + /** + * The SDK Client Version. + */ + sdkClientVersion: string; + /** + * The domain at which the web widgets are hosted (provided via Firebase Config). + */ + authDomain?: string; +} + +/* Excluded from this release type: ConfigInternal */ + +/** + * A result from a phone number sign-in, link, or reauthenticate call. + * + * @public + */ +export declare interface ConfirmationResult { + /** + * The phone number authentication operation's verification ID. + * + * @remarks + * This can be used along with the verification code to initialize a + * {@link PhoneAuthCredential}. + */ + readonly verificationId: string; + /** + * Finishes a phone number sign-in, link, or reauthentication. + * + * @example + * ```javascript + * const confirmationResult = await signInWithPhoneNumber(auth, phoneNumber, applicationVerifier); + * // Obtain verificationCode from the user. + * const userCredential = await confirmationResult.confirm(verificationCode); + * ``` + * + * @param verificationCode - The code that was sent to the user's mobile device. + */ + confirm(verificationCode: string): Promise; +} + +/** + * Completes the password reset process, given a confirmation code and new password. + * + * @param auth - The {@link Auth} instance. + * @param oobCode - A confirmation code sent to the user. + * @param newPassword - The new password. + * + * @public + */ +export declare function confirmPasswordReset(auth: Auth, oobCode: string, newPassword: string): Promise; + +/** + * Changes the {@link Auth} instance to communicate with the Firebase Auth Emulator, instead of production + * Firebase Auth services. + * + * @remarks + * This must be called synchronously immediately following the first call to + * {@link initializeAuth}. Do not use with production credentials as emulator + * traffic is not encrypted. + * + * + * @example + * ```javascript + * connectAuthEmulator(auth, 'http://127.0.0.1:9099', { disableWarnings: true }); + * ``` + * + * @param auth - The {@link Auth} instance. + * @param url - The URL at which the emulator is running (eg, 'http://localhost:9099'). + * @param options - Optional. `options.disableWarnings` defaults to `false`. Set it to + * `true` to disable the warning banner attached to the DOM. + * + * @public + */ +export declare function connectAuthEmulator(auth: Auth, url: string, options?: { + disableWarnings: boolean; +}): void; + +/** + * Creates a new user account associated with the specified email address and password. + * + * @remarks + * On successful creation of the user account, this user will also be signed in to your application. + * + * User account creation can fail if the account already exists or the password is invalid. + * + * This method is not supported on {@link Auth} instances created with a + * {@link @firebase/app#FirebaseServerApp}. + * + * Note: The email address acts as a unique identifier for the user and enables an email-based + * password reset. This function will create a new user account and set the initial user password. + * + * @param auth - The {@link Auth} instance. + * @param email - The user's email address. + * @param password - The user's chosen password. + * + * @public + */ +export declare function createUserWithEmailAndPassword(auth: Auth, email: string, password: string): Promise; + +/** + * Map of OAuth Custom Parameters. + * + * @public + */ +export declare type CustomParameters = Record; + +/** + * A verbose error map with detailed descriptions for most error codes. + * + * See discussion at {@link AuthErrorMap} + * + * @public + */ +export declare const debugErrorMap: AuthErrorMap; + +/** + * Deletes and signs out the user. + * + * @remarks + * Important: this is a security-sensitive operation that requires the user to have recently + * signed in. If this requirement isn't met, ask the user to authenticate again and then call + * {@link reauthenticateWithCredential}. + * + * @param user - The user. + * + * @public + */ +export declare function deleteUser(user: User): Promise; + +/** + * The dependencies that can be used to initialize an {@link Auth} instance. + * + * @remarks + * + * The modular SDK enables tree shaking by allowing explicit declarations of + * dependencies. For example, a web app does not need to include code that + * enables Cordova redirect sign in. That functionality is therefore split into + * {@link browserPopupRedirectResolver} and + * {@link cordovaPopupRedirectResolver}. The dependencies object is how Auth is + * configured to reduce bundle sizes. + * + * There are two ways to initialize an {@link Auth} instance: {@link getAuth} and + * {@link initializeAuth}. `getAuth` initializes everything using + * platform-specific configurations, while `initializeAuth` takes a + * `Dependencies` object directly, giving you more control over what is used. + * + * @public + */ +export declare interface Dependencies { + /** + * Which {@link Persistence} to use. If this is an array, the first + * `Persistence` that the device supports is used. The SDK searches for an + * existing account in order and, if one is found in a secondary + * `Persistence`, the account is moved to the primary `Persistence`. + * + * If no persistence is provided, the SDK falls back on + * {@link inMemoryPersistence}. + */ + persistence?: Persistence | Persistence[]; + /** + * The {@link PopupRedirectResolver} to use. This value depends on the + * platform. Options are {@link browserPopupRedirectResolver} and + * {@link cordovaPopupRedirectResolver}. This field is optional if neither + * {@link signInWithPopup} or {@link signInWithRedirect} are being used. + */ + popupRedirectResolver?: PopupRedirectResolver; + /** + * Which {@link AuthErrorMap} to use. + */ + errorMap?: AuthErrorMap; +} + +/** + * Interface that represents the credentials returned by {@link EmailAuthProvider} for + * {@link ProviderId}.PASSWORD + * + * @remarks + * Covers both {@link SignInMethod}.EMAIL_PASSWORD and + * {@link SignInMethod}.EMAIL_LINK. + * + * @public + */ +export declare class EmailAuthCredential extends AuthCredential { + /* Excluded from this release type: _email */ + /* Excluded from this release type: _password */ + /* Excluded from this release type: _tenantId */ + /* Excluded from this release type: __constructor */ + /* Excluded from this release type: _fromEmailAndPassword */ + /* Excluded from this release type: _fromEmailAndCode */ + /** {@inheritdoc AuthCredential.toJSON} */ + toJSON(): object; + /** + * Static method to deserialize a JSON representation of an object into an {@link AuthCredential}. + * + * @param json - Either `object` or the stringified representation of the object. When string is + * provided, `JSON.parse` would be called first. + * + * @returns If the JSON input does not represent an {@link AuthCredential}, null is returned. + */ + static fromJSON(json: object | string): EmailAuthCredential | null; + /* Excluded from this release type: _getIdTokenResponse */ + /* Excluded from this release type: _linkToIdToken */ + /* Excluded from this release type: _getReauthenticationResolver */ +} + +/** + * Provider for generating {@link EmailAuthCredential}. + * + * @public + */ +export declare class EmailAuthProvider implements AuthProvider { + /** + * Always set to {@link ProviderId}.PASSWORD, even for email link. + */ + static readonly PROVIDER_ID: 'password'; + /** + * Always set to {@link SignInMethod}.EMAIL_PASSWORD. + */ + static readonly EMAIL_PASSWORD_SIGN_IN_METHOD: 'password'; + /** + * Always set to {@link SignInMethod}.EMAIL_LINK. + */ + static readonly EMAIL_LINK_SIGN_IN_METHOD: 'emailLink'; + /** + * Always set to {@link ProviderId}.PASSWORD, even for email link. + */ + readonly providerId: "password"; + /** + * Initialize an {@link AuthCredential} using an email and password. + * + * @example + * ```javascript + * const authCredential = EmailAuthProvider.credential(email, password); + * const userCredential = await signInWithCredential(auth, authCredential); + * ``` + * + * @example + * ```javascript + * const userCredential = await signInWithEmailAndPassword(auth, email, password); + * ``` + * + * @param email - Email address. + * @param password - User account password. + * @returns The auth provider credential. + */ + static credential(email: string, password: string): EmailAuthCredential; + /** + * Initialize an {@link AuthCredential} using an email and an email link after a sign in with + * email link operation. + * + * @example + * ```javascript + * const authCredential = EmailAuthProvider.credentialWithLink(auth, email, emailLink); + * const userCredential = await signInWithCredential(auth, authCredential); + * ``` + * + * @example + * ```javascript + * await sendSignInLinkToEmail(auth, email); + * // Obtain emailLink from user. + * const userCredential = await signInWithEmailLink(auth, email, emailLink); + * ``` + * + * @param auth - The {@link Auth} instance used to verify the link. + * @param email - Email address. + * @param emailLink - Sign-in email link. + * @returns - The auth provider credential. + */ + static credentialWithLink(email: string, emailLink: string): EmailAuthCredential; +} + +/** + * Configuration of Firebase Authentication Emulator. + * @public + */ +export declare interface EmulatorConfig { + /** + * The protocol used to communicate with the emulator ("http"/"https"). + */ + readonly protocol: string; + /** + * The hostname of the emulator, which may be a domain ("localhost"), IPv4 address ("127.0.0.1") + * or quoted IPv6 address ("[::1]"). + */ + readonly host: string; + /** + * The port of the emulator, or null if port isn't specified (i.e. protocol default). + */ + readonly port: number | null; + /** + * The emulator-specific options. + */ + readonly options: { + /** + * Whether the warning banner attached to the DOM was disabled. + */ + readonly disableWarnings: boolean; + }; +} + +declare const enum EnforcementState { + ENFORCE = "ENFORCE", + AUDIT = "AUDIT", + OFF = "OFF", + ENFORCEMENT_STATE_UNSPECIFIED = "ENFORCEMENT_STATE_UNSPECIFIED" +} +export { ErrorFn } + +/* Excluded from this release type: EventManager */ + +/** + * Provider for generating an {@link OAuthCredential} for {@link ProviderId}.FACEBOOK. + * + * @example + * ```javascript + * // Sign in using a redirect. + * const provider = new FacebookAuthProvider(); + * // Start a sign in process for an unauthenticated user. + * provider.addScope('user_birthday'); + * await signInWithRedirect(auth, provider); + * // This will trigger a full page redirect away from your app + * + * // After returning from the redirect when your app initializes you can obtain the result + * const result = await getRedirectResult(auth); + * if (result) { + * // This is the signed-in user + * const user = result.user; + * // This gives you a Facebook Access Token. + * const credential = FacebookAuthProvider.credentialFromResult(result); + * const token = credential.accessToken; + * } + * ``` + * + * @example + * ```javascript + * // Sign in using a popup. + * const provider = new FacebookAuthProvider(); + * provider.addScope('user_birthday'); + * const result = await signInWithPopup(auth, provider); + * + * // The signed-in user info. + * const user = result.user; + * // This gives you a Facebook Access Token. + * const credential = FacebookAuthProvider.credentialFromResult(result); + * const token = credential.accessToken; + * ``` + * + * @public + */ +export declare class FacebookAuthProvider extends BaseOAuthProvider { + /** Always set to {@link SignInMethod}.FACEBOOK. */ + static readonly FACEBOOK_SIGN_IN_METHOD: 'facebook.com'; + /** Always set to {@link ProviderId}.FACEBOOK. */ + static readonly PROVIDER_ID: 'facebook.com'; + constructor(); + /** + * Creates a credential for Facebook. + * + * @example + * ```javascript + * // `event` from the Facebook auth.authResponseChange callback. + * const credential = FacebookAuthProvider.credential(event.authResponse.accessToken); + * const result = await signInWithCredential(credential); + * ``` + * + * @param accessToken - Facebook access token. + */ + static credential(accessToken: string): OAuthCredential; + /** + * Used to extract the underlying {@link OAuthCredential} from a {@link UserCredential}. + * + * @param userCredential - The user credential. + */ + static credentialFromResult(userCredential: UserCredential): OAuthCredential | null; + /** + * Used to extract the underlying {@link OAuthCredential} from a {@link AuthError} which was + * thrown during a sign-in, link, or reauthenticate operation. + * + * @param userCredential - The user credential. + */ + static credentialFromError(error: FirebaseError): OAuthCredential | null; + private static credentialFromTaggedObject; +} + +/** + * An enum of factors that may be used for multifactor authentication. + * + * @public + */ +export declare const FactorId: { + /** Phone as second factor */ + readonly PHONE: "phone"; + readonly TOTP: "totp"; +}; + +/** + * The base class for all Federated providers (OAuth (including OIDC), SAML). + * + * This class is not meant to be instantiated directly. + * + * @public + */ +declare abstract class FederatedAuthProvider implements AuthProvider { + readonly providerId: string; + /* Excluded from this release type: defaultLanguageCode */ + /* Excluded from this release type: customParameters */ + /** + * Constructor for generic OAuth providers. + * + * @param providerId - Provider for which credentials should be generated. + */ + constructor(providerId: string); + /** + * Set the language gode. + * + * @param languageCode - language code + */ + setDefaultLanguage(languageCode: string | null): void; + /** + * Sets the OAuth custom parameters to pass in an OAuth request for popup and redirect sign-in + * operations. + * + * @remarks + * For a detailed list, check the reserved required OAuth 2.0 parameters such as `client_id`, + * `redirect_uri`, `scope`, `response_type`, and `state` are not allowed and will be ignored. + * + * @param customOAuthParameters - The custom OAuth parameters to pass in the OAuth request. + */ + setCustomParameters(customOAuthParameters: CustomParameters): AuthProvider; + /** + * Retrieve the current list of {@link CustomParameters}. + */ + getCustomParameters(): CustomParameters; +} + +/** + * Gets the list of possible sign in methods for the given email address. This method returns an + * empty list when + * {@link https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection | Email Enumeration Protection} + * is enabled, irrespective of the number of authentication methods available for the given email. + * + * @remarks + * This is useful to differentiate methods of sign-in for the same provider, eg. + * {@link EmailAuthProvider} which has 2 methods of sign-in, + * {@link SignInMethod}.EMAIL_PASSWORD and + * {@link SignInMethod}.EMAIL_LINK. + * + * @param auth - The {@link Auth} instance. + * @param email - The user's email address. + * + * Deprecated. Migrating off of this method is recommended as a security best-practice. + * Learn more in the Identity Platform documentation for + * {@link https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection | Email Enumeration Protection}. + * @public + */ +export declare function fetchSignInMethodsForEmail(auth: Auth, email: string): Promise; + +declare interface FinalizeMfaResponse { + idToken: string; + refreshToken: string; +} + +/* Excluded from this release type: GenericAuthErrorParams */ + +/** + * Extracts provider specific {@link AdditionalUserInfo} for the given credential. + * + * @param userCredential - The user credential. + * + * @public + */ +export declare function getAdditionalUserInfo(userCredential: UserCredential): AdditionalUserInfo | null; + +/** + * Returns the Auth instance associated with the provided {@link @firebase/app#FirebaseApp}. + * If no instance exists, initializes an Auth instance with platform-specific default dependencies. + * + * @param app - The Firebase App. + * + * @public + */ +export declare function getAuth(app?: FirebaseApp): Auth; + +/** + * Returns a JSON Web Token (JWT) used to identify the user to a Firebase service. + * + * @remarks + * Returns the current token if it has not expired or if it will not expire in the next five + * minutes. Otherwise, this will refresh the token and return a new one. + * + * @param user - The user. + * @param forceRefresh - Force refresh regardless of token expiration. + * + * @public + */ +export declare function getIdToken(user: User, forceRefresh?: boolean): Promise; + +/** + * Returns a deserialized JSON Web Token (JWT) used to identify the user to a Firebase service. + * + * @remarks + * Returns the current token if it has not expired or if it will not expire in the next five + * minutes. Otherwise, this will refresh the token and return a new one. + * + * @param user - The user. + * @param forceRefresh - Force refresh regardless of token expiration. + * + * @public + */ +export declare function getIdTokenResult(user: User, forceRefresh?: boolean): Promise; + +/** + * Provides a {@link MultiFactorResolver} suitable for completion of a + * multi-factor flow. + * + * @param auth - The {@link Auth} instance. + * @param error - The {@link MultiFactorError} raised during a sign-in, or + * reauthentication operation. + * + * @public + */ +export declare function getMultiFactorResolver(auth: Auth, error: MultiFactorError): MultiFactorResolver; + +declare interface GetRecaptchaConfigResponse { + recaptchaKey: string; + recaptchaEnforcementState: RecaptchaEnforcementProviderState[]; +} + +/** + * Returns a {@link UserCredential} from the redirect-based sign-in flow. + * + * @remarks + * If sign-in succeeded, returns the signed in user. If sign-in was unsuccessful, fails with an + * error. If no redirect operation was called, returns `null`. + * + * This method does not work in a Node.js environment or with {@link Auth} instances created with a + * {@link @firebase/app#FirebaseServerApp}. + * + * @example + * ```javascript + * // Sign in using a redirect. + * const provider = new FacebookAuthProvider(); + * // You can add additional scopes to the provider: + * provider.addScope('user_birthday'); + * // Start a sign in process for an unauthenticated user. + * await signInWithRedirect(auth, provider); + * // This will trigger a full page redirect away from your app + * + * // After returning from the redirect when your app initializes you can obtain the result + * const result = await getRedirectResult(auth); + * if (result) { + * // This is the signed-in user + * const user = result.user; + * // This gives you a Facebook Access Token. + * const credential = provider.credentialFromResult(auth, result); + * const token = credential.accessToken; + * } + * // As this API can be used for sign-in, linking and reauthentication, + * // check the operationType to determine what triggered this redirect + * // operation. + * const operationType = result.operationType; + * ``` + * + * @param auth - The {@link Auth} instance. + * @param resolver - An instance of {@link PopupRedirectResolver}, optional + * if already supplied to {@link initializeAuth} or provided by {@link getAuth}. + * + * @public + */ +export declare function getRedirectResult(auth: Auth, resolver?: PopupRedirectResolver): Promise; + +/** + * Provider for generating an {@link OAuthCredential} for {@link ProviderId}.GITHUB. + * + * @remarks + * GitHub requires an OAuth 2.0 redirect, so you can either handle the redirect directly, or use + * the {@link signInWithPopup} handler: + * + * @example + * ```javascript + * // Sign in using a redirect. + * const provider = new GithubAuthProvider(); + * // Start a sign in process for an unauthenticated user. + * provider.addScope('repo'); + * await signInWithRedirect(auth, provider); + * // This will trigger a full page redirect away from your app + * + * // After returning from the redirect when your app initializes you can obtain the result + * const result = await getRedirectResult(auth); + * if (result) { + * // This is the signed-in user + * const user = result.user; + * // This gives you a GitHub Access Token. + * const credential = GithubAuthProvider.credentialFromResult(result); + * const token = credential.accessToken; + * } + * ``` + * + * @example + * ```javascript + * // Sign in using a popup. + * const provider = new GithubAuthProvider(); + * provider.addScope('repo'); + * const result = await signInWithPopup(auth, provider); + * + * // The signed-in user info. + * const user = result.user; + * // This gives you a GitHub Access Token. + * const credential = GithubAuthProvider.credentialFromResult(result); + * const token = credential.accessToken; + * ``` + * @public + */ +export declare class GithubAuthProvider extends BaseOAuthProvider { + /** Always set to {@link SignInMethod}.GITHUB. */ + static readonly GITHUB_SIGN_IN_METHOD: 'github.com'; + /** Always set to {@link ProviderId}.GITHUB. */ + static readonly PROVIDER_ID: 'github.com'; + constructor(); + /** + * Creates a credential for GitHub. + * + * @param accessToken - GitHub access token. + */ + static credential(accessToken: string): OAuthCredential; + /** + * Used to extract the underlying {@link OAuthCredential} from a {@link UserCredential}. + * + * @param userCredential - The user credential. + */ + static credentialFromResult(userCredential: UserCredential): OAuthCredential | null; + /** + * Used to extract the underlying {@link OAuthCredential} from a {@link AuthError} which was + * thrown during a sign-in, link, or reauthenticate operation. + * + * @param userCredential - The user credential. + */ + static credentialFromError(error: FirebaseError): OAuthCredential | null; + private static credentialFromTaggedObject; +} + +/** + * Provider for generating an {@link OAuthCredential} for {@link ProviderId}.GOOGLE. + * + * @example + * ```javascript + * // Sign in using a redirect. + * const provider = new GoogleAuthProvider(); + * // Start a sign in process for an unauthenticated user. + * provider.addScope('profile'); + * provider.addScope('email'); + * await signInWithRedirect(auth, provider); + * // This will trigger a full page redirect away from your app + * + * // After returning from the redirect when your app initializes you can obtain the result + * const result = await getRedirectResult(auth); + * if (result) { + * // This is the signed-in user + * const user = result.user; + * // This gives you a Google Access Token. + * const credential = GoogleAuthProvider.credentialFromResult(result); + * const token = credential.accessToken; + * } + * ``` + * + * @example + * ```javascript + * // Sign in using a popup. + * const provider = new GoogleAuthProvider(); + * provider.addScope('profile'); + * provider.addScope('email'); + * const result = await signInWithPopup(auth, provider); + * + * // The signed-in user info. + * const user = result.user; + * // This gives you a Google Access Token. + * const credential = GoogleAuthProvider.credentialFromResult(result); + * const token = credential.accessToken; + * ``` + * + * @public + */ +export declare class GoogleAuthProvider extends BaseOAuthProvider { + /** Always set to {@link SignInMethod}.GOOGLE. */ + static readonly GOOGLE_SIGN_IN_METHOD: 'google.com'; + /** Always set to {@link ProviderId}.GOOGLE. */ + static readonly PROVIDER_ID: 'google.com'; + constructor(); + /** + * Creates a credential for Google. At least one of ID token and access token is required. + * + * @example + * ```javascript + * // \`googleUser\` from the onsuccess Google Sign In callback. + * const credential = GoogleAuthProvider.credential(googleUser.getAuthResponse().id_token); + * const result = await signInWithCredential(credential); + * ``` + * + * @param idToken - Google ID token. + * @param accessToken - Google access token. + */ + static credential(idToken?: string | null, accessToken?: string | null): OAuthCredential; + /** + * Used to extract the underlying {@link OAuthCredential} from a {@link UserCredential}. + * + * @param userCredential - The user credential. + */ + static credentialFromResult(userCredential: UserCredential): OAuthCredential | null; + /** + * Used to extract the underlying {@link OAuthCredential} from a {@link AuthError} which was + * thrown during a sign-in, link, or reauthenticate operation. + * + * @param userCredential - The user credential. + */ + static credentialFromError(error: FirebaseError): OAuthCredential | null; + private static credentialFromTaggedObject; +} + +/** + * Raw encoded JWT + * + */ +declare type IdToken = string; + +/* Excluded from this release type: IdTokenMfaResponse */ + +/* Excluded from this release type: IdTokenResponse */ + +/* Excluded from this release type: IdTokenResponseKind */ + +/** + * Interface representing ID token result obtained from {@link User.getIdTokenResult}. + * + * @remarks + * `IdTokenResult` contains the ID token JWT string and other helper properties for getting different data + * associated with the token as well as all the decoded payload claims. + * + * Note that these claims are not to be trusted as they are parsed client side. Only server side + * verification can guarantee the integrity of the token claims. + * + * @public + */ +export declare interface IdTokenResult { + /** + * The authentication time formatted as a UTC string. + * + * @remarks + * This is the time the user authenticated (signed in) and not the time the token was refreshed. + */ + authTime: string; + /** The ID token expiration time formatted as a UTC string. */ + expirationTime: string; + /** The ID token issuance time formatted as a UTC string. */ + issuedAtTime: string; + /** + * The sign-in provider through which the ID token was obtained (anonymous, custom, phone, + * password, etc). + * + * @remarks + * Note, this does not map to provider IDs. + */ + signInProvider: string | null; + /** + * The type of second factor associated with this session, provided the user was multi-factor + * authenticated (eg. phone, etc). + */ + signInSecondFactor: string | null; + /** The Firebase Auth ID token JWT string. */ + token: string; + /** + * The entire payload claims of the ID token including the standard reserved claims as well as + * the custom claims. + */ + claims: ParsedToken; +} + +/** + * An implementation of {@link Persistence} of type `LOCAL` using `indexedDB` + * for the underlying storage. + * + * @public + */ +export declare const indexedDBLocalPersistence: Persistence; + +/** + * Initializes an {@link Auth} instance with fine-grained control over + * {@link Dependencies}. + * + * @remarks + * + * This function allows more control over the {@link Auth} instance than + * {@link getAuth}. `getAuth` uses platform-specific defaults to supply + * the {@link Dependencies}. In general, `getAuth` is the easiest way to + * initialize Auth and works for most use cases. Use `initializeAuth` if you + * need control over which persistence layer is used, or to minimize bundle + * size if you're not using either `signInWithPopup` or `signInWithRedirect`. + * + * For example, if your app only uses anonymous accounts and you only want + * accounts saved for the current session, initialize `Auth` with: + * + * ```js + * const auth = initializeAuth(app, { + * persistence: browserSessionPersistence, + * popupRedirectResolver: undefined, + * }); + * ``` + * + * @public + */ +export declare function initializeAuth(app: FirebaseApp, deps?: Dependencies): Auth; + +/** + * Loads the reCAPTCHA configuration into the `Auth` instance. + * + * @remarks + * This will load the reCAPTCHA config, which indicates whether the reCAPTCHA + * verification flow should be triggered for each auth provider, into the + * current Auth session. + * + * If initializeRecaptchaConfig() is not invoked, the auth flow will always start + * without reCAPTCHA verification. If the provider is configured to require reCAPTCHA + * verification, the SDK will transparently load the reCAPTCHA config and restart the + * auth flows. + * + * Thus, by calling this optional method, you will reduce the latency of future auth flows. + * Loading the reCAPTCHA config early will also enhance the signal collected by reCAPTCHA. + * + * This method does not work in a Node.js environment. + * + * @example + * ```javascript + * initializeRecaptchaConfig(auth); + * ``` + * + * @param auth - The {@link Auth} instance. + * + * @public + */ +export declare function initializeRecaptchaConfig(auth: Auth): Promise; + +/** + * An implementation of {@link Persistence} of type 'NONE'. + * + * @public + */ +export declare const inMemoryPersistence: Persistence; + +/** + * Checks if an incoming link is a sign-in with email link suitable for {@link signInWithEmailLink}. + * + * @param auth - The {@link Auth} instance. + * @param emailLink - The link sent to the user's email address. + * + * @public + */ +export declare function isSignInWithEmailLink(auth: Auth, emailLink: string): boolean; + +/** + * Links the user account with the given credentials. + * + * @remarks + * An {@link AuthProvider} can be used to generate the credential. + * + * @param user - The user. + * @param credential - The auth credential. + * + * @public + */ +export declare function linkWithCredential(user: User, credential: AuthCredential): Promise; + +/** + * Links the user account with the given phone number. + * + * @remarks + * This method does not work in a Node.js environment. + * + * @param user - The user. + * @param phoneNumber - The user's phone number in E.164 format (e.g. +16505550101). + * @param appVerifier - The {@link ApplicationVerifier}. + * + * @public + */ +export declare function linkWithPhoneNumber(user: User, phoneNumber: string, appVerifier?: ApplicationVerifier): Promise; + +/** + * Links the authenticated provider to the user account using a pop-up based OAuth flow. + * + * @remarks + * If the linking is successful, the returned result will contain the user and the provider's credential. + * + * This method does not work in a Node.js environment. + * + * @example + * ```javascript + * // Sign in using some other provider. + * const result = await signInWithEmailAndPassword(auth, email, password); + * // Link using a popup. + * const provider = new FacebookAuthProvider(); + * await linkWithPopup(result.user, provider); + * ``` + * + * @param user - The user. + * @param provider - The provider to authenticate. The provider has to be an {@link OAuthProvider}. + * Non-OAuth providers like {@link EmailAuthProvider} will throw an error. + * @param resolver - An instance of {@link PopupRedirectResolver}, optional + * if already supplied to {@link initializeAuth} or provided by {@link getAuth}. + * + * @public + */ +export declare function linkWithPopup(user: User, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; + +/** + * Links the {@link OAuthProvider} to the user account using a full-page redirect flow. + * @remarks + * To handle the results and errors for this operation, refer to {@link getRedirectResult}. + * Follow the {@link https://firebase.google.com/docs/auth/web/redirect-best-practices + * | best practices} when using {@link linkWithRedirect}. + * + * This method does not work in a Node.js environment or with {@link Auth} instances + * created with a {@link @firebase/app#FirebaseServerApp}. + * + * @example + * ```javascript + * // Sign in using some other provider. + * const result = await signInWithEmailAndPassword(auth, email, password); + * // Link using a redirect. + * const provider = new FacebookAuthProvider(); + * await linkWithRedirect(result.user, provider); + * // This will trigger a full page redirect away from your app + * + * // After returning from the redirect when your app initializes you can obtain the result + * const result = await getRedirectResult(auth); + * ``` + * + * @param user - The user. + * @param provider - The provider to authenticate. The provider has to be an {@link OAuthProvider}. + * Non-OAuth providers like {@link EmailAuthProvider} will throw an error. + * @param resolver - An instance of {@link PopupRedirectResolver}, optional + * if already supplied to {@link initializeAuth} or provided by {@link getAuth}. + * + * @public + */ +export declare function linkWithRedirect(user: User, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; + +/** + * MfaEnrollment can be any subtype of BaseMfaEnrollment, currently only PhoneMfaEnrollment and TotpMfaEnrollment are supported. + */ +declare type MfaEnrollment = PhoneMfaEnrollment | TotpMfaEnrollment; + +/** + * The {@link MultiFactorUser} corresponding to the user. + * + * @remarks + * This is used to access all multi-factor properties and operations related to the user. + * + * @param user - The user. + * + * @public + */ +export declare function multiFactor(user: User): MultiFactorUser; + +/** + * The base class for asserting ownership of a second factor. + * + * @remarks + * This is used to facilitate enrollment of a second factor on an existing user or sign-in of a + * user who already verified the first factor. + * + * @public + */ +export declare interface MultiFactorAssertion { + /** The identifier of the second factor. */ + readonly factorId: (typeof FactorId)[keyof typeof FactorId]; +} + +/** + * The error thrown when the user needs to provide a second factor to sign in successfully. + * + * @remarks + * The error code for this error is `auth/multi-factor-auth-required`. + * + * @example + * ```javascript + * let resolver; + * let multiFactorHints; + * + * signInWithEmailAndPassword(auth, email, password) + * .then((result) => { + * // User signed in. No 2nd factor challenge is needed. + * }) + * .catch((error) => { + * if (error.code == 'auth/multi-factor-auth-required') { + * resolver = getMultiFactorResolver(auth, error); + * multiFactorHints = resolver.hints; + * } else { + * // Handle other errors. + * } + * }); + * + * // Obtain a multiFactorAssertion by verifying the second factor. + * + * const userCredential = await resolver.resolveSignIn(multiFactorAssertion); + * ``` + * + * @public + */ +export declare interface MultiFactorError extends AuthError { + /** Details about the MultiFactorError. */ + readonly customData: AuthError['customData'] & { + /** + * The type of operation (sign-in, linking, or re-authentication) that raised the error. + */ + readonly operationType: (typeof OperationType)[keyof typeof OperationType]; + }; +} + +/** + * A structure containing the information of a second factor entity. + * + * @public + */ +export declare interface MultiFactorInfo { + /** The multi-factor enrollment ID. */ + readonly uid: string; + /** The user friendly name of the current second factor. */ + readonly displayName?: string | null; + /** The enrollment date of the second factor formatted as a UTC string. */ + readonly enrollmentTime: string; + /** The identifier of the second factor. */ + readonly factorId: (typeof FactorId)[keyof typeof FactorId]; +} + +/** + * The class used to facilitate recovery from {@link MultiFactorError} when a user needs to + * provide a second factor to sign in. + * + * @example + * ```javascript + * let resolver; + * let multiFactorHints; + * + * signInWithEmailAndPassword(auth, email, password) + * .then((result) => { + * // User signed in. No 2nd factor challenge is needed. + * }) + * .catch((error) => { + * if (error.code == 'auth/multi-factor-auth-required') { + * resolver = getMultiFactorResolver(auth, error); + * // Show UI to let user select second factor. + * multiFactorHints = resolver.hints; + * } else { + * // Handle other errors. + * } + * }); + * + * // The enrolled second factors that can be used to complete + * // sign-in are returned in the `MultiFactorResolver.hints` list. + * // UI needs to be presented to allow the user to select a second factor + * // from that list. + * + * const selectedHint = // ; selected from multiFactorHints + * const phoneAuthProvider = new PhoneAuthProvider(auth); + * const phoneInfoOptions = { + * multiFactorHint: selectedHint, + * session: resolver.session + * }; + * const verificationId = phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, appVerifier); + * // Store `verificationId` and show UI to let user enter verification code. + * + * // UI to enter verification code and continue. + * // Continue button click handler + * const phoneAuthCredential = PhoneAuthProvider.credential(verificationId, verificationCode); + * const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(phoneAuthCredential); + * const userCredential = await resolver.resolveSignIn(multiFactorAssertion); + * ``` + * + * @public + */ +export declare interface MultiFactorResolver { + /** + * The list of hints for the second factors needed to complete the sign-in for the current + * session. + */ + readonly hints: MultiFactorInfo[]; + /** + * The session identifier for the current sign-in flow, which can be used to complete the second + * factor sign-in. + */ + readonly session: MultiFactorSession; + /** + * A helper function to help users complete sign in with a second factor using an + * {@link MultiFactorAssertion} confirming the user successfully completed the second factor + * challenge. + * + * @example + * ```javascript + * const phoneAuthCredential = PhoneAuthProvider.credential(verificationId, verificationCode); + * const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(phoneAuthCredential); + * const userCredential = await resolver.resolveSignIn(multiFactorAssertion); + * ``` + * + * @param assertion - The multi-factor assertion to resolve sign-in with. + * @returns The promise that resolves with the user credential object. + */ + resolveSignIn(assertion: MultiFactorAssertion): Promise; +} + +/** + * An interface defining the multi-factor session object used for enrolling a second factor on a + * user or helping sign in an enrolled user with a second factor. + * + * @public + */ +export declare interface MultiFactorSession { +} + +/** + * An interface that defines the multi-factor related properties and operations pertaining + * to a {@link User}. + * + * @public + */ +export declare interface MultiFactorUser { + /** Returns a list of the user's enrolled second factors. */ + readonly enrolledFactors: MultiFactorInfo[]; + /** + * Returns the session identifier for a second factor enrollment operation. This is used to + * identify the user trying to enroll a second factor. + * + * @example + * ```javascript + * const multiFactorUser = multiFactor(auth.currentUser); + * const multiFactorSession = await multiFactorUser.getSession(); + * + * // Send verification code. + * const phoneAuthProvider = new PhoneAuthProvider(auth); + * const phoneInfoOptions = { + * phoneNumber: phoneNumber, + * session: multiFactorSession + * }; + * const verificationId = await phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, appVerifier); + * + * // Obtain verification code from user. + * const phoneAuthCredential = PhoneAuthProvider.credential(verificationId, verificationCode); + * const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(phoneAuthCredential); + * await multiFactorUser.enroll(multiFactorAssertion); + * ``` + * + * @returns The promise that resolves with the {@link MultiFactorSession}. + */ + getSession(): Promise; + /** + * + * Enrolls a second factor as identified by the {@link MultiFactorAssertion} for the + * user. + * + * @remarks + * On resolution, the user tokens are updated to reflect the change in the JWT payload. + * Accepts an additional display name parameter used to identify the second factor to the end + * user. Recent re-authentication is required for this operation to succeed. On successful + * enrollment, existing Firebase sessions (refresh tokens) are revoked. When a new factor is + * enrolled, an email notification is sent to the user’s email. + * + * @example + * ```javascript + * const multiFactorUser = multiFactor(auth.currentUser); + * const multiFactorSession = await multiFactorUser.getSession(); + * + * // Send verification code. + * const phoneAuthProvider = new PhoneAuthProvider(auth); + * const phoneInfoOptions = { + * phoneNumber: phoneNumber, + * session: multiFactorSession + * }; + * const verificationId = await phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, appVerifier); + * + * // Obtain verification code from user. + * const phoneAuthCredential = PhoneAuthProvider.credential(verificationId, verificationCode); + * const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(phoneAuthCredential); + * await multiFactorUser.enroll(multiFactorAssertion); + * // Second factor enrolled. + * ``` + * + * @param assertion - The multi-factor assertion to enroll with. + * @param displayName - The display name of the second factor. + */ + enroll(assertion: MultiFactorAssertion, displayName?: string | null): Promise; + /** + * Unenrolls the specified second factor. + * + * @remarks + * To specify the factor to remove, pass a {@link MultiFactorInfo} object (retrieved from + * {@link MultiFactorUser.enrolledFactors}) or the + * factor's UID string. Sessions are not revoked when the account is unenrolled. An email + * notification is likely to be sent to the user notifying them of the change. Recent + * re-authentication is required for this operation to succeed. When an existing factor is + * unenrolled, an email notification is sent to the user’s email. + * + * @example + * ```javascript + * const multiFactorUser = multiFactor(auth.currentUser); + * // Present user the option to choose which factor to unenroll. + * await multiFactorUser.unenroll(multiFactorUser.enrolledFactors[i]) + * ``` + * + * @param option - The multi-factor option to unenroll. + * @returns - A `Promise` which resolves when the unenroll operation is complete. + */ + unenroll(option: MultiFactorInfo | string): Promise; +} + +declare type MutableUserInfo = { + -readonly [K in keyof UserInfo]: UserInfo[K]; +}; +export { NextFn } + +/** + * Type definition for an event callback. + * + * @privateRemarks TODO(avolkovi): should we consolidate with Subscribe since we're changing the API anyway? + * + * @public + */ +export declare type NextOrObserver = NextFn | Observer; + +/** + * Represents the OAuth credentials returned by an {@link OAuthProvider}. + * + * @remarks + * Implementations specify the details about each auth provider's credential requirements. + * + * @public + */ +export declare class OAuthCredential extends AuthCredential { + /** + * The OAuth ID token associated with the credential if it belongs to an OIDC provider, + * such as `google.com`. + * @readonly + */ + idToken?: string; + /** + * The OAuth access token associated with the credential if it belongs to an + * {@link OAuthProvider}, such as `facebook.com`, `twitter.com`, etc. + * @readonly + */ + accessToken?: string; + /** + * The OAuth access token secret associated with the credential if it belongs to an OAuth 1.0 + * provider, such as `twitter.com`. + * @readonly + */ + secret?: string; + private nonce?; + private pendingToken; + /* Excluded from this release type: _fromParams */ + /** {@inheritdoc AuthCredential.toJSON} */ + toJSON(): object; + /** + * Static method to deserialize a JSON representation of an object into an + * {@link AuthCredential}. + * + * @param json - Input can be either Object or the stringified representation of the object. + * When string is provided, JSON.parse would be called first. + * + * @returns If the JSON input does not represent an {@link AuthCredential}, null is returned. + */ + static fromJSON(json: string | object): OAuthCredential | null; + /* Excluded from this release type: _getIdTokenResponse */ + /* Excluded from this release type: _linkToIdToken */ + /* Excluded from this release type: _getReauthenticationResolver */ + private buildRequest; +} + +/** + * Defines the options for initializing an {@link OAuthCredential}. + * + * @remarks + * For ID tokens with nonce claim, the raw nonce has to also be provided. + * + * @public + */ +export declare interface OAuthCredentialOptions { + /** + * The OAuth ID token used to initialize the {@link OAuthCredential}. + */ + idToken?: string; + /** + * The OAuth access token used to initialize the {@link OAuthCredential}. + */ + accessToken?: string; + /** + * The raw nonce associated with the ID token. + * + * @remarks + * It is required when an ID token with a nonce field is provided. The SHA-256 hash of the + * raw nonce must match the nonce field in the ID token. + */ + rawNonce?: string; +} + +declare interface OAuthCredentialParams { + idToken?: string | null; + accessToken?: string | null; + oauthToken?: string; + secret?: string; + oauthTokenSecret?: string; + nonce?: string; + pendingToken?: string; + providerId: string; + signInMethod: string; +} + +/** + * Provider for generating generic {@link OAuthCredential}. + * + * @example + * ```javascript + * // Sign in using a redirect. + * const provider = new OAuthProvider('google.com'); + * // Start a sign in process for an unauthenticated user. + * provider.addScope('profile'); + * provider.addScope('email'); + * await signInWithRedirect(auth, provider); + * // This will trigger a full page redirect away from your app + * + * // After returning from the redirect when your app initializes you can obtain the result + * const result = await getRedirectResult(auth); + * if (result) { + * // This is the signed-in user + * const user = result.user; + * // This gives you a OAuth Access Token for the provider. + * const credential = provider.credentialFromResult(auth, result); + * const token = credential.accessToken; + * } + * ``` + * + * @example + * ```javascript + * // Sign in using a popup. + * const provider = new OAuthProvider('google.com'); + * provider.addScope('profile'); + * provider.addScope('email'); + * const result = await signInWithPopup(auth, provider); + * + * // The signed-in user info. + * const user = result.user; + * // This gives you a OAuth Access Token for the provider. + * const credential = provider.credentialFromResult(auth, result); + * const token = credential.accessToken; + * ``` + * @public + */ +export declare class OAuthProvider extends BaseOAuthProvider { + /** + * Creates an {@link OAuthCredential} from a JSON string or a plain object. + * @param json - A plain object or a JSON string + */ + static credentialFromJSON(json: object | string): OAuthCredential; + /** + * Creates a {@link OAuthCredential} from a generic OAuth provider's access token or ID token. + * + * @remarks + * The raw nonce is required when an ID token with a nonce field is provided. The SHA-256 hash of + * the raw nonce must match the nonce field in the ID token. + * + * @example + * ```javascript + * // `googleUser` from the onsuccess Google Sign In callback. + * // Initialize a generate OAuth provider with a `google.com` providerId. + * const provider = new OAuthProvider('google.com'); + * const credential = provider.credential({ + * idToken: googleUser.getAuthResponse().id_token, + * }); + * const result = await signInWithCredential(credential); + * ``` + * + * @param params - Either the options object containing the ID token, access token and raw nonce + * or the ID token string. + */ + credential(params: OAuthCredentialOptions): OAuthCredential; + /** An internal credential method that accepts more permissive options */ + private _credential; + /** + * Used to extract the underlying {@link OAuthCredential} from a {@link UserCredential}. + * + * @param userCredential - The user credential. + */ + static credentialFromResult(userCredential: UserCredential): OAuthCredential | null; + /** + * Used to extract the underlying {@link OAuthCredential} from a {@link AuthError} which was + * thrown during a sign-in, link, or reauthenticate operation. + * + * @param userCredential - The user credential. + */ + static credentialFromError(error: FirebaseError): OAuthCredential | null; + private static oauthCredentialFromTaggedObject; +} + +/** + * Adds an observer for changes to the user's sign-in state. + * + * @remarks + * To keep the old behavior, see {@link onIdTokenChanged}. + * + * @param auth - The {@link Auth} instance. + * @param nextOrObserver - callback triggered on change. + * @param error - Deprecated. This callback is never triggered. Errors + * on signing in/out can be caught in promises returned from + * sign-in/sign-out functions. + * @param completed - Deprecated. This callback is never triggered. + * + * @public + */ +export declare function onAuthStateChanged(auth: Auth, nextOrObserver: NextOrObserver, error?: ErrorFn, completed?: CompleteFn): Unsubscribe; + +/** + * Adds an observer for changes to the signed-in user's ID token. + * + * @remarks + * This includes sign-in, sign-out, and token refresh events. + * This will not be triggered automatically upon ID token expiration. Use {@link User.getIdToken} to refresh the ID token. + * + * @param auth - The {@link Auth} instance. + * @param nextOrObserver - callback triggered on change. + * @param error - Deprecated. This callback is never triggered. Errors + * on signing in/out can be caught in promises returned from + * sign-in/sign-out functions. + * @param completed - Deprecated. This callback is never triggered. + * + * @public + */ +export declare function onIdTokenChanged(auth: Auth, nextOrObserver: NextOrObserver, error?: ErrorFn, completed?: CompleteFn): Unsubscribe; + +/** + * Enumeration of supported operation types. + * + * @public + */ +export declare const OperationType: { + /** Operation involving linking an additional provider to an already signed-in user. */ + readonly LINK: "link"; + /** Operation involving using a provider to reauthenticate an already signed-in user. */ + readonly REAUTHENTICATE: "reauthenticate"; + /** Operation involving signing in a user. */ + readonly SIGN_IN: "signIn"; +}; + +/** + * Parses the email action link string and returns an {@link ActionCodeURL} if + * the link is valid, otherwise returns null. + * + * @public + */ +export declare function parseActionCodeURL(link: string): ActionCodeURL | null; + +/** + * Interface representing a parsed ID token. + * + * @privateRemarks TODO(avolkovi): consolidate with parsed_token in implementation. + * + * @public + */ +export declare interface ParsedToken { + /** Expiration time of the token. */ + 'exp'?: string; + /** UID of the user. */ + 'sub'?: string; + /** Time at which authentication was performed. */ + 'auth_time'?: string; + /** Issuance time of the token. */ + 'iat'?: string; + /** Firebase specific claims, containing the provider(s) used to authenticate the user. */ + 'firebase'?: { + 'sign_in_provider'?: string; + 'sign_in_second_factor'?: string; + 'identities'?: Record; + }; + /** Map of any additional custom claims. */ + [key: string]: unknown; +} + +/** + * A structure specifying password policy requirements. + * + * @public + */ +export declare interface PasswordPolicy { + /** + * Requirements enforced by this password policy. + */ + readonly customStrengthOptions: { + /** + * Minimum password length, or undefined if not configured. + */ + readonly minPasswordLength?: number; + /** + * Maximum password length, or undefined if not configured. + */ + readonly maxPasswordLength?: number; + /** + * Whether the password should contain a lowercase letter, or undefined if not configured. + */ + readonly containsLowercaseLetter?: boolean; + /** + * Whether the password should contain an uppercase letter, or undefined if not configured. + */ + readonly containsUppercaseLetter?: boolean; + /** + * Whether the password should contain a numeric character, or undefined if not configured. + */ + readonly containsNumericCharacter?: boolean; + /** + * Whether the password should contain a non-alphanumeric character, or undefined if not configured. + */ + readonly containsNonAlphanumericCharacter?: boolean; + }; + /** + * List of characters that are considered non-alphanumeric during validation. + */ + readonly allowedNonAlphanumericCharacters: string; + /** + * The enforcement state of the policy. Can be 'OFF' or 'ENFORCE'. + */ + readonly enforcementState: string; + /** + * Whether existing passwords must meet the policy. + */ + readonly forceUpgradeOnSignin: boolean; +} + +/* Excluded from this release type: PasswordPolicyCustomStrengthOptions */ + +/* Excluded from this release type: PasswordPolicyInternal */ + +/** + * A structure indicating which password policy requirements were met or violated and what the + * requirements are. + * + * @public + */ +export declare interface PasswordValidationStatus { + /** + * Whether the password meets all requirements. + */ + readonly isValid: boolean; + /** + * Whether the password meets the minimum password length, or undefined if not required. + */ + readonly meetsMinPasswordLength?: boolean; + /** + * Whether the password meets the maximum password length, or undefined if not required. + */ + readonly meetsMaxPasswordLength?: boolean; + /** + * Whether the password contains a lowercase letter, or undefined if not required. + */ + readonly containsLowercaseLetter?: boolean; + /** + * Whether the password contains an uppercase letter, or undefined if not required. + */ + readonly containsUppercaseLetter?: boolean; + /** + * Whether the password contains a numeric character, or undefined if not required. + */ + readonly containsNumericCharacter?: boolean; + /** + * Whether the password contains a non-alphanumeric character, or undefined if not required. + */ + readonly containsNonAlphanumericCharacter?: boolean; + /** + * The policy used to validate the password. + */ + readonly passwordPolicy: PasswordPolicy; +} + +declare type PersistedBlob = Record; + +/** + * An interface covering the possible persistence mechanism types. + * + * @public + */ +export declare interface Persistence { + /** + * Type of Persistence. + * - 'SESSION' is used for temporary persistence such as `sessionStorage`. + * - 'LOCAL' is used for long term persistence such as `localStorage` or `IndexedDB`. + * - 'NONE' is used for in-memory, or no persistence. + * - 'COOKIE' is used for cookie persistence, useful for server-side rendering. + */ + readonly type: 'SESSION' | 'LOCAL' | 'NONE' | 'COOKIE'; +} + +declare interface PersistenceInternal extends Persistence { + type: PersistenceType; + _isAvailable(): Promise; + _set(key: string, value: PersistenceValue): Promise; + _get(key: string): Promise; + _remove(key: string): Promise; + _addListener(key: string, listener: StorageEventListener): void; + _removeListener(key: string, listener: StorageEventListener): void; + _shouldAllowMigration?: boolean; +} + +declare const enum PersistenceType { + SESSION = "SESSION", + LOCAL = "LOCAL", + NONE = "NONE", + COOKIE = "COOKIE" +} + +declare type PersistenceValue = PersistedBlob | string; + +/** + * Represents the credentials returned by {@link PhoneAuthProvider}. + * + * @public + */ +export declare class PhoneAuthCredential extends AuthCredential { + private readonly params; + private constructor(); + /* Excluded from this release type: _fromVerification */ + /* Excluded from this release type: _fromTokenResponse */ + /* Excluded from this release type: _getIdTokenResponse */ + /* Excluded from this release type: _linkToIdToken */ + /* Excluded from this release type: _getReauthenticationResolver */ + /* Excluded from this release type: _makeVerificationRequest */ + /** {@inheritdoc AuthCredential.toJSON} */ + toJSON(): object; + /** Generates a phone credential based on a plain object or a JSON string. */ + static fromJSON(json: object | string): PhoneAuthCredential | null; +} + +/** + * Provider for generating an {@link PhoneAuthCredential}. + * + * @remarks + * `PhoneAuthProvider` does not work in a Node.js environment. + * + * @example + * ```javascript + * // 'recaptcha-container' is the ID of an element in the DOM. + * const applicationVerifier = new RecaptchaVerifier('recaptcha-container'); + * const provider = new PhoneAuthProvider(auth); + * const verificationId = await provider.verifyPhoneNumber('+16505550101', applicationVerifier); + * // Obtain the verificationCode from the user. + * const phoneCredential = PhoneAuthProvider.credential(verificationId, verificationCode); + * const userCredential = await signInWithCredential(auth, phoneCredential); + * ``` + * + * @public + */ +export declare class PhoneAuthProvider { + /** Always set to {@link ProviderId}.PHONE. */ + static readonly PROVIDER_ID: 'phone'; + /** Always set to {@link SignInMethod}.PHONE. */ + static readonly PHONE_SIGN_IN_METHOD: 'phone'; + /** Always set to {@link ProviderId}.PHONE. */ + readonly providerId: "phone"; + private readonly auth; + /** + * @param auth - The Firebase {@link Auth} instance in which sign-ins should occur. + * + */ + constructor(auth: Auth); + /** + * + * Starts a phone number authentication flow by sending a verification code to the given phone + * number. + * + * @example + * ```javascript + * const provider = new PhoneAuthProvider(auth); + * const verificationId = await provider.verifyPhoneNumber(phoneNumber, applicationVerifier); + * // Obtain verificationCode from the user. + * const authCredential = PhoneAuthProvider.credential(verificationId, verificationCode); + * const userCredential = await signInWithCredential(auth, authCredential); + * ``` + * + * @example + * An alternative flow is provided using the `signInWithPhoneNumber` method. + * ```javascript + * const confirmationResult = signInWithPhoneNumber(auth, phoneNumber, applicationVerifier); + * // Obtain verificationCode from the user. + * const userCredential = confirmationResult.confirm(verificationCode); + * ``` + * + * @param phoneInfoOptions - The user's {@link PhoneInfoOptions}. The phone number should be in + * E.164 format (e.g. +16505550101). + * @param applicationVerifier - An {@link ApplicationVerifier}, which prevents + * requests from unauthorized clients. This SDK includes an implementation + * based on reCAPTCHA v2, {@link RecaptchaVerifier}. If you've enabled + * reCAPTCHA Enterprise bot protection in Enforce mode, this parameter is + * optional; in all other configurations, the parameter is required. + * + * @returns A Promise for a verification ID that can be passed to + * {@link PhoneAuthProvider.credential} to identify this flow. + */ + verifyPhoneNumber(phoneOptions: PhoneInfoOptions | string, applicationVerifier?: ApplicationVerifier): Promise; + /** + * Creates a phone auth credential, given the verification ID from + * {@link PhoneAuthProvider.verifyPhoneNumber} and the code that was sent to the user's + * mobile device. + * + * @example + * ```javascript + * const provider = new PhoneAuthProvider(auth); + * const verificationId = provider.verifyPhoneNumber(phoneNumber, applicationVerifier); + * // Obtain verificationCode from the user. + * const authCredential = PhoneAuthProvider.credential(verificationId, verificationCode); + * const userCredential = signInWithCredential(auth, authCredential); + * ``` + * + * @example + * An alternative flow is provided using the `signInWithPhoneNumber` method. + * ```javascript + * const confirmationResult = await signInWithPhoneNumber(auth, phoneNumber, applicationVerifier); + * // Obtain verificationCode from the user. + * const userCredential = await confirmationResult.confirm(verificationCode); + * ``` + * + * @param verificationId - The verification ID returned from {@link PhoneAuthProvider.verifyPhoneNumber}. + * @param verificationCode - The verification code sent to the user's mobile device. + * + * @returns The auth provider credential. + */ + static credential(verificationId: string, verificationCode: string): PhoneAuthCredential; + /** + * Generates an {@link AuthCredential} from a {@link UserCredential}. + * @param userCredential - The user credential. + */ + static credentialFromResult(userCredential: UserCredential): AuthCredential | null; + /** + * Returns an {@link AuthCredential} when passed an error. + * + * @remarks + * + * This method works for errors like + * `auth/account-exists-with-different-credentials`. This is useful for + * recovering when attempting to set a user's phone number but the number + * in question is already tied to another account. For example, the following + * code tries to update the current user's phone number, and if that + * fails, links the user with the account associated with that number: + * + * ```js + * const provider = new PhoneAuthProvider(auth); + * const verificationId = await provider.verifyPhoneNumber(number, verifier); + * try { + * const code = ''; // Prompt the user for the verification code + * await updatePhoneNumber( + * auth.currentUser, + * PhoneAuthProvider.credential(verificationId, code)); + * } catch (e) { + * if ((e as FirebaseError)?.code === 'auth/account-exists-with-different-credential') { + * const cred = PhoneAuthProvider.credentialFromError(e); + * await linkWithCredential(auth.currentUser, cred); + * } + * } + * + * // At this point, auth.currentUser.phoneNumber === number. + * ``` + * + * @param error - The error to generate a credential from. + */ + static credentialFromError(error: FirebaseError): AuthCredential | null; + private static credentialFromTaggedObject; +} + +/** + * The information required to verify the ownership of a phone number. + * + * @remarks + * The information that's required depends on whether you are doing single-factor sign-in, + * multi-factor enrollment or multi-factor sign-in. + * + * @public + */ +export declare type PhoneInfoOptions = PhoneSingleFactorInfoOptions | PhoneMultiFactorEnrollInfoOptions | PhoneMultiFactorSignInInfoOptions; + +/** + * An MFA provided by SMS verification. + */ +declare interface PhoneMfaEnrollment extends BaseMfaEnrollment { + phoneInfo: string; +} + +/** + * The class for asserting ownership of a phone second factor. Provided by + * {@link PhoneMultiFactorGenerator.assertion}. + * + * @public + */ +export declare interface PhoneMultiFactorAssertion extends MultiFactorAssertion { +} + +/** + * Options used for enrolling a second factor. + * + * @public + */ +export declare interface PhoneMultiFactorEnrollInfoOptions { + /** Phone number to send a verification code to. */ + phoneNumber: string; + /** The {@link MultiFactorSession} obtained via {@link MultiFactorUser.getSession}. */ + session: MultiFactorSession; +} + +/** + * Provider for generating a {@link PhoneMultiFactorAssertion}. + * + * @public + */ +export declare class PhoneMultiFactorGenerator { + private constructor(); + /** + * Provides a {@link PhoneMultiFactorAssertion} to confirm ownership of the phone second factor. + * + * @remarks + * This method does not work in a Node.js environment. + * + * @param phoneAuthCredential - A credential provided by {@link PhoneAuthProvider.credential}. + * @returns A {@link PhoneMultiFactorAssertion} which can be used with + * {@link MultiFactorResolver.resolveSignIn} + */ + static assertion(credential: PhoneAuthCredential): PhoneMultiFactorAssertion; + /** + * The identifier of the phone second factor: `phone`. + */ + static FACTOR_ID: string; +} + +/** + * The subclass of the {@link MultiFactorInfo} interface for phone number + * second factors. The `factorId` of this second factor is {@link FactorId}.PHONE. + * @public + */ +export declare interface PhoneMultiFactorInfo extends MultiFactorInfo { + /** The phone number associated with the current second factor. */ + readonly phoneNumber: string; +} + +/** + * Options used for signing in with a second factor. + * + * @public + */ +export declare interface PhoneMultiFactorSignInInfoOptions { + /** + * The {@link MultiFactorInfo} obtained via {@link MultiFactorResolver.hints}. + * + * One of `multiFactorHint` or `multiFactorUid` is required. + */ + multiFactorHint?: MultiFactorInfo; + /** + * The uid of the second factor. + * + * One of `multiFactorHint` or `multiFactorUid` is required. + */ + multiFactorUid?: string; + /** The {@link MultiFactorSession} obtained via {@link MultiFactorResolver.session}. */ + session: MultiFactorSession; +} + +/* Excluded from this release type: PhoneOrOauthTokenResponse */ + +/** + * Options used for single-factor sign-in. + * + * @public + */ +export declare interface PhoneSingleFactorInfoOptions { + /** Phone number to send a verification code to. */ + phoneNumber: string; +} + +/** + * A resolver used for handling DOM specific operations like {@link signInWithPopup} + * or {@link signInWithRedirect}. + * + * @public + */ +export declare interface PopupRedirectResolver { +} + +/* Excluded from this release type: PopupRedirectResolverInternal */ + +/** + * A minimal error map with all verbose error messages stripped. + * + * See discussion at {@link AuthErrorMap} + * + * @public + */ +export declare const prodErrorMap: AuthErrorMap; + +/** + * Enumeration of supported providers. + * + * @public + */ +export declare const ProviderId: { + /** Facebook provider ID */ + readonly FACEBOOK: "facebook.com"; + /** GitHub provider ID */ + readonly GITHUB: "github.com"; + /** Google provider ID */ + readonly GOOGLE: "google.com"; + /** Password provider */ + readonly PASSWORD: "password"; + /** Phone provider */ + readonly PHONE: "phone"; + /** Twitter provider ID */ + readonly TWITTER: "twitter.com"; +}; + +/* Excluded from this release type: ProviderId_2 */ + +declare interface ProviderUserInfo { + providerId: string; + rawId?: string; + email?: string; + displayName?: string; + photoUrl?: string; + phoneNumber?: string; +} + +/** + * Interface for a supplied `AsyncStorage`. + * + * @public + */ +export declare interface ReactNativeAsyncStorage { + /** + * Persist an item in storage. + * + * @param key - storage key. + * @param value - storage value. + */ + setItem(key: string, value: string): Promise; + /** + * Retrieve an item from storage. + * + * @param key - storage key. + */ + getItem(key: string): Promise; + /** + * Remove an item from storage. + * + * @param key - storage key. + */ + removeItem(key: string): Promise; +} + +/** + * Re-authenticates a user using a fresh credential. + * + * @remarks + * Use before operations such as {@link updatePassword} that require tokens from recent sign-in + * attempts. This method can be used to recover from a `CREDENTIAL_TOO_OLD_LOGIN_AGAIN` error + * or a `TOKEN_EXPIRED` error. + * + * This method is not supported on any {@link User} signed in by {@link Auth} instances + * created with a {@link @firebase/app#FirebaseServerApp}. + * + * @param user - The user. + * @param credential - The auth credential. + * + * @public + */ +export declare function reauthenticateWithCredential(user: User, credential: AuthCredential): Promise; + +/** + * Re-authenticates a user using a fresh phone credential. + * + * @remarks + * Use before operations such as {@link updatePassword} that require tokens from recent sign-in attempts. + * + * This method does not work in a Node.js environment or on any {@link User} signed in by + * {@link Auth} instances created with a {@link @firebase/app#FirebaseServerApp}. + * + * @param user - The user. + * @param phoneNumber - The user's phone number in E.164 format (e.g. +16505550101). + * @param appVerifier - The {@link ApplicationVerifier}. + * + * @public + */ +export declare function reauthenticateWithPhoneNumber(user: User, phoneNumber: string, appVerifier?: ApplicationVerifier): Promise; + +/** + * Reauthenticates the current user with the specified {@link OAuthProvider} using a pop-up based + * OAuth flow. + * + * @remarks + * If the reauthentication is successful, the returned result will contain the user and the + * provider's credential. + * + * This method does not work in a Node.js environment or on any {@link User} signed in by + * {@link Auth} instances created with a {@link @firebase/app#FirebaseServerApp}. + * + * @example + * ```javascript + * // Sign in using a popup. + * const provider = new FacebookAuthProvider(); + * const result = await signInWithPopup(auth, provider); + * // Reauthenticate using a popup. + * await reauthenticateWithPopup(result.user, provider); + * ``` + * + * @param user - The user. + * @param provider - The provider to authenticate. The provider has to be an {@link OAuthProvider}. + * Non-OAuth providers like {@link EmailAuthProvider} will throw an error. + * @param resolver - An instance of {@link PopupRedirectResolver}, optional + * if already supplied to {@link initializeAuth} or provided by {@link getAuth}. + * + * @public + */ +export declare function reauthenticateWithPopup(user: User, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; + +/** + * Reauthenticates the current user with the specified {@link OAuthProvider} using a full-page redirect flow. + * @remarks + * To handle the results and errors for this operation, refer to {@link getRedirectResult}. + * Follow the {@link https://firebase.google.com/docs/auth/web/redirect-best-practices + * | best practices} when using {@link reauthenticateWithRedirect}. + * + * This method does not work in a Node.js environment or with {@link Auth} instances + * created with a {@link @firebase/app#FirebaseServerApp}. + * + * @example + * ```javascript + * // Sign in using a redirect. + * const provider = new FacebookAuthProvider(); + * const result = await signInWithRedirect(auth, provider); + * // This will trigger a full page redirect away from your app + * + * // After returning from the redirect when your app initializes you can obtain the result + * const result = await getRedirectResult(auth); + * // Reauthenticate using a redirect. + * await reauthenticateWithRedirect(result.user, provider); + * // This will again trigger a full page redirect away from your app + * + * // After returning from the redirect when your app initializes you can obtain the result + * const result = await getRedirectResult(auth); + * ``` + * + * @param user - The user. + * @param provider - The provider to authenticate. The provider has to be an {@link OAuthProvider}. + * Non-OAuth providers like {@link EmailAuthProvider} will throw an error. + * @param resolver - An instance of {@link PopupRedirectResolver}, optional + * if already supplied to {@link initializeAuth} or provided by {@link getAuth}. + * + * @public + */ +export declare function reauthenticateWithRedirect(user: User, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; + +declare interface Recaptcha { + render: (container: HTMLElement, parameters: RecaptchaParameters) => number; + getResponse: (id: number) => string; + execute: (id: number) => unknown; + reset: (id: number) => unknown; +} + +declare class RecaptchaConfig { + /** + * The reCAPTCHA site key. + */ + siteKey: string; + /** + * The list of providers and their enablement status for reCAPTCHA Enterprise. + */ + recaptchaEnforcementState: RecaptchaEnforcementProviderState[]; + constructor(response: GetRecaptchaConfigResponse); + /** + * Returns the reCAPTCHA Enterprise enforcement state for the given provider. + * + * @param providerStr - The provider whose enforcement state is to be returned. + * @returns The reCAPTCHA Enterprise enforcement state for the given provider. + */ + getProviderEnforcementState(providerStr: string): EnforcementState | null; + /** + * Returns true if the reCAPTCHA Enterprise enforcement state for the provider is set to ENFORCE or AUDIT. + * + * @param providerStr - The provider whose enablement state is to be returned. + * @returns Whether or not reCAPTCHA Enterprise protection is enabled for the given provider. + */ + isProviderEnabled(providerStr: string): boolean; + /** + * Returns true if reCAPTCHA Enterprise protection is enabled in at least one provider, otherwise + * returns false. + * + * @returns Whether or not reCAPTCHA Enterprise protection is enabled for at least one provider. + */ + isAnyProviderEnabled(): boolean; +} + +declare interface RecaptchaEnforcementProviderState { + provider: string; + enforcementState: string; +} + +/* Excluded from this release type: ReCaptchaLoader */ + +/** + * Interface representing reCAPTCHA parameters. + * + * See the {@link https://developers.google.com/recaptcha/docs/display#render_param | reCAPTCHA docs} + * for the list of accepted parameters. All parameters are accepted except for `sitekey`: Firebase Auth + * provisions a reCAPTCHA for each project and will configure the site key upon rendering. + * + * For an invisible reCAPTCHA, set the `size` key to `invisible`. + * + * @public + */ +export declare interface RecaptchaParameters { + [key: string]: any; +} + +/** + * An {@link https://www.google.com/recaptcha/ | reCAPTCHA}-based application verifier. + * + * @remarks + * `RecaptchaVerifier` does not work in a Node.js environment. + * + * @public + */ +export declare class RecaptchaVerifier implements ApplicationVerifierInternal { + private readonly parameters; + /** + * The application verifier type. + * + * @remarks + * For a reCAPTCHA verifier, this is 'recaptcha'. + */ + readonly type = "recaptcha"; + private destroyed; + private widgetId; + private readonly container; + private readonly isInvisible; + private readonly tokenChangeListeners; + private renderPromise; + private readonly auth; + /* Excluded from this release type: _recaptchaLoader */ + private recaptcha; + /** + * @param authExtern - The corresponding Firebase {@link Auth} instance. + * + * @param containerOrId - The reCAPTCHA container parameter. + * + * @remarks + * This has different meaning depending on whether the reCAPTCHA is hidden or visible. For a + * visible reCAPTCHA the container must be empty. If a string is used, it has to correspond to + * an element ID. The corresponding element must also must be in the DOM at the time of + * initialization. + * + * @param parameters - The optional reCAPTCHA parameters. + * + * @remarks + * Check the reCAPTCHA docs for a comprehensive list. All parameters are accepted except for + * the sitekey. Firebase Auth backend provisions a reCAPTCHA for each project and will + * configure this upon rendering. For an invisible reCAPTCHA, a size key must have the value + * 'invisible'. + */ + constructor(authExtern: Auth, containerOrId: HTMLElement | string, parameters?: RecaptchaParameters); + /** + * Waits for the user to solve the reCAPTCHA and resolves with the reCAPTCHA token. + * + * @returns A Promise for the reCAPTCHA token. + */ + verify(): Promise; + /** + * Renders the reCAPTCHA widget on the page. + * + * @returns A Promise that resolves with the reCAPTCHA widget ID. + */ + render(): Promise; + /* Excluded from this release type: _reset */ + /** + * Clears the reCAPTCHA widget from the page and destroys the instance. + */ + clear(): void; + private validateStartingState; + private makeTokenCallback; + private assertNotDestroyed; + private makeRenderPromise; + private init; + private getAssertedRecaptcha; +} + +/** + * Reloads user account data, if signed in. + * + * @param user - The user. + * + * @public + */ +export declare function reload(user: User): Promise; + +/** + * Revokes the given access token. Currently only supports Apple OAuth access tokens. + * + * @param auth - The {@link Auth} instance. + * @param token - The Apple OAuth access token. + * + * @public + */ +export declare function revokeAccessToken(auth: Auth, token: string): Promise; + +/** + * An {@link AuthProvider} for SAML. + * + * @public + */ +export declare class SAMLAuthProvider extends FederatedAuthProvider { + /** + * Constructor. The providerId must start with "saml." + * @param providerId - SAML provider ID. + */ + constructor(providerId: string); + /** + * Generates an {@link AuthCredential} from a {@link UserCredential} after a + * successful SAML flow completes. + * + * @remarks + * + * For example, to get an {@link AuthCredential}, you could write the + * following code: + * + * ```js + * const userCredential = await signInWithPopup(auth, samlProvider); + * const credential = SAMLAuthProvider.credentialFromResult(userCredential); + * ``` + * + * @param userCredential - The user credential. + */ + static credentialFromResult(userCredential: UserCredential): AuthCredential | null; + /** + * Used to extract the underlying {@link OAuthCredential} from a {@link AuthError} which was + * thrown during a sign-in, link, or reauthenticate operation. + * + * @param userCredential - The user credential. + */ + static credentialFromError(error: FirebaseError): AuthCredential | null; + /** + * Creates an {@link AuthCredential} from a JSON string or a plain object. + * @param json - A plain object or a JSON string + */ + static credentialFromJSON(json: string | object): AuthCredential; + private static samlCredentialFromTaggedObject; +} + +/** + * Sends a verification email to a user. + * + * @remarks + * The verification process is completed by calling {@link applyActionCode}. + * + * @example + * ```javascript + * const actionCodeSettings = { + * url: 'https://www.example.com/?email=user@example.com', + * iOS: { + * bundleId: 'com.example.ios' + * }, + * android: { + * packageName: 'com.example.android', + * installApp: true, + * minimumVersion: '12' + * }, + * handleCodeInApp: true + * }; + * await sendEmailVerification(user, actionCodeSettings); + * // Obtain code from the user. + * await applyActionCode(auth, code); + * ``` + * + * @param user - The user. + * @param actionCodeSettings - The {@link ActionCodeSettings}. + * + * @public + */ +export declare function sendEmailVerification(user: User, actionCodeSettings?: ActionCodeSettings | null): Promise; + +/** + * Sends a password reset email to the given email address. This method does not throw an error when + * there's no user account with the given email address and + * {@link https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection | Email Enumeration Protection} + * is enabled. + * + * @remarks + * To complete the password reset, call {@link confirmPasswordReset} with the code supplied in + * the email sent to the user, along with the new password specified by the user. + * + * @example + * ```javascript + * const actionCodeSettings = { + * url: 'https://www.example.com/?email=user@example.com', + * iOS: { + * bundleId: 'com.example.ios' + * }, + * android: { + * packageName: 'com.example.android', + * installApp: true, + * minimumVersion: '12' + * }, + * handleCodeInApp: true + * }; + * await sendPasswordResetEmail(auth, 'user@example.com', actionCodeSettings); + * // Obtain code from user. + * await confirmPasswordReset('user@example.com', code); + * ``` + * + * @param auth - The {@link Auth} instance. + * @param email - The user's email address. + * @param actionCodeSettings - The {@link ActionCodeSettings}. + * + * @public + */ +export declare function sendPasswordResetEmail(auth: Auth, email: string, actionCodeSettings?: ActionCodeSettings): Promise; + +/** + * Sends a sign-in email link to the user with the specified email. + * + * @remarks + * The sign-in operation has to always be completed in the app unlike other out of band email + * actions (password reset and email verifications). This is because, at the end of the flow, + * the user is expected to be signed in and their Auth state persisted within the app. + * + * To complete sign in with the email link, call {@link signInWithEmailLink} with the email + * address and the email link supplied in the email sent to the user. + * + * @example + * ```javascript + * const actionCodeSettings = { + * url: 'https://www.example.com/?email=user@example.com', + * iOS: { + * bundleId: 'com.example.ios' + * }, + * android: { + * packageName: 'com.example.android', + * installApp: true, + * minimumVersion: '12' + * }, + * handleCodeInApp: true + * }; + * await sendSignInLinkToEmail(auth, 'user@example.com', actionCodeSettings); + * // Obtain emailLink from the user. + * if(isSignInWithEmailLink(auth, emailLink)) { + * await signInWithEmailLink(auth, 'user@example.com', emailLink); + * } + * ``` + * + * @param authInternal - The {@link Auth} instance. + * @param email - The user's email address. + * @param actionCodeSettings - The {@link ActionCodeSettings}. + * + * @public + */ +export declare function sendSignInLinkToEmail(auth: Auth, email: string, actionCodeSettings: ActionCodeSettings): Promise; + +/** + * Changes the type of persistence on the {@link Auth} instance for the currently saved + * `Auth` session and applies this type of persistence for future sign-in requests, including + * sign-in with redirect requests. + * + * @remarks + * This makes it easy for a user signing in to specify whether their session should be + * remembered or not. It also makes it easier to never persist the `Auth` state for applications + * that are shared by other users or have sensitive data. + * + * This method does not work in a Node.js environment or with {@link Auth} instances created with a + * {@link @firebase/app#FirebaseServerApp}. + * + * @example + * ```javascript + * setPersistence(auth, browserSessionPersistence); + * ``` + * + * @param auth - The {@link Auth} instance. + * @param persistence - The {@link Persistence} to use. + * @returns A `Promise` that resolves once the persistence change has completed + * + * @public + */ +export declare function setPersistence(auth: Auth, persistence: Persistence): Promise; + +/** + * Asynchronously signs in as an anonymous user. + * + * @remarks + * If there is already an anonymous user signed in, that user will be returned; otherwise, a + * new anonymous user identity will be created and returned. + * + * This method is not supported by {@link Auth} instances created with a + * {@link @firebase/app#FirebaseServerApp}. + * + * @param auth - The {@link Auth} instance. + * + * @public + */ +export declare function signInAnonymously(auth: Auth): Promise; + +/** + * Enumeration of supported sign-in methods. + * + * @public + */ +export declare const SignInMethod: { + /** Email link sign in method */ + readonly EMAIL_LINK: "emailLink"; + /** Email/password sign in method */ + readonly EMAIL_PASSWORD: "password"; + /** Facebook sign in method */ + readonly FACEBOOK: "facebook.com"; + /** GitHub sign in method */ + readonly GITHUB: "github.com"; + /** Google sign in method */ + readonly GOOGLE: "google.com"; + /** Phone sign in method */ + readonly PHONE: "phone"; + /** Twitter sign in method */ + readonly TWITTER: "twitter.com"; +}; + +/** + * Asynchronously signs in with the given credentials. + * + * @remarks + * An {@link AuthProvider} can be used to generate the credential. + * + * This method is not supported by {@link Auth} instances created with a + * {@link @firebase/app#FirebaseServerApp}. + * + * @param auth - The {@link Auth} instance. + * @param credential - The auth credential. + * + * @public + */ +export declare function signInWithCredential(auth: Auth, credential: AuthCredential): Promise; + +/** + * Asynchronously signs in using a custom token. + * + * @remarks + * Custom tokens are used to integrate Firebase Auth with existing auth systems, and must + * be generated by an auth backend using the + * {@link https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#createcustomtoken | createCustomToken} + * method in the {@link https://firebase.google.com/docs/auth/admin | Admin SDK} . + * + * Fails with an error if the token is invalid, expired, or not accepted by the Firebase Auth service. + * + * This method is not supported by {@link Auth} instances created with a + * {@link @firebase/app#FirebaseServerApp}. + * + * @param auth - The {@link Auth} instance. + * @param customToken - The custom token to sign in with. + * + * @public + */ +export declare function signInWithCustomToken(auth: Auth, customToken: string): Promise; + +/** + * Asynchronously signs in using an email and password. + * + * @remarks + * Fails with an error if the email address and password do not match. When + * {@link https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection | Email Enumeration Protection} + * is enabled, this method fails with "auth/invalid-credential" in case of an invalid + * email/password. + * + * This method is not supported on {@link Auth} instances created with a + * {@link @firebase/app#FirebaseServerApp}. + * + * Note: The user's password is NOT the password used to access the user's email account. The + * email address serves as a unique identifier for the user, and the password is used to access + * the user's account in your Firebase project. See also: {@link createUserWithEmailAndPassword}. + * + * + * @param auth - The {@link Auth} instance. + * @param email - The users email address. + * @param password - The users password. + * + * @public + */ +export declare function signInWithEmailAndPassword(auth: Auth, email: string, password: string): Promise; + +/** + * Asynchronously signs in using an email and sign-in email link. + * + * @remarks + * If no link is passed, the link is inferred from the current URL. + * + * Fails with an error if the email address is invalid or OTP in email link expires. + * + * This method is not supported by {@link Auth} instances created with a + * {@link @firebase/app#FirebaseServerApp}. + * + * Note: Confirm the link is a sign-in email link before calling this method firebase.auth.Auth.isSignInWithEmailLink. + * + * @example + * ```javascript + * const actionCodeSettings = { + * url: 'https://www.example.com/?email=user@example.com', + * iOS: { + * bundleId: 'com.example.ios' + * }, + * android: { + * packageName: 'com.example.android', + * installApp: true, + * minimumVersion: '12' + * }, + * handleCodeInApp: true + * }; + * await sendSignInLinkToEmail(auth, 'user@example.com', actionCodeSettings); + * // Obtain emailLink from the user. + * if(isSignInWithEmailLink(auth, emailLink)) { + * await signInWithEmailLink(auth, 'user@example.com', emailLink); + * } + * ``` + * + * + * @param auth - The {@link Auth} instance. + * @param email - The user's email address. + * @param emailLink - The link sent to the user's email address. + * + * @public + */ +export declare function signInWithEmailLink(auth: Auth, email: string, emailLink?: string): Promise; + +/* Excluded from this release type: SignInWithIdpResponse */ + +/** + * Asynchronously signs in using a phone number. + * + * @remarks + * This method sends a code via SMS to the given + * phone number, and returns a {@link ConfirmationResult}. After the user + * provides the code sent to their phone, call {@link ConfirmationResult.confirm} + * with the code to sign the user in. + * + * For abuse prevention, this method requires a {@link ApplicationVerifier}. + * This SDK includes an implementation based on reCAPTCHA v2, {@link RecaptchaVerifier}. + * This function can work on other platforms that do not support the + * {@link RecaptchaVerifier} (like React Native), but you need to use a + * third-party {@link ApplicationVerifier} implementation. + * + * If you've enabled project-level reCAPTCHA Enterprise bot protection in + * Enforce mode, you can omit the {@link ApplicationVerifier}. + * + * This method does not work in a Node.js environment or with {@link Auth} instances created with a + * {@link @firebase/app#FirebaseServerApp}. + * + * @example + * ```javascript + * // 'recaptcha-container' is the ID of an element in the DOM. + * const applicationVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container'); + * const confirmationResult = await signInWithPhoneNumber(auth, phoneNumber, applicationVerifier); + * // Obtain a verificationCode from the user. + * const credential = await confirmationResult.confirm(verificationCode); + * ``` + * + * @param auth - The {@link Auth} instance. + * @param phoneNumber - The user's phone number in E.164 format (e.g. +16505550101). + * @param appVerifier - The {@link ApplicationVerifier}. + * + * @public + */ +export declare function signInWithPhoneNumber(auth: Auth, phoneNumber: string, appVerifier?: ApplicationVerifier): Promise; + +/* Excluded from this release type: SignInWithPhoneNumberRequest */ + +/* Excluded from this release type: SignInWithPhoneNumberResponse */ + +/** + * Authenticates a Firebase client using a popup-based OAuth authentication flow. + * + * @remarks + * If succeeds, returns the signed in user along with the provider's credential. If sign in was + * unsuccessful, returns an error object containing additional information about the error. + * + * This method does not work in a Node.js environment or with {@link Auth} instances created with a + * {@link @firebase/app#FirebaseServerApp}. + * + * @example + * ```javascript + * // Sign in using a popup. + * const provider = new FacebookAuthProvider(); + * const result = await signInWithPopup(auth, provider); + * + * // The signed-in user info. + * const user = result.user; + * // This gives you a Facebook Access Token. + * const credential = provider.credentialFromResult(auth, result); + * const token = credential.accessToken; + * ``` + * + * @param auth - The {@link Auth} instance. + * @param provider - The provider to authenticate. The provider has to be an {@link OAuthProvider}. + * Non-OAuth providers like {@link EmailAuthProvider} will throw an error. + * @param resolver - An instance of {@link PopupRedirectResolver}, optional + * if already supplied to {@link initializeAuth} or provided by {@link getAuth}. + * + * @public + */ +export declare function signInWithPopup(auth: Auth, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; + +/** + * Authenticates a Firebase client using a full-page redirect flow. + * + * @remarks + * To handle the results and errors for this operation, refer to {@link getRedirectResult}. + * Follow the {@link https://firebase.google.com/docs/auth/web/redirect-best-practices + * | best practices} when using {@link signInWithRedirect}. + * + * This method does not work in a Node.js environment or with {@link Auth} instances created with a + * {@link @firebase/app#FirebaseServerApp}. + * + * @example + * ```javascript + * // Sign in using a redirect. + * const provider = new FacebookAuthProvider(); + * // You can add additional scopes to the provider: + * provider.addScope('user_birthday'); + * // Start a sign in process for an unauthenticated user. + * await signInWithRedirect(auth, provider); + * // This will trigger a full page redirect away from your app + * + * // After returning from the redirect when your app initializes you can obtain the result + * const result = await getRedirectResult(auth); + * if (result) { + * // This is the signed-in user + * const user = result.user; + * // This gives you a Facebook Access Token. + * const credential = provider.credentialFromResult(auth, result); + * const token = credential.accessToken; + * } + * // As this API can be used for sign-in, linking and reauthentication, + * // check the operationType to determine what triggered this redirect + * // operation. + * const operationType = result.operationType; + * ``` + * + * @param auth - The {@link Auth} instance. + * @param provider - The provider to authenticate. The provider has to be an {@link OAuthProvider}. + * Non-OAuth providers like {@link EmailAuthProvider} will throw an error. + * @param resolver - An instance of {@link PopupRedirectResolver}, optional + * if already supplied to {@link initializeAuth} or provided by {@link getAuth}. + * + * @public + */ +export declare function signInWithRedirect(auth: Auth, provider: AuthProvider, resolver?: PopupRedirectResolver): Promise; + +/** + * Signs out the current user. + * + * @remarks + * This method is not supported by {@link Auth} instances created with a + * {@link @firebase/app#FirebaseServerApp}. + * + * @param auth - The {@link Auth} instance. + * + * @public + */ +export declare function signOut(auth: Auth): Promise; + +declare interface StartTotpMfaEnrollmentResponse { + totpSessionInfo: { + sharedSecretKey: string; + verificationCodeLength: number; + hashingAlgorithm: string; + periodSec: number; + sessionInfo: string; + finalizeEnrollmentTime: number; + }; +} + +declare interface StorageEventListener { + (value: PersistenceValue | null): void; +} + +/* Excluded from this release type: StsTokenManager */ + +/* Excluded from this release type: TaggedWithTokenResponse */ + +/** + * An MFA provided by TOTP (Time-based One Time Password). + */ +declare interface TotpMfaEnrollment extends BaseMfaEnrollment { +} + +/** + * The class for asserting ownership of a TOTP second factor. Provided by + * {@link TotpMultiFactorGenerator.assertionForEnrollment} and + * {@link TotpMultiFactorGenerator.assertionForSignIn}. + * + * @public + */ +export declare interface TotpMultiFactorAssertion extends MultiFactorAssertion { +} + +/** + * Provider for generating a {@link TotpMultiFactorAssertion}. + * + * @public + */ +export declare class TotpMultiFactorGenerator { + /** + * Provides a {@link TotpMultiFactorAssertion} to confirm ownership of + * the TOTP (time-based one-time password) second factor. + * This assertion is used to complete enrollment in TOTP second factor. + * + * @param secret A {@link TotpSecret} containing the shared secret key and other TOTP parameters. + * @param oneTimePassword One-time password from TOTP App. + * @returns A {@link TotpMultiFactorAssertion} which can be used with + * {@link MultiFactorUser.enroll}. + */ + static assertionForEnrollment(secret: TotpSecret, oneTimePassword: string): TotpMultiFactorAssertion; + /** + * Provides a {@link TotpMultiFactorAssertion} to confirm ownership of the TOTP second factor. + * This assertion is used to complete signIn with TOTP as the second factor. + * + * @param enrollmentId identifies the enrolled TOTP second factor. + * @param oneTimePassword One-time password from TOTP App. + * @returns A {@link TotpMultiFactorAssertion} which can be used with + * {@link MultiFactorResolver.resolveSignIn}. + */ + static assertionForSignIn(enrollmentId: string, oneTimePassword: string): TotpMultiFactorAssertion; + /** + * Returns a promise to {@link TotpSecret} which contains the TOTP shared secret key and other parameters. + * Creates a TOTP secret as part of enrolling a TOTP second factor. + * Used for generating a QR code URL or inputting into a TOTP app. + * This method uses the auth instance corresponding to the user in the multiFactorSession. + * + * @param session The {@link MultiFactorSession} that the user is part of. + * @returns A promise to {@link TotpSecret}. + */ + static generateSecret(session: MultiFactorSession): Promise; + /** + * The identifier of the TOTP second factor: `totp`. + */ + static FACTOR_ID: 'totp'; +} + +/** + * The subclass of the {@link MultiFactorInfo} interface for TOTP + * second factors. The `factorId` of this second factor is {@link FactorId}.TOTP. + * @public + */ +export declare interface TotpMultiFactorInfo extends MultiFactorInfo { +} + +/** + * Provider for generating a {@link TotpMultiFactorAssertion}. + * + * Stores the shared secret key and other parameters to generate time-based OTPs. + * Implements methods to retrieve the shared secret key and generate a QR code URL. + * @public + */ +export declare class TotpSecret { + private readonly sessionInfo; + private readonly auth; + /** + * Shared secret key/seed used for enrolling in TOTP MFA and generating OTPs. + */ + readonly secretKey: string; + /** + * Hashing algorithm used. + */ + readonly hashingAlgorithm: string; + /** + * Length of the one-time passwords to be generated. + */ + readonly codeLength: number; + /** + * The interval (in seconds) when the OTP codes should change. + */ + readonly codeIntervalSeconds: number; + /** + * The timestamp (UTC string) by which TOTP enrollment should be completed. + */ + readonly enrollmentCompletionDeadline: string; + private constructor(); + /* Excluded from this release type: _fromStartTotpMfaEnrollmentResponse */ + /* Excluded from this release type: _makeTotpVerificationInfo */ + /** + * Returns a QR code URL as described in + * https://github.com/google/google-authenticator/wiki/Key-Uri-Format + * This can be displayed to the user as a QR code to be scanned into a TOTP app like Google Authenticator. + * If the optional parameters are unspecified, an accountName of and issuer of are used. + * + * @param accountName the name of the account/app along with a user identifier. + * @param issuer issuer of the TOTP (likely the app name). + * @returns A QR code URL string. + */ + generateQrCodeUrl(accountName?: string, issuer?: string): string; +} + +declare interface TotpVerificationInfo { + sessionInfo: string; + verificationCode: string; +} + +/** + * Provider for generating an {@link OAuthCredential} for {@link ProviderId}.TWITTER. + * + * @example + * ```javascript + * // Sign in using a redirect. + * const provider = new TwitterAuthProvider(); + * // Start a sign in process for an unauthenticated user. + * await signInWithRedirect(auth, provider); + * // This will trigger a full page redirect away from your app + * + * // After returning from the redirect when your app initializes you can obtain the result + * const result = await getRedirectResult(auth); + * if (result) { + * // This is the signed-in user + * const user = result.user; + * // This gives you a Twitter Access Token and Secret. + * const credential = TwitterAuthProvider.credentialFromResult(result); + * const token = credential.accessToken; + * const secret = credential.secret; + * } + * ``` + * + * @example + * ```javascript + * // Sign in using a popup. + * const provider = new TwitterAuthProvider(); + * const result = await signInWithPopup(auth, provider); + * + * // The signed-in user info. + * const user = result.user; + * // This gives you a Twitter Access Token and Secret. + * const credential = TwitterAuthProvider.credentialFromResult(result); + * const token = credential.accessToken; + * const secret = credential.secret; + * ``` + * + * @public + */ +export declare class TwitterAuthProvider extends BaseOAuthProvider { + /** Always set to {@link SignInMethod}.TWITTER. */ + static readonly TWITTER_SIGN_IN_METHOD: 'twitter.com'; + /** Always set to {@link ProviderId}.TWITTER. */ + static readonly PROVIDER_ID: 'twitter.com'; + constructor(); + /** + * Creates a credential for Twitter. + * + * @param token - Twitter access token. + * @param secret - Twitter secret. + */ + static credential(token: string, secret: string): OAuthCredential; + /** + * Used to extract the underlying {@link OAuthCredential} from a {@link UserCredential}. + * + * @param userCredential - The user credential. + */ + static credentialFromResult(userCredential: UserCredential): OAuthCredential | null; + /** + * Used to extract the underlying {@link OAuthCredential} from a {@link AuthError} which was + * thrown during a sign-in, link, or reauthenticate operation. + * + * @param userCredential - The user credential. + */ + static credentialFromError(error: FirebaseError): OAuthCredential | null; + private static credentialFromTaggedObject; +} + +/** + * Unlinks a provider from a user account. + * + * @param user - The user. + * @param providerId - The provider to unlink. + * + * @public + */ +export declare function unlink(user: User, providerId: string): Promise; +export { Unsubscribe } + +/** + * Asynchronously sets the provided user as {@link Auth.currentUser} on the + * {@link Auth} instance. + * + * @remarks + * A new instance copy of the user provided will be made and set as currentUser. + * + * This will trigger {@link onAuthStateChanged} and {@link onIdTokenChanged} listeners + * like other sign in methods. + * + * The operation fails with an error if the user to be updated belongs to a different Firebase + * project. + * + * This method is not supported by {@link Auth} instances created with a + * {@link @firebase/app#FirebaseServerApp}. + * + * @param auth - The {@link Auth} instance. + * @param user - The new {@link User}. + * + * @public + */ +export declare function updateCurrentUser(auth: Auth, user: User | null): Promise; + +/** + * Updates the user's email address. + * + * @remarks + * An email will be sent to the original email address (if it was set) that allows to revoke the + * email address change, in order to protect them from account hijacking. + * + * This method is not supported on any {@link User} signed in by {@link Auth} instances + * created with a {@link @firebase/app#FirebaseServerApp}. + * + * Important: this is a security sensitive operation that requires the user to have recently signed + * in. If this requirement isn't met, ask the user to authenticate again and then call + * {@link reauthenticateWithCredential}. + * + * @param user - The user. + * @param newEmail - The new email address. + * + * Throws "auth/operation-not-allowed" error when + * {@link https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection | Email Enumeration Protection} + * is enabled. + * Deprecated - Use {@link verifyBeforeUpdateEmail} instead. + * + * @public + */ +export declare function updateEmail(user: User, newEmail: string): Promise; + +/** + * Updates the user's password. + * + * @remarks + * Important: this is a security sensitive operation that requires the user to have recently signed + * in. If this requirement isn't met, ask the user to authenticate again and then call + * {@link reauthenticateWithCredential}. + * + * @param user - The user. + * @param newPassword - The new password. + * + * @public + */ +export declare function updatePassword(user: User, newPassword: string): Promise; + +/** + * Updates the user's phone number. + * + * @remarks + * This method does not work in a Node.js environment or on any {@link User} signed in by + * {@link Auth} instances created with a {@link @firebase/app#FirebaseServerApp}. + * + * @example + * ``` + * // 'recaptcha-container' is the ID of an element in the DOM. + * const applicationVerifier = new RecaptchaVerifier('recaptcha-container'); + * const provider = new PhoneAuthProvider(auth); + * const verificationId = await provider.verifyPhoneNumber('+16505550101', applicationVerifier); + * // Obtain the verificationCode from the user. + * const phoneCredential = PhoneAuthProvider.credential(verificationId, verificationCode); + * await updatePhoneNumber(user, phoneCredential); + * ``` + * + * @param user - The user. + * @param credential - A credential authenticating the new phone number. + * + * @public + */ +export declare function updatePhoneNumber(user: User, credential: PhoneAuthCredential): Promise; + +/** + * Updates a user's profile data. + * + * @param user - The user. + * @param profile - The profile's `displayName` and `photoURL` to update. + * + * @public + */ +export declare function updateProfile(user: User, { displayName, photoURL: photoUrl }: { + displayName?: string | null; + photoURL?: string | null; +}): Promise; + +/** + * Sets the current language to the default device/browser preference. + * + * @param auth - The {@link Auth} instance. + * + * @public + */ +export declare function useDeviceLanguage(auth: Auth): void; + +/** + * A user account. + * + * @public + */ +export declare interface User extends UserInfo { + /** + * Whether the email has been verified with {@link sendEmailVerification} and + * {@link applyActionCode}. + */ + readonly emailVerified: boolean; + /** + * Whether the user is authenticated using the {@link ProviderId}.ANONYMOUS provider. + */ + readonly isAnonymous: boolean; + /** + * Additional metadata around user creation and sign-in times. + */ + readonly metadata: UserMetadata; + /** + * Additional per provider such as displayName and profile information. + */ + readonly providerData: UserInfo[]; + /** + * Refresh token used to reauthenticate the user. Avoid using this directly and prefer + * {@link User.getIdToken} to refresh the ID token instead. + */ + readonly refreshToken: string; + /** + * The user's tenant ID. + * + * @remarks + * This is a read-only property, which indicates the tenant ID + * used to sign in the user. This is null if the user is signed in from the parent + * project. + * + * @example + * ```javascript + * // Set the tenant ID on Auth instance. + * auth.tenantId = 'TENANT_PROJECT_ID'; + * + * // All future sign-in request now include tenant ID. + * const result = await signInWithEmailAndPassword(auth, email, password); + * // result.user.tenantId should be 'TENANT_PROJECT_ID'. + * ``` + */ + readonly tenantId: string | null; + /** + * Deletes and signs out the user. + * + * @remarks + * Important: this is a security-sensitive operation that requires the user to have recently + * signed in. If this requirement isn't met, ask the user to authenticate again and then call + * one of the reauthentication methods like {@link reauthenticateWithCredential}. + * + * This method is not supported on any {@link User} signed in by {@link Auth} instances + * created with a {@link @firebase/app#FirebaseServerApp}. + */ + delete(): Promise; + /** + * Returns a JSON Web Token (JWT) used to identify the user to a Firebase service. + * + * @remarks + * Returns the current token if it has not expired or if it will not expire in the next five + * minutes. Otherwise, this will refresh the token and return a new one. + * + * @param forceRefresh - Force refresh regardless of token expiration. + */ + getIdToken(forceRefresh?: boolean): Promise; + /** + * Returns a deserialized JSON Web Token (JWT) used to identify the user to a Firebase service. + * + * @remarks + * Returns the current token if it has not expired or if it will not expire in the next five + * minutes. Otherwise, this will refresh the token and return a new one. + * + * @param forceRefresh - Force refresh regardless of token expiration. + */ + getIdTokenResult(forceRefresh?: boolean): Promise; + /** + * Refreshes the user, if signed in. + */ + reload(): Promise; + /** + * Returns a JSON-serializable representation of this object. + * + * @returns A JSON-serializable representation of this object. + */ + toJSON(): object; +} + +/** + * A structure containing a {@link User}, the {@link OperationType}, and the provider ID. + * + * @remarks + * `operationType` could be {@link OperationType}.SIGN_IN for a sign-in operation, + * {@link OperationType}.LINK for a linking operation and {@link OperationType}.REAUTHENTICATE for + * a reauthentication operation. + * + * @public + */ +export declare interface UserCredential { + /** + * The user authenticated by this credential. + */ + user: User; + /** + * The provider which was used to authenticate the user. + */ + providerId: string | null; + /** + * The type of operation which was used to authenticate the user (such as sign-in or link). + */ + operationType: (typeof OperationType)[keyof typeof OperationType]; +} + +/* Excluded from this release type: UserCredentialInternal */ + +/** + * User profile information, visible only to the Firebase project's apps. + * + * @public + */ +export declare interface UserInfo { + /** + * The display name of the user. + */ + readonly displayName: string | null; + /** + * The email of the user. + */ + readonly email: string | null; + /** + * The phone number normalized based on the E.164 standard (e.g. +16505550101) for the + * user. + * + * @remarks + * This is null if the user has no phone credential linked to the account. + */ + readonly phoneNumber: string | null; + /** + * The profile photo URL of the user. + */ + readonly photoURL: string | null; + /** + * The provider used to authenticate the user. + */ + readonly providerId: string; + /** + * The user's unique ID, scoped to the project. + */ + readonly uid: string; +} + +/* Excluded from this release type: UserInternal */ + +/** + * Interface representing a user's metadata. + * + * @public + */ +export declare interface UserMetadata { + /** Time the user was created. */ + readonly creationTime?: string; + /** Time the user last signed in. */ + readonly lastSignInTime?: string; +} + +declare class UserMetadata_2 implements UserMetadata { + private createdAt?; + private lastLoginAt?; + creationTime?: string; + lastSignInTime?: string; + constructor(createdAt?: (string | number) | undefined, lastLoginAt?: (string | number) | undefined); + private _initializeTime; + _copy(metadata: UserMetadata_2): void; + toJSON(): object; +} + +/** + * User profile used in {@link AdditionalUserInfo}. + * + * @public + */ +export declare type UserProfile = Record; + +/** + * Validates the password against the password policy configured for the project or tenant. + * + * @remarks + * If no tenant ID is set on the `Auth` instance, then this method will use the password + * policy configured for the project. Otherwise, this method will use the policy configured + * for the tenant. If a password policy has not been configured, then the default policy + * configured for all projects will be used. + * + * If an auth flow fails because a submitted password does not meet the password policy + * requirements and this method has previously been called, then this method will use the + * most recent policy available when called again. + * + * @example + * ```javascript + * validatePassword(auth, 'some-password'); + * ``` + * + * @param auth The {@link Auth} instance. + * @param password The password to validate. + * + * @public + */ +export declare function validatePassword(auth: Auth, password: string): Promise; + +/** + * Sends a verification email to a new email address. + * + * @remarks + * The user's email will be updated to the new one after being verified. + * + * If you have a custom email action handler, you can complete the verification process by calling + * {@link applyActionCode}. + * + * @example + * ```javascript + * const actionCodeSettings = { + * url: 'https://www.example.com/?email=user@example.com', + * iOS: { + * bundleId: 'com.example.ios' + * }, + * android: { + * packageName: 'com.example.android', + * installApp: true, + * minimumVersion: '12' + * }, + * handleCodeInApp: true + * }; + * await verifyBeforeUpdateEmail(user, 'newemail@example.com', actionCodeSettings); + * // Obtain code from the user. + * await applyActionCode(auth, code); + * ``` + * + * @param user - The user. + * @param newEmail - The new email address to be verified before update. + * @param actionCodeSettings - The {@link ActionCodeSettings}. + * + * @public + */ +export declare function verifyBeforeUpdateEmail(user: User, newEmail: string, actionCodeSettings?: ActionCodeSettings | null): Promise; + +/** + * Checks a password reset code sent to the user by email or other out-of-band mechanism. + * + * @returns the user's email address if valid. + * + * @param auth - The {@link Auth} instance. + * @param code - A verification code sent to the user. + * + * @public + */ +export declare function verifyPasswordResetCode(auth: Auth, code: string): Promise; + +export { } diff --git a/.github/scripts/compare-types/packages/auth/config.ts b/.github/scripts/compare-types/packages/auth/config.ts new file mode 100644 index 0000000000..f0055a1ff0 --- /dev/null +++ b/.github/scripts/compare-types/packages/auth/config.ts @@ -0,0 +1,256 @@ +/** + * Known differences between the firebase-js-sdk @firebase/auth public + * API and the @react-native-firebase/auth modular API. + * + * Each entry must have a `name` and a `reason`. Any undocumented + * difference or stale entry will fail `yarn compare:types`. + */ + +import type { PackageConfig } from '../../src/types'; + +const config: PackageConfig = { + nameMapping: {}, + + missingInRN: [ + { + name: 'initializeRecaptchaConfig', + reason: + 'Web-only reCAPTCHA bootstrap helper from the firebase-js-sdk. RN Firebase does not expose browser reCAPTCHA initialization because native SDKs own the phone-auth verification flow.', + }, + { + name: 'AuthErrorCodes', + reason: + 'RN Firebase still relies on native auth error code strings and does not export the firebase-js-sdk AuthErrorCodes constant map.', + }, + { + name: 'browserCookiePersistence', + reason: + 'Browser-only persistence implementation from the firebase-js-sdk. Not applicable to React Native native-auth persistence.', + }, + { + name: 'browserLocalPersistence', + reason: + 'Browser-only localStorage persistence implementation from the firebase-js-sdk. Not applicable to React Native native-auth persistence.', + }, + { + name: 'browserPopupRedirectResolver', + reason: + 'Browser-only popup/redirect resolver from the firebase-js-sdk. Native provider flows do not use the browser popup resolver stack.', + }, + { + name: 'browserSessionPersistence', + reason: + 'Browser-only sessionStorage persistence implementation from the firebase-js-sdk. Not applicable to React Native native-auth persistence.', + }, + { + name: 'debugErrorMap', + reason: + 'firebase-js-sdk web error-map helper. RN Firebase does not expose the SDK error-map selection API.', + }, + { + name: 'EmailAuthCredential', + reason: + 'RN Firebase does not yet expose the firebase-js-sdk EmailAuthCredential class as part of the modular public surface.', + }, + { + name: 'indexedDBLocalPersistence', + reason: + 'IndexedDB persistence is web-only and not applicable to React Native native-auth persistence.', + }, + { + name: 'inMemoryPersistence', + reason: + 'The firebase-js-sdk persistence object is not exported by RN Firebase because auth state is managed by the underlying native SDKs.', + }, + { + name: 'OAuthCredential', + reason: + 'RN Firebase does not yet expose the firebase-js-sdk OAuthCredential class as part of the modular public surface.', + }, + { + name: 'OAuthCredentialOptions', + reason: + 'RN Firebase does not yet export the firebase-js-sdk OAuthCredentialOptions interface.', + }, + { + name: 'PhoneAuthCredential', + reason: + 'RN Firebase does not yet expose the firebase-js-sdk PhoneAuthCredential class as part of the modular public surface.', + }, + { + name: 'prodErrorMap', + reason: + 'firebase-js-sdk web error-map helper. RN Firebase does not expose the SDK error-map selection API.', + }, + { + name: 'ReactNativeAsyncStorage', + reason: + 'The firebase-js-sdk React Native persistence helper type is not exported by RN Firebase because persistence is delegated to the native iOS/Android SDKs rather than configured through initializeAuth().', + }, + { + name: 'RecaptchaParameters', + reason: 'Browser reCAPTCHA configuration is not part of the RN Firebase native auth surface.', + }, + { + name: 'RecaptchaVerifier', + reason: + 'Browser reCAPTCHA verifier implementation is not available in RN Firebase because native SDKs own application verification.', + }, + { + name: 'SAMLAuthProvider', + reason: + 'SAMLAuthProvider is not yet surfaced on the RN Firebase modular/public auth surface.', + }, + ], + + extraInRN: [ + { + name: 'AppleAuthProvider', + reason: + 'RN Firebase-specific Apple auth provider helper exposed for native Sign in with Apple flows; the firebase-js-sdk does not export a separate AppleAuthProvider class.', + }, + { + name: 'NativeFirebaseAuthError', + reason: + 'RN Firebase-specific native bridge auth error type used in place of the firebase-js-sdk AuthError export.', + }, + { + name: 'OIDCAuthProvider', + reason: + 'RN Firebase-specific OIDC auth provider class retained for compatibility with the existing public package surface; the firebase-js-sdk exposes OAuthProvider instead of a separate OIDCAuthProvider class.', + }, + { + name: 'OIDCProvider', + reason: + 'RN Firebase-specific OIDC provider class export retained for compatibility with the existing package surface.', + }, + { + name: 'PhoneAuthState', + reason: 'RN Firebase-specific enum-like object describing native phone-auth listener states.', + }, + { + name: 'PhoneAuthListener', + reason: 'RN Firebase-specific listener object returned by verifyPhoneNumber().', + }, + { + name: 'PhoneAuthError', + reason: + 'RN Firebase-specific phone verification error snapshot type used by native phone-auth listeners.', + }, + { + name: 'PhoneAuthSnapshot', + reason: + 'RN Firebase-specific phone verification snapshot type used by native phone-auth listeners.', + }, + { + name: 'verifyPhoneNumber', + reason: + 'RN Firebase-specific helper exposing the native phone verification listener flow; the firebase-js-sdk does not export this helper.', + }, + { + name: 'setLanguageCode', + reason: + 'RN Firebase keeps a modular helper for setting auth.languageCode, while the firebase-js-sdk only exposes the writable property.', + }, + { + name: 'useUserAccessGroup', + reason: + 'RN Firebase-specific iOS keychain sharing helper with no firebase-js-sdk equivalent.', + }, + { + name: 'getCustomAuthDomain', + reason: + 'RN Firebase-specific helper that exposes the configured native auth domain; no firebase-js-sdk equivalent exists.', + }, + ], + + differentShape: [ + { + name: 'connectAuthEmulator', + reason: + 'RN Firebase models disableWarnings as an optional property, while the firebase-js-sdk emitted type text shows a required boolean property.', + }, + { + name: 'EmailAuthProvider', + reason: + 'RN Firebase now exports EmailAuthProvider from the modular surface, but its native helper class still exposes a reduced static API and RNFB credential objects rather than mirroring the firebase-js-sdk EmailAuthProvider class shape.', + }, + { + name: 'isSignInWithEmailLink', + reason: + 'RN Firebase resolves this check asynchronously through the native bridge and returns Promise, whereas the firebase-js-sdk returns a synchronous boolean.', + }, + { + name: 'linkWithRedirect', + reason: + 'Native provider flows resolve immediately with a UserCredential instead of following the browser redirect contract used by the firebase-js-sdk.', + }, + { + name: 'reauthenticateWithRedirect', + reason: + 'Native provider flows do not follow the browser redirect contract. RN Firebase models this as Promise rather than the firebase-js-sdk Promise signature.', + }, + { + name: 'signInWithRedirect', + reason: + 'Native provider flows resolve immediately with a UserCredential instead of following the browser redirect contract used by the firebase-js-sdk.', + }, + { + name: 'updatePhoneNumber', + reason: + 'RN Firebase accepts the broader AuthCredential surface, while the firebase-js-sdk narrows this parameter to PhoneAuthCredential.', + }, + { + name: 'FacebookAuthProvider', + reason: + 'RN Firebase now exports FacebookAuthProvider from the modular surface, but its native helper class only exposes the credential factory used by RNFB and does not yet mirror the firebase-js-sdk static fields and credentialFromResult/credentialFromError helpers.', + }, + { + name: 'GithubAuthProvider', + reason: + 'RN Firebase now exports GithubAuthProvider from the modular surface, but its native helper class only exposes the credential factory used by RNFB and does not yet mirror the firebase-js-sdk static fields and credentialFromResult/credentialFromError helpers.', + }, + { + name: 'GoogleAuthProvider', + reason: + 'RN Firebase now exports GoogleAuthProvider from the modular surface, but its native helper class only exposes the credential factory used by RNFB and does not yet mirror the firebase-js-sdk static fields and credentialFromResult/credentialFromError helpers.', + }, + { + name: 'OAuthProvider', + reason: + 'RN Firebase now exports OAuthProvider from the modular surface, but its native helper class still differs from the firebase-js-sdk class by exposing RNFB-specific credential construction and omitting credentialFromJSON/credentialFromResult/credentialFromError helpers.', + }, + { + name: 'ParsedToken', + reason: + 'TypeScript emits the parsed-token property keys without quotes in the generated declaration file, so compare-types reports a text-only difference even though the property set matches the firebase-js-sdk surface.', + }, + { + name: 'PhoneAuthProvider', + reason: + 'RN Firebase now exports PhoneAuthProvider from the modular surface, but the native helper class still exposes a reduced static API and looser signatures than the firebase-js-sdk PhoneAuthProvider class.', + }, + { + name: 'PhoneMultiFactorGenerator', + reason: + 'RN Firebase now exports PhoneMultiFactorGenerator from the modular surface, but its helper class still returns RNFB credential/assertion shapes rather than the firebase-js-sdk PhoneMultiFactorGenerator static API exactly.', + }, + { + name: 'TotpMultiFactorGenerator', + reason: + 'RN Firebase now exports TotpMultiFactorGenerator from the modular surface, but its helper class still differs from the firebase-js-sdk static API in signatures and returned assertion/secret shapes.', + }, + { + name: 'TotpSecret', + reason: + 'RN Firebase now exports TotpSecret from the modular surface, but the native-backed helper class exposes a reduced field set plus async/native helper methods that do not match the firebase-js-sdk TotpSecret class shape.', + }, + { + name: 'TwitterAuthProvider', + reason: + 'RN Firebase now exports TwitterAuthProvider from the modular surface, but its native helper class only exposes the credential factory used by RNFB and does not yet mirror the firebase-js-sdk static fields and credentialFromResult/credentialFromError helpers.', + }, + ], +}; + +export default config; diff --git a/.github/scripts/compare-types/src/registry.ts b/.github/scripts/compare-types/src/registry.ts index e1f546a0f8..ba2abaef7d 100644 --- a/.github/scripts/compare-types/src/registry.ts +++ b/.github/scripts/compare-types/src/registry.ts @@ -15,6 +15,7 @@ import type { PackageConfig } from './types'; import storageConfig from '../packages/storage/config'; import aiConfig from '../packages/ai/config'; +import authConfig from '../packages/auth/config'; import appCheckConfig from '../packages/app-check/config'; import firestoreConfig from '../packages/firestore/config'; import firestorePipelinesConfig from '../packages/firestore-pipelines/config'; @@ -51,14 +52,43 @@ function rnDist(packageName: string): string { } export const packages: PackageEntry[] = [ + { + name: 'auth', + firebaseSdkTypesPaths: [path.join(SCRIPT_DIR, 'packages', 'auth', 'auth-js-sdk.d.ts')], + rnFirebaseModularFiles: [ + path.join(rnDist('auth'), 'types', 'auth.d.ts'), + path.join(rnDist('auth'), 'modular.d.ts'), + ], + rnFirebaseSupportFiles: [ + path.join(rnDist('auth'), 'index.d.ts'), + path.join(rnDist('auth'), 'namespaced.d.ts'), + path.join(rnDist('auth'), 'types', 'namespaced.d.ts'), + path.join(rnDist('auth'), 'types', 'internal.d.ts'), + path.join(rnDist('auth'), 'ConfirmationResult.d.ts'), + path.join(rnDist('auth'), 'MultiFactorResolver.d.ts'), + path.join(rnDist('auth'), 'PhoneAuthListener.d.ts'), + path.join(rnDist('auth'), 'PhoneMultiFactorGenerator.d.ts'), + path.join(rnDist('auth'), 'Settings.d.ts'), + path.join(rnDist('auth'), 'TotpMultiFactorGenerator.d.ts'), + path.join(rnDist('auth'), 'TotpSecret.d.ts'), + path.join(rnDist('auth'), 'User.d.ts'), + path.join(rnDist('auth'), 'getMultiFactorResolver.d.ts'), + path.join(rnDist('auth'), 'multiFactor.d.ts'), + path.join(rnDist('auth'), 'providers', 'AppleAuthProvider.d.ts'), + path.join(rnDist('auth'), 'providers', 'EmailAuthProvider.d.ts'), + path.join(rnDist('auth'), 'providers', 'FacebookAuthProvider.d.ts'), + path.join(rnDist('auth'), 'providers', 'GithubAuthProvider.d.ts'), + path.join(rnDist('auth'), 'providers', 'GoogleAuthProvider.d.ts'), + path.join(rnDist('auth'), 'providers', 'OAuthProvider.d.ts'), + path.join(rnDist('auth'), 'providers', 'OIDCAuthProvider.d.ts'), + path.join(rnDist('auth'), 'providers', 'PhoneAuthProvider.d.ts'), + path.join(rnDist('auth'), 'providers', 'TwitterAuthProvider.d.ts'), + ], + config: authConfig, + }, { name: 'storage', - firebaseSdkTypesPaths: [path.join( - SCRIPT_DIR, - 'packages', - 'storage', - 'storage-js-sdk.d.ts', - )], + firebaseSdkTypesPaths: [path.join(SCRIPT_DIR, 'packages', 'storage', 'storage-js-sdk.d.ts')], rnFirebaseModularFiles: [ path.join(rnDist('storage'), 'types', 'storage.d.ts'), path.join(rnDist('storage'), 'modular.d.ts'), @@ -190,5 +220,3 @@ export const packages: PackageEntry[] = [ config: firestorePipelinesConfig, }, ]; - - diff --git a/packages/ai/tsconfig.json b/packages/ai/tsconfig.json index 837c336e3e..f57afbea70 100644 --- a/packages/ai/tsconfig.json +++ b/packages/ai/tsconfig.json @@ -32,7 +32,7 @@ ], "@react-native-firebase/app/lib/internal": ["../app/dist/typescript/lib/internal"], "@react-native-firebase/app": ["../app/dist/typescript/lib"], - "@react-native-firebase/auth": ["../auth/lib"], + "@react-native-firebase/auth": ["../auth/dist/typescript/lib"], "@react-native-firebase/app-check": ["../app-check/dist/typescript/lib"] } }, diff --git a/packages/auth/lib/ConfirmationResult.js b/packages/auth/lib/ConfirmationResult.ts similarity index 62% rename from packages/auth/lib/ConfirmationResult.js rename to packages/auth/lib/ConfirmationResult.ts index 25b72a2cd7..94faaacf74 100644 --- a/packages/auth/lib/ConfirmationResult.js +++ b/packages/auth/lib/ConfirmationResult.ts @@ -15,19 +15,28 @@ * */ +import type { FirebaseAuthTypes } from './types/namespaced'; +import type { AuthInternal } from './types/internal'; + export default class ConfirmationResult { - constructor(auth, verificationId) { + private readonly _auth: AuthInternal; + private readonly _verificationId: string; + + constructor(auth: AuthInternal, verificationId: string) { this._auth = auth; this._verificationId = verificationId; } - confirm(verificationCode) { + confirm(verificationCode: string): Promise { return this._auth.native .confirmationResultConfirm(verificationCode) - .then(userCredential => this._auth._setUserCredential(userCredential)); + .then( + userCredential => + this._auth._setUserCredential(userCredential) as FirebaseAuthTypes.UserCredential, + ); } - get verificationId() { + get verificationId(): string { return this._verificationId; } } diff --git a/packages/auth/lib/MultiFactorResolver.js b/packages/auth/lib/MultiFactorResolver.js deleted file mode 100644 index 364c5d4ec5..0000000000 --- a/packages/auth/lib/MultiFactorResolver.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Base class to facilitate multi-factor authentication. - */ -export default class MultiFactorResolver { - constructor(auth, resolver) { - this._auth = auth; - this.hints = resolver.hints; - this.session = resolver.session; - } - - resolveSignIn(assertion) { - const { token, secret, uid, verificationCode } = assertion; - - if (token && secret) { - return this._auth.resolveMultiFactorSignIn(this.session, token, secret); - } - - return this._auth.resolveTotpSignIn(this.session, uid, verificationCode); - } -} diff --git a/packages/auth/lib/MultiFactorResolver.ts b/packages/auth/lib/MultiFactorResolver.ts new file mode 100644 index 0000000000..9232de5d67 --- /dev/null +++ b/packages/auth/lib/MultiFactorResolver.ts @@ -0,0 +1,51 @@ +/** + * Base class to facilitate multi-factor authentication. + */ +import type { FirebaseAuthTypes } from './types/namespaced'; +import type { AuthInternal } from './types/internal'; + +type ResolverLike = { + hints: FirebaseAuthTypes.MultiFactorInfo[]; + session: FirebaseAuthTypes.MultiFactorSession; +}; + +type PhoneMultiFactorAssertion = { + token?: string; + secret?: string; + uid?: string; + verificationCode?: string; +}; + +export default class MultiFactorResolver { + readonly hints: FirebaseAuthTypes.MultiFactorInfo[]; + readonly session: FirebaseAuthTypes.MultiFactorSession; + private readonly _auth: AuthInternal; + + constructor(auth: AuthInternal, resolver: ResolverLike) { + this._auth = auth; + this.hints = resolver.hints; + this.session = resolver.session; + } + + resolveSignIn(assertion: PhoneMultiFactorAssertion): Promise { + const { token, secret, uid, verificationCode } = assertion; + + if (token && secret) { + return this._auth.resolveMultiFactorSignIn( + this.session, + token, + secret, + ) as Promise; + } + + if (uid && verificationCode) { + return this._auth.resolveTotpSignIn( + this.session, + uid, + verificationCode, + ) as Promise; + } + + throw new Error('Invalid multi-factor assertion provided for sign-in resolution.'); + } +} diff --git a/packages/auth/lib/PhoneAuthListener.js b/packages/auth/lib/PhoneAuthListener.ts similarity index 52% rename from packages/auth/lib/PhoneAuthListener.js rename to packages/auth/lib/PhoneAuthListener.ts index eed1aa7021..1820cc088a 100644 --- a/packages/auth/lib/PhoneAuthListener.js +++ b/packages/auth/lib/PhoneAuthListener.ts @@ -21,17 +21,46 @@ import { isIOS, promiseDefer, } from '@react-native-firebase/app/dist/module/common'; +import type { ReactNativeFirebase } from '@react-native-firebase/app'; import NativeFirebaseError from '@react-native-firebase/app/dist/module/internal/NativeFirebaseError'; +import type { FirebaseAuthTypes } from './types/namespaced'; +import type { + AuthInternal, + NativePhoneAuthCredentialInternal, + NativePhoneAuthErrorInternal, +} from './types/internal'; + +type PhoneAuthInternalEventType = + | 'codeSent' + | 'verificationFailed' + | 'verificationComplete' + | 'codeAutoRetrievalTimeout'; + +type InternalEvents = Record; +type PublicEvents = Record<'error' | 'event' | 'success', string>; +type PhoneAuthSnapshot = FirebaseAuthTypes.PhoneAuthSnapshot; +type PhoneAuthError = FirebaseAuthTypes.PhoneAuthError; let REQUEST_ID = 0; export default class PhoneAuthListener { - constructor(auth, phoneNumber, timeout, forceResend) { + private readonly _auth: AuthInternal; + private _reject: ((error: ReactNativeFirebase.NativeFirebaseError) => void) | null; + private _resolve: ((snapshot: PhoneAuthSnapshot) => void) | null; + private _promise: Promise | null; + private readonly _jsStack: string; + private readonly _timeout: number; + private readonly _phoneAuthRequestId: number; + private readonly _forceResending: boolean; + private readonly _internalEvents: InternalEvents; + private readonly _publicEvents: PublicEvents; + + constructor(auth: AuthInternal, phoneNumber: string, timeout?: number, forceResend?: boolean) { this._auth = auth; this._reject = null; this._resolve = null; this._promise = null; - this._jsStack = new Error().stack; + this._jsStack = new Error().stack ?? ''; this._timeout = timeout || 20; this._phoneAuthRequestId = REQUEST_ID++; @@ -68,42 +97,64 @@ export default class PhoneAuthListener { } } - _subscribeToEvents() { - const events = Object.keys(this._internalEvents); + private _subscribeToEvents(): void { + const events: PhoneAuthInternalEventType[] = [ + 'codeSent', + 'verificationFailed', + 'verificationComplete', + 'codeAutoRetrievalTimeout', + ]; - for (let i = 0, len = events.length; i < len; i++) { - const type = events[i]; + for (const type of events) { const subscription = this._auth.emitter.addListener(this._internalEvents[type], event => { - this[`_${type}Handler`](event); + switch (type) { + case 'codeSent': + this._codeSentHandler(event as NativePhoneAuthCredentialInternal); + break; + case 'verificationFailed': + this._verificationFailedHandler(event as NativePhoneAuthErrorInternal); + break; + case 'verificationComplete': + this._verificationCompleteHandler(event as NativePhoneAuthCredentialInternal); + break; + case 'codeAutoRetrievalTimeout': + this._codeAutoRetrievalTimeoutHandler(event as NativePhoneAuthCredentialInternal); + break; + } subscription.remove(); }); } } - _addUserObserver(observer) { + private _addUserObserver(observer: (snapshot: PhoneAuthSnapshot) => void): void { this._auth.emitter.addListener(this._publicEvents.event, observer); } - _emitToObservers(snapshot) { + private _emitToObservers(snapshot: PhoneAuthSnapshot): void { this._auth.emitter.emit(this._publicEvents.event, snapshot); } - _emitToErrorCb(snapshot) { + private _emitToErrorCb(snapshot: PhoneAuthSnapshot): void { const { error } = snapshot; - if (this._reject) { + if (this._reject && error) { this._reject(error); } - this._auth.emitter.emit(this._publicEvents.error, error); + this._auth.emitter.emit(this._publicEvents.error, { + code: error?.code ?? null, + verificationId: snapshot.verificationId, + message: error?.message ?? null, + stack: error?.stack ?? null, + } as PhoneAuthError); } - _emitToSuccessCb(snapshot) { + private _emitToSuccessCb(snapshot: PhoneAuthSnapshot): void { if (this._resolve) { this._resolve(snapshot); } this._auth.emitter.emit(this._publicEvents.success, snapshot); } - _removeAllListeners() { + private _removeAllListeners(): void { setTimeout(() => { // move to next event loop - not sure if needed // internal listeners @@ -118,9 +169,13 @@ export default class PhoneAuthListener { }, 0); } - _promiseDeferred() { + private _promiseDeferred(): void { if (!this._promise) { - const { promise, resolve, reject } = promiseDefer(); + const { promise, resolve, reject } = promiseDefer() as { + promise: Promise; + resolve: (snapshot: PhoneAuthSnapshot) => void; + reject: (error: ReactNativeFirebase.NativeFirebaseError) => void; + }; this._promise = promise; this._resolve = resolve; this._reject = reject; @@ -131,8 +186,8 @@ export default class PhoneAuthListener { --- INTERNAL EVENT HANDLERS ---------------------------- */ - _codeSentHandler(credential) { - const snapshot = { + private _codeSentHandler(credential: NativePhoneAuthCredentialInternal): void { + const snapshot: PhoneAuthSnapshot = { verificationId: credential.verificationId, code: null, error: null, @@ -151,8 +206,8 @@ export default class PhoneAuthListener { } } - _codeAutoRetrievalTimeoutHandler(credential) { - const snapshot = { + private _codeAutoRetrievalTimeoutHandler(credential: NativePhoneAuthCredentialInternal): void { + const snapshot: PhoneAuthSnapshot = { verificationId: credential.verificationId, code: null, error: null, @@ -163,8 +218,8 @@ export default class PhoneAuthListener { this._emitToSuccessCb(snapshot); } - _verificationCompleteHandler(credential) { - const snapshot = { + private _verificationCompleteHandler(credential: NativePhoneAuthCredentialInternal): void { + const snapshot: PhoneAuthSnapshot = { verificationId: credential.verificationId, code: credential.code || null, error: null, @@ -176,15 +231,19 @@ export default class PhoneAuthListener { this._removeAllListeners(); } - _verificationFailedHandler(state) { - const snapshot = { + private _verificationFailedHandler(state: NativePhoneAuthErrorInternal): void { + const snapshot: PhoneAuthSnapshot = { verificationId: state.verificationId, code: null, error: null, state: 'error', }; - snapshot.error = new NativeFirebaseError({ userInfo: state.error }, this._jsStack, 'auth'); + snapshot.error = new NativeFirebaseError( + { userInfo: state.error }, + this._jsStack, + 'auth', + ) as ReactNativeFirebase.NativeFirebaseError; this._emitToObservers(snapshot); this._emitToErrorCb(snapshot); @@ -195,7 +254,12 @@ export default class PhoneAuthListener { -- PUBLIC API --------------*/ - on(event, observer, errorCb, successCb) { + on( + event: string, + observer: (snapshot: PhoneAuthSnapshot) => void, + errorCb?: (error: PhoneAuthError) => void, + successCb?: (snapshot: PhoneAuthSnapshot) => void, + ): PhoneAuthListener { if (event !== 'state_changed') { throw new Error( "firebase.auth.PhoneAuthListener.on(*, _, _, _) 'event' must equal 'state_changed'.", @@ -213,33 +277,36 @@ export default class PhoneAuthListener { if (isFunction(errorCb)) { const subscription = this._auth.emitter.addListener(this._publicEvents.error, event => { subscription.remove(); - errorCb(event); + errorCb(event as PhoneAuthError); }); } if (isFunction(successCb)) { const subscription = this._auth.emitter.addListener(this._publicEvents.success, event => { subscription.remove(); - successCb(event); + successCb(event as PhoneAuthSnapshot); }); } return this; } - then(fn) { + then( + onFulfilled?: ((value: PhoneAuthSnapshot) => TResult1 | PromiseLike) | null, + onRejected?: + | ((reason: ReactNativeFirebase.NativeFirebaseError) => TResult2 | PromiseLike) + | null, + ): Promise { this._promiseDeferred(); - if (this._promise) { - return this._promise.then.bind(this._promise)(fn); - } - return undefined; + return this._promise!.then(onFulfilled ?? undefined, onRejected ?? undefined); } - catch(fn) { + catch( + onRejected?: + | ((reason: ReactNativeFirebase.NativeFirebaseError) => TResult | PromiseLike) + | null, + ): Promise { this._promiseDeferred(); - if (this._promise) { - return this._promise.catch.bind(this._promise)(fn); - } - return undefined; + return this._promise!.catch(onRejected ?? undefined); } } diff --git a/packages/auth/lib/PhoneAuthState.ts b/packages/auth/lib/PhoneAuthState.ts new file mode 100644 index 0000000000..9e06b8c3e6 --- /dev/null +++ b/packages/auth/lib/PhoneAuthState.ts @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + */ + +export const PhoneAuthState = { + CODE_SENT: 'sent', + AUTO_VERIFY_TIMEOUT: 'timeout', + AUTO_VERIFIED: 'verified', + ERROR: 'error', +} as const; diff --git a/packages/auth/lib/PhoneMultiFactorGenerator.js b/packages/auth/lib/PhoneMultiFactorGenerator.ts similarity index 80% rename from packages/auth/lib/PhoneMultiFactorGenerator.js rename to packages/auth/lib/PhoneMultiFactorGenerator.ts index 1f3bbdd4e4..d8a7e7dcc9 100644 --- a/packages/auth/lib/PhoneMultiFactorGenerator.js +++ b/packages/auth/lib/PhoneMultiFactorGenerator.ts @@ -15,6 +15,8 @@ * */ +import type { AuthCredential, MultiFactorAssertion } from './types/auth'; + export default class PhoneMultiFactorGenerator { static FACTOR_ID = 'phone'; @@ -24,10 +26,14 @@ export default class PhoneMultiFactorGenerator { ); } - static assertion(credential) { + static assertion(credential: AuthCredential): MultiFactorAssertion { // There is no logic here, we mainly do this for API compatibility // (following the Web API). const { token, secret } = credential; - return { token, secret }; + return { + token, + secret, + factorId: 'phone', + } as MultiFactorAssertion; } } diff --git a/packages/auth/lib/Settings.js b/packages/auth/lib/Settings.ts similarity index 72% rename from packages/auth/lib/Settings.js rename to packages/auth/lib/Settings.ts index 28f1d44d48..c4c48955a7 100644 --- a/packages/auth/lib/Settings.js +++ b/packages/auth/lib/Settings.ts @@ -16,35 +16,40 @@ */ import { isAndroid } from '@react-native-firebase/app/dist/module/common'; +import type { AuthInternal } from './types/internal'; export default class Settings { - constructor(auth) { + private readonly _auth: AuthInternal; + private _forceRecaptchaFlowForTesting: boolean; + private _appVerificationDisabledForTesting: boolean; + + constructor(auth: AuthInternal) { this._auth = auth; this._forceRecaptchaFlowForTesting = false; this._appVerificationDisabledForTesting = false; } - get forceRecaptchaFlowForTesting() { + get forceRecaptchaFlowForTesting(): boolean { return this._forceRecaptchaFlowForTesting; } - set forceRecaptchaFlowForTesting(forceRecaptchaFlow) { + set forceRecaptchaFlowForTesting(forceRecaptchaFlow: boolean) { if (isAndroid) { this._forceRecaptchaFlowForTesting = forceRecaptchaFlow; this._auth.native.forceRecaptchaFlowForTesting(forceRecaptchaFlow); } } - get appVerificationDisabledForTesting() { + get appVerificationDisabledForTesting(): boolean { return this._appVerificationDisabledForTesting; } - set appVerificationDisabledForTesting(disabled) { + set appVerificationDisabledForTesting(disabled: boolean) { this._appVerificationDisabledForTesting = disabled; this._auth.native.setAppVerificationDisabledForTesting(disabled); } - setAutoRetrievedSmsCodeForPhoneNumber(phoneNumber, smsCode) { + setAutoRetrievedSmsCodeForPhoneNumber(phoneNumber: string, smsCode: string): Promise { if (isAndroid) { return this._auth.native.setAutoRetrievedSmsCodeForPhoneNumber(phoneNumber, smsCode); } diff --git a/packages/auth/lib/TotpMultiFactorGenerator.js b/packages/auth/lib/TotpMultiFactorGenerator.ts similarity index 56% rename from packages/auth/lib/TotpMultiFactorGenerator.js rename to packages/auth/lib/TotpMultiFactorGenerator.ts index 6d8a3561be..d6e540ec61 100644 --- a/packages/auth/lib/TotpMultiFactorGenerator.js +++ b/packages/auth/lib/TotpMultiFactorGenerator.ts @@ -18,6 +18,8 @@ import { isOther } from '@react-native-firebase/app/dist/module/common'; import { TotpSecret } from './TotpSecret'; import { getAuth } from './modular'; +import type { FirebaseAuthTypes } from './types/namespaced'; +import type { AuthInternal } from './types/internal'; export default class TotpMultiFactorGenerator { static FACTOR_ID = 'totp'; @@ -28,20 +30,40 @@ export default class TotpMultiFactorGenerator { ); } - static assertionForSignIn(uid, verificationCode) { + static assertionForSignIn( + uid: string, + verificationCode: string, + ): FirebaseAuthTypes.MultiFactorAssertion { if (isOther) { // we require the web native assertion when using firebase-js-sdk // as it has functions used by the SDK, a shim won't do - return getAuth().native.assertionForSignIn(uid, verificationCode); + return (getAuth() as unknown as AuthInternal).native.assertionForSignIn( + uid, + verificationCode, + ) as unknown as FirebaseAuthTypes.MultiFactorAssertion; } - return { uid, verificationCode }; + return { + uid, + verificationCode, + factorId: 'totp', + } as unknown as FirebaseAuthTypes.MultiFactorAssertion; } - static assertionForEnrollment(totpSecret, verificationCode) { - return { totpSecret: totpSecret.secretKey, verificationCode }; + static assertionForEnrollment( + totpSecret: TotpSecret, + verificationCode: string, + ): FirebaseAuthTypes.MultiFactorAssertion { + return { + totpSecret: totpSecret.secretKey, + verificationCode, + factorId: 'totp', + } as unknown as FirebaseAuthTypes.MultiFactorAssertion; } - static async generateSecret(session, auth) { + static async generateSecret( + session: FirebaseAuthTypes.MultiFactorSession, + auth: FirebaseAuthTypes.Module, + ): Promise { if (!session) { throw new Error('Session is required to generate a TOTP secret.'); } @@ -49,8 +71,8 @@ export default class TotpMultiFactorGenerator { secretKey, // Other properties are not publicly exposed in native APIs // hashingAlgorithm, codeLength, codeIntervalSeconds, enrollmentCompletionDeadline - } = await auth.native.generateTotpSecret(session); + } = await (auth as unknown as AuthInternal).native.generateTotpSecret(session); - return new TotpSecret(secretKey, auth); + return new TotpSecret(secretKey, auth as unknown as AuthInternal); } } diff --git a/packages/auth/lib/TotpSecret.js b/packages/auth/lib/TotpSecret.ts similarity index 87% rename from packages/auth/lib/TotpSecret.js rename to packages/auth/lib/TotpSecret.ts index 027e8d92d0..4f8338f4a6 100644 --- a/packages/auth/lib/TotpSecret.js +++ b/packages/auth/lib/TotpSecret.ts @@ -15,9 +15,13 @@ */ import { isString } from '@react-native-firebase/app/dist/module/common'; +import type { AuthInternal } from './types/internal'; export class TotpSecret { - constructor(secretKey, auth) { + readonly secretKey: string; + private readonly auth: AuthInternal; + + constructor(secretKey: string, auth: AuthInternal) { // The native TotpSecret has many more properties, but they are // internal to the native SDKs, we only maintain the secret in JS layer this.secretKey = secretKey; @@ -29,8 +33,6 @@ export class TotpSecret { /** * Shared secret key/seed used for enrolling in TOTP MFA and generating OTPs. */ - secretKey = null; - /** * Returns a QR code URL as described in * https://github.com/google/google-authenticator/wiki/Key-Uri-Format @@ -41,7 +43,7 @@ export class TotpSecret { * @param issuer issuer of the TOTP (likely the app name). * @returns A Promise that resolves to a QR code URL string. */ - async generateQrCodeUrl(accountName, issuer) { + async generateQrCodeUrl(accountName?: string, issuer?: string): Promise { // accountName and issure are nullable in the API specification but are // required by tha native SDK. The JS SDK returns '' if they are missing/empty. if (!isString(accountName) || !isString(issuer) || accountName === '' || issuer === '') { @@ -59,8 +61,8 @@ export class TotpSecret { * * @param qrCodeUrl the URL to open in the app, from generateQrCodeUrl */ - openInOtpApp(qrCodeUrl) { - if (isString(qrCodeUrl) && !qrCodeUrl !== '') { + openInOtpApp(qrCodeUrl: string): string | void { + if (isString(qrCodeUrl) && qrCodeUrl !== '') { return this.auth.native.openInOtpApp(this.secretKey, qrCodeUrl); } } diff --git a/packages/auth/lib/User.js b/packages/auth/lib/User.ts similarity index 56% rename from packages/auth/lib/User.js rename to packages/auth/lib/User.ts index 3ac5591aed..0affbd3125 100644 --- a/packages/auth/lib/User.js +++ b/packages/auth/lib/User.ts @@ -21,30 +21,40 @@ import { isUndefined, isBoolean, } from '@react-native-firebase/app/dist/module/common'; +import type { IdTokenResult, UserInfo, UserMetadata } from './types/auth'; +import type { FirebaseAuthTypes } from './types/namespaced'; +import type { AuthInternal, NativeUserInternal } from './types/internal'; + +type AuthProviderWithObject = FirebaseAuthTypes.AuthProvider & { + toObject(): Record; +}; export default class User { - constructor(auth, user) { + private readonly _auth: AuthInternal; + private readonly _user: NativeUserInternal; + + constructor(auth: AuthInternal, user: NativeUserInternal) { this._auth = auth; this._user = user; } - get displayName() { + get displayName(): string | null { return this._user.displayName || null; } - get email() { + get email(): string | null { return this._user.email || null; } - get emailVerified() { + get emailVerified(): boolean { return this._user.emailVerified || false; } - get isAnonymous() { + get isAnonymous(): boolean { return this._user.isAnonymous || false; } - get metadata() { + get metadata(): UserMetadata { const { metadata } = this._user; return { @@ -53,148 +63,169 @@ export default class User { }; } - get multiFactor() { + get multiFactor(): FirebaseAuthTypes.MultiFactor | null { return this._user.multiFactor || null; } - get phoneNumber() { + get phoneNumber(): string | null { return this._user.phoneNumber || null; } - get tenantId() { + get tenantId(): string | null { return this._user.tenantId || null; } - get photoURL() { + get photoURL(): string | null { return this._user.photoURL || null; } - get providerData() { - return this._user.providerData; + get providerData(): UserInfo[] { + return this._user.providerData.map(provider => ({ + displayName: provider.displayName ?? null, + email: provider.email ?? null, + phoneNumber: provider.phoneNumber ?? null, + photoURL: provider.photoURL ?? null, + providerId: provider.providerId, + uid: provider.uid, + })); } - get providerId() { + get providerId(): string { return this._user.providerId; } - get uid() { + get uid(): string { return this._user.uid; } - delete() { + delete(): Promise { return this._auth.native.delete().then(() => { - this._auth._setUser(); + this._auth._setUser(null); }); } - getIdToken(forceRefresh = false) { + getIdToken(forceRefresh = false): Promise { return this._auth.native.getIdToken(forceRefresh); } - getIdTokenResult(forceRefresh = false) { - return this._auth.native.getIdTokenResult(forceRefresh); + getIdTokenResult(forceRefresh = false): Promise { + return this._auth.native.getIdTokenResult(forceRefresh) as Promise; } - linkWithCredential(credential) { + linkWithCredential( + credential: FirebaseAuthTypes.AuthCredential, + ): Promise { return this._auth.native .linkWithCredential(credential.providerId, credential.token, credential.secret) - .then(userCredential => this._auth._setUserCredential(userCredential)); + .then( + userCredential => + this._auth._setUserCredential(userCredential) as FirebaseAuthTypes.UserCredential, + ); } - linkWithPopup(provider) { + linkWithPopup(provider: AuthProviderWithObject): Promise { // call through to linkWithRedirect for shared implementation return this.linkWithRedirect(provider); } - linkWithRedirect(provider) { + linkWithRedirect(provider: AuthProviderWithObject): Promise { return this._auth.native .linkWithProvider(provider.toObject()) - .then(userCredential => this._auth._setUserCredential(userCredential)); + .then( + userCredential => + this._auth._setUserCredential(userCredential) as FirebaseAuthTypes.UserCredential, + ); } - reauthenticateWithCredential(credential) { + reauthenticateWithCredential( + credential: FirebaseAuthTypes.AuthCredential, + ): Promise { return this._auth.native .reauthenticateWithCredential(credential.providerId, credential.token, credential.secret) - .then(userCredential => this._auth._setUserCredential(userCredential)); + .then( + userCredential => + this._auth._setUserCredential(userCredential) as FirebaseAuthTypes.UserCredential, + ); } - reauthenticateWithPopup(provider) { + reauthenticateWithPopup( + provider: AuthProviderWithObject, + ): Promise { // call through to reauthenticateWithRedirect for shared implementation - return this.reauthenticateWithRedirect(provider); - } - - reauthenticateWithRedirect(provider) { return this._auth.native .reauthenticateWithProvider(provider.toObject()) - .then(userCredential => this._auth._setUserCredential(userCredential)); + .then( + userCredential => + this._auth._setUserCredential(userCredential) as FirebaseAuthTypes.UserCredential, + ); + } + + reauthenticateWithRedirect(provider: AuthProviderWithObject): Promise { + return this._auth.native.reauthenticateWithProvider(provider.toObject()).then(() => {}); } - reload() { + reload(): Promise { return this._auth.native.reload().then(user => { this._auth._setUser(user); }); } - sendEmailVerification(actionCodeSettings) { + sendEmailVerification(actionCodeSettings?: FirebaseAuthTypes.ActionCodeSettings): Promise { if (isObject(actionCodeSettings)) { - if (!isString(actionCodeSettings.url)) { + const settings = actionCodeSettings as FirebaseAuthTypes.ActionCodeSettings; + + if (!isString(settings.url)) { throw new Error( "firebase.auth.User.sendEmailVerification(*) 'actionCodeSettings.url' expected a string value.", ); } - if (!isUndefined(actionCodeSettings.linkDomain) && !isString(actionCodeSettings.linkDomain)) { + if (!isUndefined(settings.linkDomain) && !isString(settings.linkDomain)) { throw new Error( "firebase.auth.User.sendEmailVerification(*) 'actionCodeSettings.linkDomain' expected a string value.", ); } - if ( - !isUndefined(actionCodeSettings.handleCodeInApp) && - !isBoolean(actionCodeSettings.handleCodeInApp) - ) { + if (!isUndefined(settings.handleCodeInApp) && !isBoolean(settings.handleCodeInApp)) { throw new Error( "firebase.auth.User.sendEmailVerification(*) 'actionCodeSettings.handleCodeInApp' expected a boolean value.", ); } - if (!isUndefined(actionCodeSettings.iOS)) { - if (!isObject(actionCodeSettings.iOS)) { + if (!isUndefined(settings.iOS)) { + if (!isObject(settings.iOS)) { throw new Error( "firebase.auth.User.sendEmailVerification(*) 'actionCodeSettings.iOS' expected an object value.", ); } - if (!isString(actionCodeSettings.iOS.bundleId)) { + if (!isString(settings.iOS.bundleId)) { throw new Error( "firebase.auth.User.sendEmailVerification(*) 'actionCodeSettings.iOS.bundleId' expected a string value.", ); } } - if (!isUndefined(actionCodeSettings.android)) { - if (!isObject(actionCodeSettings.android)) { + if (!isUndefined(settings.android)) { + if (!isObject(settings.android)) { throw new Error( "firebase.auth.User.sendEmailVerification(*) 'actionCodeSettings.android' expected an object value.", ); } - if (!isString(actionCodeSettings.android.packageName)) { + if (!isString(settings.android.packageName)) { throw new Error( "firebase.auth.User.sendEmailVerification(*) 'actionCodeSettings.android.packageName' expected a string value.", ); } - if ( - !isUndefined(actionCodeSettings.android.installApp) && - !isBoolean(actionCodeSettings.android.installApp) - ) { + if (!isUndefined(settings.android.installApp) && !isBoolean(settings.android.installApp)) { throw new Error( "firebase.auth.User.sendEmailVerification(*) 'actionCodeSettings.android.installApp' expected a boolean value.", ); } if ( - !isUndefined(actionCodeSettings.android.minimumVersion) && - !isString(actionCodeSettings.android.minimumVersion) + !isUndefined(settings.android.minimumVersion) && + !isString(settings.android.minimumVersion) ) { throw new Error( "firebase.auth.User.sendEmailVerification(*) 'actionCodeSettings.android.minimumVersion' expected a string value.", @@ -204,31 +235,39 @@ export default class User { } return this._auth.native.sendEmailVerification(actionCodeSettings).then(user => { - this._auth._setUser(user); - }); + this._auth._setUser(user); + }); } - toJSON() { + toJSON(): object { return Object.assign({}, this._user); } - unlink(providerId) { - return this._auth.native.unlink(providerId).then(user => this._auth._setUser(user)); + unlink(providerId: string): Promise { + return this._auth.native + .unlink(providerId) + .then(user => { + const updatedUser = this._auth._setUser(user); + if (!updatedUser) { + throw new Error('firebase.auth.User.unlink() returned no user after unlinking provider.'); + } + return updatedUser; + }); } - updateEmail(email) { + updateEmail(email: string): Promise { return this._auth.native.updateEmail(email).then(user => { this._auth._setUser(user); }); } - updatePassword(password) { + updatePassword(password: string): Promise { return this._auth.native.updatePassword(password).then(user => { this._auth._setUser(user); }); } - updatePhoneNumber(credential) { + updatePhoneNumber(credential: FirebaseAuthTypes.AuthCredential): Promise { return this._auth.native .updatePhoneNumber(credential.providerId, credential.token, credential.secret) .then(user => { @@ -236,13 +275,16 @@ export default class User { }); } - updateProfile(updates) { + updateProfile(updates: FirebaseAuthTypes.UpdateProfile): Promise { return this._auth.native.updateProfile(updates).then(user => { this._auth._setUser(user); }); } - verifyBeforeUpdateEmail(newEmail, actionCodeSettings) { + verifyBeforeUpdateEmail( + newEmail: string, + actionCodeSettings?: FirebaseAuthTypes.ActionCodeSettings, + ): Promise { if (!isString(newEmail)) { throw new Error( "firebase.auth.User.verifyBeforeUpdateEmail(*) 'newEmail' expected a string value.", @@ -250,64 +292,60 @@ export default class User { } if (isObject(actionCodeSettings)) { - if (!isString(actionCodeSettings.url)) { + const settings = actionCodeSettings as FirebaseAuthTypes.ActionCodeSettings; + + if (!isString(settings.url)) { throw new Error( "firebase.auth.User.verifyBeforeUpdateEmail(_, *) 'actionCodeSettings.url' expected a string value.", ); } - if (!isUndefined(actionCodeSettings.linkDomain) && !isString(actionCodeSettings.linkDomain)) { + if (!isUndefined(settings.linkDomain) && !isString(settings.linkDomain)) { throw new Error( "firebase.auth.User.verifyBeforeUpdateEmail(_, *) 'actionCodeSettings.linkDomain' expected a string value.", ); } - if ( - !isUndefined(actionCodeSettings.handleCodeInApp) && - !isBoolean(actionCodeSettings.handleCodeInApp) - ) { + if (!isUndefined(settings.handleCodeInApp) && !isBoolean(settings.handleCodeInApp)) { throw new Error( "firebase.auth.User.verifyBeforeUpdateEmail(_, *) 'actionCodeSettings.handleCodeInApp' expected a boolean value.", ); } - if (!isUndefined(actionCodeSettings.iOS)) { - if (!isObject(actionCodeSettings.iOS)) { + if (!isUndefined(settings.iOS)) { + if (!isObject(settings.iOS)) { throw new Error( "firebase.auth.User.verifyBeforeUpdateEmail(_, *) 'actionCodeSettings.iOS' expected an object value.", ); } - if (!isString(actionCodeSettings.iOS.bundleId)) { + if (!isString(settings.iOS.bundleId)) { throw new Error( "firebase.auth.User.verifyBeforeUpdateEmail(_, *) 'actionCodeSettings.iOS.bundleId' expected a string value.", ); } } - if (!isUndefined(actionCodeSettings.android)) { - if (!isObject(actionCodeSettings.android)) { + if (!isUndefined(settings.android)) { + if (!isObject(settings.android)) { throw new Error( "firebase.auth.User.verifyBeforeUpdateEmail(_, *) 'actionCodeSettings.android' expected an object value.", ); } - if (!isString(actionCodeSettings.android.packageName)) { + if (!isString(settings.android.packageName)) { throw new Error( "firebase.auth.User.verifyBeforeUpdateEmail(_, *) 'actionCodeSettings.android.packageName' expected a string value.", ); } - if ( - !isUndefined(actionCodeSettings.android.installApp) && - !isBoolean(actionCodeSettings.android.installApp) - ) { + if (!isUndefined(settings.android.installApp) && !isBoolean(settings.android.installApp)) { throw new Error( "firebase.auth.User.verifyBeforeUpdateEmail(_, *) 'actionCodeSettings.android.installApp' expected a boolean value.", ); } if ( - !isUndefined(actionCodeSettings.android.minimumVersion) && - !isString(actionCodeSettings.android.minimumVersion) + !isUndefined(settings.android.minimumVersion) && + !isString(settings.android.minimumVersion) ) { throw new Error( "firebase.auth.User.verifyBeforeUpdateEmail(_, *) 'actionCodeSettings.android.minimumVersion' expected a string value.", @@ -317,27 +355,27 @@ export default class User { } return this._auth.native.verifyBeforeUpdateEmail(newEmail, actionCodeSettings).then(user => { - this._auth._setUser(user); - }); + this._auth._setUser(user); + }); } /** * KNOWN UNSUPPORTED METHODS */ - linkWithPhoneNumber() { + linkWithPhoneNumber(): never { throw new Error( 'firebase.auth.User.linkWithPhoneNumber() is unsupported by the native Firebase SDKs.', ); } - reauthenticateWithPhoneNumber() { + reauthenticateWithPhoneNumber(): never { throw new Error( 'firebase.auth.User.reauthenticateWithPhoneNumber() is unsupported by the native Firebase SDKs.', ); } - get refreshToken() { + get refreshToken(): never { throw new Error('firebase.auth.User.refreshToken is unsupported by the native Firebase SDKs.'); } } diff --git a/packages/auth/lib/getMultiFactorResolver.js b/packages/auth/lib/getMultiFactorResolver.js deleted file mode 100644 index 70c3a53265..0000000000 --- a/packages/auth/lib/getMultiFactorResolver.js +++ /dev/null @@ -1,23 +0,0 @@ -import { isOther } from '@react-native-firebase/app/dist/module/common'; -import MultiFactorResolver from './MultiFactorResolver.js'; - -/** - * Create a new resolver based on an auth instance and error - * object. - * - * Returns null if no resolver object can be found on the error. - */ -export function getMultiFactorResolver(auth, error) { - if (isOther) { - return auth.native.getMultiFactorResolver(error); - } - if ( - error.hasOwnProperty('userInfo') && - error.userInfo.hasOwnProperty('resolver') && - error.userInfo.resolver - ) { - return new MultiFactorResolver(auth, error.userInfo.resolver); - } - - return null; -} diff --git a/packages/auth/lib/getMultiFactorResolver.ts b/packages/auth/lib/getMultiFactorResolver.ts new file mode 100644 index 0000000000..c5b4205264 --- /dev/null +++ b/packages/auth/lib/getMultiFactorResolver.ts @@ -0,0 +1,40 @@ +import { isOther } from '@react-native-firebase/app/dist/module/common'; +import MultiFactorResolver from './MultiFactorResolver'; +import type { FirebaseAuthTypes } from './types/namespaced'; +import type { AuthInternal } from './types/internal'; + +type ErrorWithResolver = FirebaseAuthTypes.MultiFactorError & { + userInfo?: { + resolver?: { + hints: FirebaseAuthTypes.MultiFactorResolver['hints']; + session: FirebaseAuthTypes.MultiFactorResolver['session']; + }; + }; +}; + +/** + * Create a new resolver based on an auth instance and error + * object. + * + * Returns null if no resolver object can be found on the error. + */ +export function getMultiFactorResolver( + auth: AuthInternal, + error: ErrorWithResolver, +): FirebaseAuthTypes.MultiFactorResolver | null { + if (isOther) { + return auth.native.getMultiFactorResolver(error) as FirebaseAuthTypes.MultiFactorResolver | null; + } + if ( + error.hasOwnProperty('userInfo') && + error.userInfo?.hasOwnProperty('resolver') && + error.userInfo.resolver + ) { + return new MultiFactorResolver( + auth, + error.userInfo.resolver, + ) as unknown as FirebaseAuthTypes.MultiFactorResolver; + } + + return null; +} diff --git a/packages/auth/lib/index.ts b/packages/auth/lib/index.ts new file mode 100644 index 0000000000..37b41812c2 --- /dev/null +++ b/packages/auth/lib/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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. + * + */ +// modular API +export type * from './types/auth'; +export * from './modular'; + +// namespaced API +export * from './types/namespaced'; +export * from './namespaced'; +export { default } from './namespaced'; diff --git a/packages/auth/lib/modular.ts b/packages/auth/lib/modular.ts new file mode 100644 index 0000000000..4b319c7538 --- /dev/null +++ b/packages/auth/lib/modular.ts @@ -0,0 +1,845 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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 { getApp } from '@react-native-firebase/app'; +import { MODULAR_DEPRECATION_ARG } from '@react-native-firebase/app/dist/module/common'; +import PhoneMultiFactorGenerator from './PhoneMultiFactorGenerator'; +import { PhoneAuthState } from './PhoneAuthState'; +import TotpMultiFactorGenerator from './TotpMultiFactorGenerator'; +import { TotpSecret } from './TotpSecret'; +import { MultiFactorUser as MultiFactorUserModule } from './multiFactor'; +import AppleAuthProvider from './providers/AppleAuthProvider'; +import EmailAuthProvider from './providers/EmailAuthProvider'; +import FacebookAuthProvider from './providers/FacebookAuthProvider'; +import GithubAuthProvider from './providers/GithubAuthProvider'; +import GoogleAuthProvider from './providers/GoogleAuthProvider'; +import OAuthProvider from './providers/OAuthProvider'; +import OIDCAuthProvider from './providers/OIDCAuthProvider'; +import PhoneAuthProvider from './providers/PhoneAuthProvider'; +import TwitterAuthProvider from './providers/TwitterAuthProvider'; +import type { FirebaseApp } from '@react-native-firebase/app'; +import { ActionCodeOperation, FactorId, OperationType, ProviderId } from './types/auth'; +import type { + ActionCodeInfo, + ActionCodeSettings, + ActionCodeURL, + AdditionalUserInfo, + ApplicationVerifier, + Auth, + AuthCredential, + AuthProvider, + CompleteFn, + ConfirmationResult, + Dependencies, + ErrorFn, + IdTokenResult, + MultiFactorError, + MultiFactorInfo, + MultiFactorResolver, + MultiFactorUser, + NextOrObserver, + PasswordValidationStatus, + Persistence, + PhoneMultiFactorInfo, + PhoneAuthListener, + PopupRedirectResolver, + TotpMultiFactorInfo, + Unsubscribe, + User, + UserCredential, +} from './types/auth'; +import type { + ActionCodeInfoResultInternal, + AppWithAuthInternal, + AuthInternal, + AuthListenerCallbackInternal, + AuthProviderWithObjectInternal, + ConfirmationResultResultInternal, + MultiFactorResolverResultInternal, + MultiFactorUserResultInternal, + MultiFactorUserSourceInternal, + UserCredentialResultInternal, + UserInternal, + WithAuthDeprecationArg, +} from './types/internal'; + +type AnyFn = (...args: any[]) => any; + +type UserModuleInternal = UserInternal; +type MultiFactorInfoInternal = + | MultiFactorInfo + | MultiFactorResolverResultInternal['hints'][number]; + +const actionCodeOperations = new Set(Object.values(ActionCodeOperation)); + +function appWithAuth(app?: FirebaseApp): AppWithAuthInternal { + return (app ? getApp(app.name) : getApp()) as unknown as AppWithAuthInternal; +} + +function getAuthInternal(auth: Auth): AuthInternal { + return auth as unknown as AuthInternal; +} + +function getUserInternal(user: User): UserModuleInternal { + return user as unknown as UserModuleInternal; +} + +function normalizeUserCredential( + userCredential: UserCredentialResultInternal, + overrides: Partial> = {}, +): UserCredential { + const normalizedUserCredential: UserCredential = { + user: userCredential.user as unknown as User, + providerId: + overrides.providerId ?? + userCredential.providerId ?? + userCredential.additionalUserInfo?.providerId ?? + null, + operationType: overrides.operationType ?? userCredential.operationType ?? OperationType.SIGN_IN, + }; + + if (userCredential.additionalUserInfo) { + Object.defineProperty(normalizedUserCredential, 'additionalUserInfo', { + value: userCredential.additionalUserInfo, + enumerable: false, + configurable: true, + }); + } + + return normalizedUserCredential; +} + +function normalizeActionCodeOperation(operation: string): ActionCodeInfo['operation'] { + if (actionCodeOperations.has(operation)) { + return operation as ActionCodeInfo['operation']; + } + + // Native auth may still surface the legacy 'ERROR' sentinel even though the modular public + // type does not model it. Preserve the native value instead of turning it into a new throw. + return operation as ActionCodeInfo['operation']; +} + +function normalizeMultiFactorInfo(info: MultiFactorInfoInternal): MultiFactorInfo { + const normalizedInfo = { + uid: info.uid, + displayName: info.displayName ?? null, + enrollmentTime: info.enrollmentTime, + factorId: info.factorId, + }; + + if ('phoneNumber' in info) { + return { + ...normalizedInfo, + phoneNumber: info.phoneNumber, + } as PhoneMultiFactorInfo; + } + + return normalizedInfo as TotpMultiFactorInfo; +} + +function normalizeActionCodeInfo(actionCodeInfo: ActionCodeInfoResultInternal): ActionCodeInfo { + const data = actionCodeInfo.data ?? {}; + + return { + data: { + email: data.email ?? null, + multiFactorInfo: + 'multiFactorInfo' in data && data.multiFactorInfo + ? normalizeMultiFactorInfo(data.multiFactorInfo) + : null, + previousEmail: + ('previousEmail' in data ? data.previousEmail : undefined) ?? + ('fromEmail' in data ? data.fromEmail : undefined) ?? + null, + }, + operation: normalizeActionCodeOperation(actionCodeInfo.operation), + }; +} + +function normalizeConfirmationResult( + confirmationResult: ConfirmationResultResultInternal, +): ConfirmationResult { + if (!confirmationResult.verificationId) { + throw new Error('signInWithPhoneNumber() did not return a verificationId.'); + } + + return { + verificationId: confirmationResult.verificationId, + async confirm(verificationCode: string) { + const userCredential = await confirmationResult.confirm(verificationCode); + + if (!userCredential) { + throw new Error('signInWithPhoneNumber().confirm() returned no user credential.'); + } + + return normalizeUserCredential(userCredential, { + providerId: ProviderId.PHONE, + operationType: OperationType.SIGN_IN, + }); + }, + }; +} + +function normalizeMultiFactorResolver( + resolver: MultiFactorResolverResultInternal, +): MultiFactorResolver { + return { + hints: resolver.hints.map(normalizeMultiFactorInfo), + session: resolver.session, + async resolveSignIn(assertion) { + return normalizeUserCredential(await resolver.resolveSignIn(assertion), { + providerId: assertion.factorId === FactorId.PHONE ? ProviderId.PHONE : null, + operationType: OperationType.SIGN_IN, + }); + }, + }; +} + +function normalizeMultiFactorUser(multiFactorUser: MultiFactorUserResultInternal): MultiFactorUser { + return { + enrolledFactors: multiFactorUser.enrolledFactors.map(normalizeMultiFactorInfo), + getSession: () => multiFactorUser.getSession(), + enroll: (assertion, displayName) => multiFactorUser.enroll(assertion, displayName), + unenroll: option => + multiFactorUser.unenroll( + option as Parameters[0], + ), + }; +} + +export { + AppleAuthProvider, + EmailAuthProvider, + FacebookAuthProvider, + GithubAuthProvider, + GoogleAuthProvider, + OAuthProvider, + OIDCAuthProvider, + PhoneAuthProvider, + PhoneAuthState, + PhoneMultiFactorGenerator, + TotpMultiFactorGenerator, + TotpSecret, + TwitterAuthProvider, +}; + +function normalizeAuthListener( + nextOrObserver: NextOrObserver, +): AuthListenerCallbackInternal | { next: AuthListenerCallbackInternal } { + if (typeof nextOrObserver === 'function') { + return nextOrObserver as AuthListenerCallbackInternal; + } + + return { + next: + typeof nextOrObserver.next === 'function' + ? (nextOrObserver.next as AuthListenerCallbackInternal) + : () => {}, + }; +} + +function callAuthMethod( + auth: AuthInternal, + method: F, + ...args: Parameters +): ReturnType { + return (method as unknown as WithAuthDeprecationArg).call( + auth, + ...args, + MODULAR_DEPRECATION_ARG, + ); +} + +function callUserMethod( + user: UserModuleInternal, + method: F, + ...args: Parameters +): ReturnType { + return (method as unknown as WithAuthDeprecationArg).call( + user, + ...args, + MODULAR_DEPRECATION_ARG, + ); +} + +/** + * Returns the Auth instance associated with the provided FirebaseApp. + */ +export function getAuth(app?: FirebaseApp): Auth { + return appWithAuth(app).auth(MODULAR_DEPRECATION_ARG); +} + +/** + * This function allows more control over the Auth instance than getAuth(). + */ +export function initializeAuth(app: FirebaseApp, _deps?: Dependencies): Auth { + return appWithAuth(app).auth(MODULAR_DEPRECATION_ARG); +} + +export function applyActionCode(auth: Auth, oobCode: string): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod(authInternal, authInternal.applyActionCode, oobCode); +} + +export function beforeAuthStateChanged( + _auth: Auth, + _callback: (user: User | null) => void | Promise, + _onAbort?: () => void, +): Unsubscribe { + throw new Error('beforeAuthStateChanged is unsupported by the native Firebase SDKs'); +} + +export function checkActionCode(auth: Auth, oobCode: string): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod(authInternal, authInternal.checkActionCode, oobCode).then( + normalizeActionCodeInfo, + ); +} + +export function confirmPasswordReset( + auth: Auth, + oobCode: string, + newPassword: string, +): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod(authInternal, authInternal.confirmPasswordReset, oobCode, newPassword); +} + +export function connectAuthEmulator( + auth: Auth, + url: string, + _options?: { disableWarnings?: boolean }, +): void { + const authInternal = getAuthInternal(auth); + callAuthMethod(authInternal, authInternal.useEmulator, url); +} + +export function createUserWithEmailAndPassword( + auth: Auth, + email: string, + password: string, +): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod(authInternal, authInternal.createUserWithEmailAndPassword, email, password).then( + userCredential => + normalizeUserCredential(userCredential, { + operationType: OperationType.SIGN_IN, + }), + ); +} + +export function fetchSignInMethodsForEmail(auth: Auth, email: string): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod(authInternal, authInternal.fetchSignInMethodsForEmail, email); +} + +export function getMultiFactorResolver(auth: Auth, error: MultiFactorError): MultiFactorResolver { + const authInternal = getAuthInternal(auth); + const resolver = callAuthMethod( + authInternal, + authInternal.getMultiFactorResolver, + error, + ) as MultiFactorResolverResultInternal | null; + + if (!resolver) { + throw new Error('The provided auth error did not contain a multi-factor resolver.'); + } + + return normalizeMultiFactorResolver(resolver); +} + +export function getRedirectResult( + _auth: Auth, + _resolver?: PopupRedirectResolver, +): Promise { + throw new Error('getRedirectResult is unsupported by the native Firebase SDKs'); +} + +export function isSignInWithEmailLink(auth: Auth, emailLink: string): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod(authInternal, authInternal.isSignInWithEmailLink, emailLink); +} + +export function onAuthStateChanged( + auth: Auth, + nextOrObserver: NextOrObserver, + _error?: ErrorFn, + _completed?: CompleteFn, +): Unsubscribe { + // The legacy callback overload exists for JS SDK compatibility, but native auth listeners + // never invoke separate error/completed callbacks. + const authInternal = getAuthInternal(auth); + return callAuthMethod( + authInternal, + authInternal.onAuthStateChanged, + normalizeAuthListener(nextOrObserver), + ); +} + +export function onIdTokenChanged( + auth: Auth, + nextOrObserver: NextOrObserver, + _error?: ErrorFn, + _completed?: CompleteFn, +): Unsubscribe { + // The legacy callback overload exists for JS SDK compatibility, but native auth listeners + // never invoke separate error/completed callbacks. + const authInternal = getAuthInternal(auth); + return callAuthMethod( + authInternal, + authInternal.onIdTokenChanged, + normalizeAuthListener(nextOrObserver), + ); +} + +export function revokeAccessToken(_auth: Auth, _token: string): Promise { + throw new Error('revokeAccessToken() is only supported on Web'); +} + +export function sendPasswordResetEmail( + auth: Auth, + email: string, + actionCodeSettings?: ActionCodeSettings, +): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod( + authInternal, + authInternal.sendPasswordResetEmail, + email, + actionCodeSettings, + ); +} + +export function sendSignInLinkToEmail( + auth: Auth, + email: string, + actionCodeSettings: ActionCodeSettings, +): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod( + authInternal, + authInternal.sendSignInLinkToEmail, + email, + actionCodeSettings, + ); +} + +export function setPersistence(_auth: Auth, _persistence: Persistence): Promise { + throw new Error('setPersistence is unsupported by the native Firebase SDKs'); +} + +export function signInAnonymously(auth: Auth): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod(authInternal, authInternal.signInAnonymously).then(userCredential => + normalizeUserCredential(userCredential, { + operationType: OperationType.SIGN_IN, + }), + ); +} + +export function signInWithCredential( + auth: Auth, + credential: AuthCredential, +): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod(authInternal, authInternal.signInWithCredential, credential).then( + userCredential => + normalizeUserCredential(userCredential, { + providerId: credential.providerId, + operationType: OperationType.SIGN_IN, + }), + ); +} + +export function signInWithCustomToken(auth: Auth, customToken: string): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod(authInternal, authInternal.signInWithCustomToken, customToken).then( + userCredential => + normalizeUserCredential(userCredential, { + operationType: OperationType.SIGN_IN, + }), + ); +} + +export function signInWithEmailAndPassword( + auth: Auth, + email: string, + password: string, +): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod(authInternal, authInternal.signInWithEmailAndPassword, email, password).then( + userCredential => + normalizeUserCredential(userCredential, { + operationType: OperationType.SIGN_IN, + }), + ); +} + +export function signInWithEmailLink( + auth: Auth, + email: string, + emailLink: string, +): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod(authInternal, authInternal.signInWithEmailLink, email, emailLink).then( + userCredential => + normalizeUserCredential(userCredential, { + providerId: ProviderId.PASSWORD, + operationType: OperationType.SIGN_IN, + }), + ); +} + +export function signInWithPhoneNumber( + auth: Auth, + phoneNumber: string, + _appVerifier?: ApplicationVerifier, +): Promise { + // Native SDKs own the verification flow, so the modular wrapper intentionally ignores the + // JS SDK's optional ApplicationVerifier and forwards only the phone number. + const authInternal = getAuthInternal(auth); + return callAuthMethod(authInternal, authInternal.signInWithPhoneNumber, phoneNumber).then( + normalizeConfirmationResult, + ); +} + +export function verifyPhoneNumber( + auth: Auth, + phoneNumber: string, + autoVerifyTimeoutOrForceResend?: number | boolean, + forceResend?: boolean, +): PhoneAuthListener { + const authInternal = getAuthInternal(auth); + return callAuthMethod( + authInternal, + authInternal.verifyPhoneNumber, + phoneNumber, + autoVerifyTimeoutOrForceResend, + forceResend, + ); +} + +export function signInWithPopup( + auth: Auth, + provider: AuthProvider, + _resolver?: PopupRedirectResolver, +): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod( + authInternal, + authInternal.signInWithPopup, + provider as unknown as AuthProviderWithObjectInternal, + ).then(userCredential => + normalizeUserCredential(userCredential, { + providerId: provider.providerId, + operationType: OperationType.SIGN_IN, + }), + ); +} + +export function signInWithRedirect( + auth: Auth, + provider: AuthProvider, + _resolver?: PopupRedirectResolver, +): Promise { + // Native provider flows complete immediately and return a credential instead of following the + // browser redirect contract from the Firebase JS SDK. + const authInternal = getAuthInternal(auth); + return callAuthMethod( + authInternal, + authInternal.signInWithRedirect, + provider as unknown as AuthProviderWithObjectInternal, + ).then(userCredential => + normalizeUserCredential(userCredential, { + providerId: provider.providerId, + operationType: OperationType.SIGN_IN, + }), + ); +} + +export function signOut(auth: Auth): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod(authInternal, authInternal.signOut); +} + +export function updateCurrentUser(_auth: Auth, _user: User | null): Promise { + throw new Error('updateCurrentUser is unsupported by the native Firebase SDKs'); +} + +export function useDeviceLanguage(_auth: Auth): void { + throw new Error('useDeviceLanguage is unsupported by the native Firebase SDKs'); +} + +export function setLanguageCode(auth: Auth, languageCode: string | null): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod(authInternal, authInternal.setLanguageCode, languageCode); +} + +export function useUserAccessGroup(auth: Auth, userAccessGroup: string): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod(authInternal, authInternal.useUserAccessGroup, userAccessGroup).then( + () => undefined, + ); +} + +export function verifyPasswordResetCode(auth: Auth, code: string): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod(authInternal, authInternal.verifyPasswordResetCode, code); +} + +export function parseActionCodeURL(_link: string): ActionCodeURL | null { + throw new Error('parseActionCodeURL is unsupported by the native Firebase SDKs'); +} + +export function deleteUser(user: User): Promise { + const userInternal = getUserInternal(user); + return callUserMethod(userInternal, userInternal.delete); +} + +export function getIdToken(user: User, forceRefresh?: boolean): Promise { + const userInternal = getUserInternal(user); + return callUserMethod(userInternal, userInternal.getIdToken, forceRefresh); +} + +export function getIdTokenResult(user: User, forceRefresh?: boolean): Promise { + const userInternal = getUserInternal(user); + return callUserMethod(userInternal, userInternal.getIdTokenResult, forceRefresh); +} + +export function linkWithCredential( + user: User, + credential: AuthCredential, +): Promise { + const userInternal = getUserInternal(user); + return callUserMethod(userInternal, userInternal.linkWithCredential, credential).then( + userCredential => + normalizeUserCredential(userCredential, { + providerId: credential.providerId, + operationType: OperationType.LINK, + }), + ); +} + +export function linkWithPhoneNumber( + _user: User, + _phoneNumber: string, + _appVerifier?: ApplicationVerifier, +): Promise { + throw new Error('linkWithPhoneNumber is unsupported by the native Firebase SDKs'); +} + +export function linkWithPopup( + user: User, + provider: AuthProvider, + _resolver?: PopupRedirectResolver, +): Promise { + const userInternal = getUserInternal(user); + return callUserMethod( + userInternal, + userInternal.linkWithPopup, + provider as unknown as AuthProviderWithObjectInternal, + ).then(userCredential => + normalizeUserCredential(userCredential, { + providerId: provider.providerId, + operationType: OperationType.LINK, + }), + ); +} + +export function linkWithRedirect( + user: User, + provider: AuthProvider, + _resolver?: PopupRedirectResolver, +): Promise { + // Native provider flows complete immediately and return a credential instead of following the + // browser redirect contract from the Firebase JS SDK. + const userInternal = getUserInternal(user); + return callUserMethod( + userInternal, + userInternal.linkWithRedirect, + provider as unknown as AuthProviderWithObjectInternal, + ).then(userCredential => + normalizeUserCredential(userCredential, { + providerId: provider.providerId, + operationType: OperationType.LINK, + }), + ); +} + +export function multiFactor(user: User): MultiFactorUser { + return normalizeMultiFactorUser( + new MultiFactorUserModule( + ((user as unknown as UserInternal)._auth || + (getAuth() as unknown as UserInternal['_auth'])) as NonNullable, + user as unknown as MultiFactorUserSourceInternal, + ), + ); +} + +export function reauthenticateWithCredential( + user: User, + credential: AuthCredential, +): Promise { + const userInternal = getUserInternal(user); + return callUserMethod(userInternal, userInternal.reauthenticateWithCredential, credential).then( + userCredential => + normalizeUserCredential(userCredential, { + providerId: credential.providerId, + operationType: OperationType.REAUTHENTICATE, + }), + ); +} + +export function reauthenticateWithPhoneNumber( + _user: User, + _phoneNumber: string, + _appVerifier?: ApplicationVerifier, +): Promise { + throw new Error('reauthenticateWithPhoneNumber is unsupported by the native Firebase SDKs'); +} + +export function reauthenticateWithPopup( + user: User, + provider: AuthProvider, + _resolver?: PopupRedirectResolver, +): Promise { + const userInternal = getUserInternal(user); + return callUserMethod( + userInternal, + userInternal.reauthenticateWithPopup, + provider as unknown as AuthProviderWithObjectInternal, + ).then(userCredential => + normalizeUserCredential(userCredential, { + providerId: provider.providerId, + operationType: OperationType.REAUTHENTICATE, + }), + ); +} + +export function reauthenticateWithRedirect( + user: User, + provider: AuthProvider, + _resolver?: PopupRedirectResolver, +): Promise { + const userInternal = getUserInternal(user); + return callUserMethod( + userInternal, + userInternal.reauthenticateWithRedirect, + provider as unknown as AuthProviderWithObjectInternal, + ); +} + +export function reload(user: User): Promise { + const userInternal = getUserInternal(user); + return callUserMethod(userInternal, userInternal.reload); +} + +export function sendEmailVerification( + user: User, + actionCodeSettings?: ActionCodeSettings | null, +): Promise { + const userInternal = getUserInternal(user); + return callUserMethod( + userInternal, + userInternal.sendEmailVerification, + actionCodeSettings ?? undefined, + ); +} + +export function unlink(user: User, providerId: string): Promise { + const userInternal = getUserInternal(user); + return callUserMethod(userInternal, userInternal.unlink, providerId); +} + +export function updateEmail(user: User, newEmail: string): Promise { + const userInternal = getUserInternal(user); + return callUserMethod(userInternal, userInternal.updateEmail, newEmail); +} + +export function updatePassword(user: User, newPassword: string): Promise { + const userInternal = getUserInternal(user); + return callUserMethod(userInternal, userInternal.updatePassword, newPassword); +} + +export function updatePhoneNumber(user: User, credential: AuthCredential): Promise { + const userInternal = getUserInternal(user); + return callUserMethod(userInternal, userInternal.updatePhoneNumber, credential); +} + +export function updateProfile( + user: User, + profile: { displayName?: string | null; photoURL?: string | null }, +): Promise { + const userInternal = getUserInternal(user); + return callUserMethod(userInternal, userInternal.updateProfile, { + displayName: profile.displayName, + photoURL: profile.photoURL, + }); +} + +export function verifyBeforeUpdateEmail( + user: User, + newEmail: string, + actionCodeSettings?: ActionCodeSettings | null, +): Promise { + const userInternal = getUserInternal(user); + return callUserMethod( + userInternal, + userInternal.verifyBeforeUpdateEmail, + newEmail, + actionCodeSettings ?? undefined, + ); +} + +export function getAdditionalUserInfo(userCredential: UserCredential): AdditionalUserInfo | null { + const info = (userCredential as unknown as UserCredentialResultInternal).additionalUserInfo; + if (!info) { + return null; + } + + return { + isNewUser: info.isNewUser, + profile: info.profile ?? null, + providerId: info.providerId ?? null, + username: info.username ?? null, + }; +} + +export function getCustomAuthDomain(auth: Auth): Promise { + const authInternal = getAuthInternal(auth); + return callAuthMethod(authInternal, authInternal.getCustomAuthDomain); +} + +export function validatePassword(auth: Auth, password: string): Promise { + if (!auth || !('app' in auth)) { + throw new Error( + "firebase.auth().validatePassword(*) 'auth' must be a valid Auth instance with an 'app' property. Received: undefined", + ); + } + + if (password === null || password === undefined) { + throw new Error( + "firebase.auth().validatePassword(*) expected 'password' to be a non-null or a defined value.", + ); + } + + const authWithPasswordValidation = getAuthInternal(auth); + return callAuthMethod( + authWithPasswordValidation, + authWithPasswordValidation.validatePassword, + password, + ); +} diff --git a/packages/auth/lib/modular/index.d.ts b/packages/auth/lib/modular/index.d.ts deleted file mode 100644 index edaec52bb2..0000000000 --- a/packages/auth/lib/modular/index.d.ts +++ /dev/null @@ -1,801 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* - * Copyright (c) 2016-present Invertase Limited & Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this library 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 { ReactNativeFirebase } from '@react-native-firebase/app'; -import { FirebaseAuthTypes, CallbackOrObserver } from '../index'; -import { firebase } from '..'; - -import Auth = FirebaseAuthTypes.Module; -import FirebaseApp = ReactNativeFirebase.FirebaseApp; - -/** - * Returns the Auth instance associated with the provided FirebaseApp. - * @param app - The Firebase app instance. - * @returns The Auth instance. - */ -export function getAuth(app?: FirebaseApp): Auth; - -/** - * This function allows more control over the Auth instance than getAuth(). - * - * @param app - The Firebase app instance. - * @param deps - Optional. Dependencies for the Auth instance. - * @returns The Auth instance. - * - * getAuth uses platform-specific defaults to supply the Dependencies. - * In general, getAuth is the easiest way to initialize Auth and works for most use cases. - * Use initializeAuth if you need control over which persistence layer is used, or to minimize bundle size - * if you're not using either signInWithPopup or signInWithRedirect. - */ -export function initializeAuth(app: FirebaseApp, deps?: any): Auth; - -/** - * Applies a verification code sent to the user by email or other out-of-band mechanism. - * - * @param auth - The Auth instance. - * @param oobCode - The out-of-band code sent to the user. - * @returns A promise that resolves when the code is applied successfully. - */ -export function applyActionCode(auth: Auth, oobCode: string): Promise; - -/** - * Adds a blocking callback that runs before an auth state change sets a new user. - * - * @param auth - The Auth instance. - * @param callback - A callback function to run before the auth state changes. - * @param onAbort - Optional. A callback function to run if the operation is aborted. - * - */ -export function beforeAuthStateChanged( - auth: Auth, - callback: (user: FirebaseAuthTypes.User | null) => void, - onAbort?: () => void, -): void; - -/** - * Checks a verification code sent to the user by email or other out-of-band mechanism. - * - * @param auth - The Auth instance. - * @param oobCode - The out-of-band code sent to the user. - * @returns A promise that resolves with the action code information. - */ -export function checkActionCode( - auth: Auth, - oobCode: string, -): Promise; - -/** - * Completes the password reset process, given a confirmation code and new password. - * - * @param auth - The Auth instance. - * @param oobCode - The out-of-band code sent to the user. - * @param newPassword - The new password. - * @returns A promise that resolves when the password is reset. - */ -export function confirmPasswordReset( - auth: Auth, - oobCode: string, - newPassword: string, -): Promise; - -/** - * Changes the Auth instance to communicate with the Firebase Auth Emulator, instead of production Firebase Auth services. - * - * @param auth - The Auth instance. - * @param url - The URL of the Firebase Auth Emulator. - * @param options - Optional. Options for the emulator connection. - * - * This must be called synchronously immediately following the first call to initializeAuth(). Do not use with production credentials as emulator traffic is not encrypted. - */ -export function connectAuthEmulator( - auth: Auth, - url: string, - options?: { disableWarnings: boolean }, -): void; - -/** - * Creates a new user account associated with the specified email address and password. - * - * @param auth - The Auth instance. - * @param email - The user's email address. - * @param password - The user's password. - * @returns A promise that resolves with the user credentials. - */ -export function createUserWithEmailAndPassword( - auth: Auth, - email: string, - password: string, -): Promise; - -/** - * Gets the list of possible sign in methods for the given email address. - * - * @param auth - The Auth instance. - * @param email - The user's email address. - * @returns A promise that resolves with the list of sign-in methods. - */ -export function fetchSignInMethodsForEmail(auth: Auth, email: string): Promise; - -/** - * Provides a MultiFactorResolver suitable for completion of a multi-factor flow. - * - * @param auth - The Auth instance. - * @param error - The multi-factor error. - * @returns The MultiFactorResolver instance. - */ -export function getMultiFactorResolver( - auth: Auth, - error: FirebaseAuthTypes.MultiFactorError, -): FirebaseAuthTypes.MultiFactorResolver; - -/** - * Returns a UserCredential from the redirect-based sign-in flow. - * - * @param auth - The Auth instance. - * @param resolver - Optional. The popup redirect resolver. - * @returns A promise that resolves with the user credentials or null. - */ -export function getRedirectResult( - auth: Auth, - resolver?: PopupRedirectResolver, -): Promise; - -export interface PopupRedirectResolver {} - -/** - * Loads the reCAPTCHA configuration into the Auth instance. - * Does not work in a Node.js environment - * @param auth - The Auth instance. - */ -export function initializeRecaptchaConfig(auth: Auth): Promise; - -/** - * Checks if an incoming link is a sign-in with email link suitable for signInWithEmailLink. - * Note that android and other platforms require `apiKey` link parameter for signInWithEmailLink - * - * @param auth - The Auth instance. - * @param emailLink - The email link to check. - * @returns A promise that resolves if the link is a sign-in with email link. - */ -export function isSignInWithEmailLink(auth: Auth, emailLink: string): Promise; - -/** - * Adds an observer for changes to the user's sign-in state. - * - * @param auth - The Auth instance. - * @param nextOrObserver - A callback function or observer for auth state changes. - * @returns A function to unsubscribe from the auth state changes. - */ -export function onAuthStateChanged( - auth: Auth, - nextOrObserver: CallbackOrObserver, -): () => void; - -/** - * Adds an observer for changes to the signed-in user's ID token. - * - * @param auth - The Auth instance. - * @param nextOrObserver - A callback function or observer for ID token changes. - * @returns A function to unsubscribe from the ID token changes. - */ -export function onIdTokenChanged( - auth: Auth, - nextOrObserver: CallbackOrObserver, -): () => void; - -/** - * Revoke the given access token, Currently only supports Apple OAuth access tokens. - * @param auth - * @param token - */ -export declare function revokeAccessToken(auth: Auth, token: string): Promise; - -/** - * Sends a password reset email to the given email address. - * - * @param auth - The Auth instance. - * @param email - The user's email address. - * @param actionCodeSettings - Optional. Action code settings. - * @returns A promise that resolves when the email is sent. - */ -export function sendPasswordResetEmail( - auth: Auth, - email: string, - actionCodeSettings?: FirebaseAuthTypes.ActionCodeSettings, -): Promise; - -/** - * Sends a sign-in email link to the user with the specified email. - * - * @param auth - The Auth instance. - * @param email - The user's email address. - * @param actionCodeSettings - Optional, Action code settings. - * @returns A promise that resolves when the email is sent. - */ -export function sendSignInLinkToEmail( - auth: Auth, - email: string, - actionCodeSettings?: FirebaseAuthTypes.ActionCodeSettings, -): Promise; - -/** - * Type of Persistence. - * - 'SESSION' is used for temporary persistence such as `sessionStorage`. - * - 'LOCAL' is used for long term persistence such as `localStorage` or `IndexedDB`. - * - 'NONE' is used for in-memory, or no persistence. - */ -export type Persistence = { - readonly type: 'SESSION' | 'LOCAL' | 'NONE'; -}; - -/** - * Changes the type of persistence on the Auth instance for the currently saved Auth session and applies this type of persistence for future sign-in requests, including sign-in with redirect requests. - * - * @param auth - The Auth instance. - * @param persistence - The persistence type. - * @returns A promise that resolves when the persistence is set. - */ -export function setPersistence(auth: Auth, persistence: Persistence): Promise; - -/** - * Asynchronously signs in as an anonymous user. - * - * @param auth - The Auth instance. - * @returns A promise that resolves with the user credentials. - */ -export function signInAnonymously(auth: Auth): Promise; - -/** - * Asynchronously signs in with the given credentials. - * - * @param auth - The Auth instance. - * @param credential - The auth credentials. - * @returns A promise that resolves with the user credentials. - */ -export function signInWithCredential( - auth: Auth, - credential: FirebaseAuthTypes.AuthCredential, -): Promise; - -/** - * Asynchronously signs in using a custom token. - * - * @param auth - The Auth instance. - * @param customToken - The custom token. - * @returns A promise that resolves with the user credentials. - */ -export function signInWithCustomToken( - auth: Auth, - customToken: string, -): Promise; - -/** - * Asynchronously signs in using an email and password. - * - * @param auth - The Auth instance. - * @param email - The user's email address. - * @param password - The user's password. - * @returns A promise that resolves with the user credentials. - */ -export function signInWithEmailAndPassword( - auth: Auth, - email: string, - password: string, -): Promise; - -/** - * Asynchronously signs in using an email and sign-in email link. - * - * @param auth - The Auth instance. - * @param email - The user's email address. - * @param emailLink - The email link. - * @returns A promise that resolves with the user credentials. - */ -export function signInWithEmailLink( - auth: Auth, - email: string, - emailLink: string, -): Promise; - -/** - * Interface representing an application verifier. - */ -export interface ApplicationVerifier { - readonly type: string; - verify(): Promise; -} - -/** - * Asynchronously signs in using a phone number. - * - * @param auth - The Auth instance. - * @param phoneNumber - The user's phone number. - * @param appVerifier - Optional. The application verifier. - * @param forceResend - Optional. (Native only) Forces a new message to be sent if it was already recently sent. - * @returns A promise that resolves with the confirmation result. - */ -export function signInWithPhoneNumber( - auth: Auth, - phoneNumber: string, - appVerifier?: ApplicationVerifier, - forceResend?: boolean, -): Promise; - -/** - * Asynchronously signs in using a phone number. - * - * @param auth - The Auth instance. - * @param phoneNumber - The user's phone number. - * @param autoVerifyTimeoutOrForceResend - The auto verify timeout or force resend flag. - * @param forceResend - Optional. Whether to force resend. - * @returns A promise that resolves with the phone auth listener. - */ -export function verifyPhoneNumber( - auth: Auth, - phoneNumber: string, - autoVerifyTimeoutOrForceResend: number | boolean, - forceResend?: boolean, -): FirebaseAuthTypes.PhoneAuthListener; - -/** - * Authenticates a Firebase client using a popup-based OAuth authentication flow. - * - * @param auth - The Auth instance. - * @param provider - The auth provider. - * @param resolver - Optional. The popup redirect resolver. - * @returns A promise that resolves with the user credentials. - */ -export function signInWithPopup( - auth: Auth, - provider: FirebaseAuthTypes.AuthProvider, - resolver?: PopupRedirectResolver, -): Promise; - -/** - * Authenticates a Firebase client using a full-page redirect flow. - * - * @param auth - The Auth instance. - * @param provider - The auth provider. - * @param resolver - Optional. The popup redirect resolver. - * @returns A promise that resolves when the redirect is complete. - */ -export function signInWithRedirect( - auth: Auth, - provider: FirebaseAuthTypes.AuthProvider, - resolver?: PopupRedirectResolver, -): Promise; - -/** - * Signs out the current user. - * - * @param auth - The Auth instance. - * @returns A promise that resolves when the user is signed out. - */ -export function signOut(auth: Auth): Promise; - -/** - * Asynchronously sets the provided user as Auth.currentUser on the Auth instance. - * - * @param auth - The Auth instance. - * @param user - The user to set as the current user. - * @returns A promise that resolves when the user is set. - */ -export function updateCurrentUser(auth: Auth, user: FirebaseAuthTypes.User | null): Promise; - -/** - * Sets the current language to the default device/browser preference. - * - * @param auth - The Auth instance. - */ -export function useDeviceLanguage(auth: Auth): void; - -/** - * Sets the language code. - * - * #### Example - * - * ```js - * // Set language to French - * await firebase.auth().setLanguageCode('fr'); - * ``` - * @param auth - The Auth instance. - * @param languageCode An ISO language code. - * 'null' value will set the language code to the app's current language. - */ -export function setLanguageCode(auth: Auth, languageCode: string | null): Promise; - -/** - * Validates the password against the password policy configured for the project or tenant. - * - * @param auth - The Auth instance. - * @param password - The password to validate. - * - */ -export function validatePassword(auth: Auth, password: string): Promise; - -/** - * Configures a shared user access group to sync auth state across multiple apps via the Keychain. - * - * @param auth - The Auth instance. - * @param userAccessGroup - The user access group. - * @returns A promise that resolves when the user access group is set. - */ -export function useUserAccessGroup(auth: Auth, userAccessGroup: string): Promise; - -/** - * Verifies the password reset code sent to the user by email or other out-of-band mechanism. - * - * @param auth - The Auth instance. - * @param code - The password reset code. - * @returns A promise that resolves with the user's email address. - */ -export function verifyPasswordResetCode(auth: Auth, code: string): Promise; - -/** - * Parses the email action link string and returns an ActionCodeURL if the link is valid, otherwise returns null. - * - * @param link - The email action link string. - * @returns The ActionCodeURL if the link is valid, otherwise null. - */ -export function parseActionCodeURL(link: string): FirebaseAuthTypes.ActionCodeURL | null; - -/** - * Deletes and signs out the user. - * - * @param user - The user to delete. - * @returns A promise that resolves when the user is deleted. - */ -export function deleteUser(user: FirebaseAuthTypes.User): Promise; - -/** - * Returns a JSON Web Token (JWT) used to identify the user to a Firebase service. - * - * @param user - The user to get the token for. - * @param forceRefresh - Optional. Whether to force refresh the token. - * @returns A promise that resolves with the token. - */ -export function getIdToken(user: FirebaseAuthTypes.User, forceRefresh?: boolean): Promise; - -/** - * Returns a deserialized JSON Web Token (JWT) used to identify the user to a Firebase service. - * - * @param user - The user to get the token result for. - * @param forceRefresh - Optional. Whether to force refresh the token. - * @returns A promise that resolves with the token result. - */ -export function getIdTokenResult( - user: FirebaseAuthTypes.User, - forceRefresh?: boolean, -): Promise; - -/** - * Links the user account with the given credentials. - * - * @param user - The user to link the credentials with. - * @param credential - The auth credentials. - * @returns A promise that resolves with the user credentials. - */ -export function linkWithCredential( - user: FirebaseAuthTypes.User, - credential: FirebaseAuthTypes.AuthCredential, -): Promise; - -/** - * Links the user account with the given phone number. - * - * @param user - The user to link the phone number with. - * @param phoneNumber - The phone number. - * @param appVerifier - The application verifier. - * @returns A promise that resolves with the confirmation result. - */ -export function linkWithPhoneNumber( - user: FirebaseAuthTypes.User, - phoneNumber: string, - appVerifier?: ApplicationVerifier, -): Promise; - -/** - * Links the authenticated provider to the user account using a pop-up based OAuth flow. - * - * @param user - The user to link the provider with. - * @param provider - The auth provider. - * @param resolver - Optional. The popup redirect resolver. - * @returns A promise that resolves with the user credentials. - */ -export function linkWithPopup( - user: FirebaseAuthTypes.User, - provider: FirebaseAuthTypes.AuthProvider, - resolver?: PopupRedirectResolver, -): Promise; - -/** - * Links the OAuthProvider to the user account using a full-page redirect flow. - * - * @param user - The user to link the provider with. - * @param provider - The auth provider. - * @param resolver - Optional. The popup redirect resolver. - * @returns A promise that resolves when the redirect is complete. - */ -export function linkWithRedirect( - user: FirebaseAuthTypes.User, - provider: FirebaseAuthTypes.AuthProvider, - resolver?: PopupRedirectResolver, -): Promise; - -/** - * The MultiFactorUser corresponding to the user. - * - * @param user - The user to get the multi-factor user for. - * @returns The MultiFactorUser instance. - */ -export function multiFactor(user: FirebaseAuthTypes.User): FirebaseAuthTypes.MultiFactorUser; - -/** - * Re-authenticates a user using a fresh credential. - * - * @param user - The user to re-authenticate. - * @param credential - The auth credentials. - * @returns A promise that resolves with the user credentials. - */ -export function reauthenticateWithCredential( - user: FirebaseAuthTypes.User, - credential: FirebaseAuthTypes.AuthCredential, -): Promise; - -/** - * Re-authenticates a user using a fresh phone credential. - * - * @param user - The user to re-authenticate. - * @param phoneNumber - The phone number. - * @param appVerifier - The application verifier. - * @returns A promise that resolves with the confirmation result. - */ -export function reauthenticateWithPhoneNumber( - user: FirebaseAuthTypes.User, - phoneNumber: string, - appVerifier?: ApplicationVerifier, -): Promise; - -/** - * Re-authenticate a user with a federated authentication provider (Microsoft, Yahoo). For native platforms, this will open a browser window. - * - * @param user - The user to re-authenticate. - * @param provider - The auth provider. - * @param resolver - Optional. The popup redirect resolver. Web only. - * @returns A promise that resolves with the user credentials. - */ -export function reauthenticateWithPopup( - user: FirebaseAuthTypes.User, - provider: FirebaseAuthTypes.AuthProvider, - resolver?: PopupRedirectResolver, -): Promise; - -/** - * Re-authenticate a user with a federated authentication provider (Microsoft, Yahoo). For native platforms, this will open a browser window. - * - * @param user - The user to re-authenticate. - * @param provider - The auth provider. - * @param resolver - Optional. The popup redirect resolver. Web only. - * @returns A promise that resolves with no value. - */ -export function reauthenticateWithRedirect( - user: FirebaseAuthTypes.User, - provider: FirebaseAuthTypes.AuthProvider, - resolver?: PopupRedirectResolver, -): Promise; - -/** - * Reloads user account data, if signed in. - * - * @param user - The user to reload data for. - * @returns A promise that resolves when the data is reloaded. - */ -export function reload(user: FirebaseAuthTypes.User): Promise; - -/** - * Sends a verification email to a user. - * - * @param user - The user to send the email to. - * @param actionCodeSettings - Optional. Action code settings. - * @returns A promise that resolves when the email is sent. - */ -export function sendEmailVerification( - user: FirebaseAuthTypes.User, - actionCodeSettings?: FirebaseAuthTypes.ActionCodeSettings, -): Promise; - -/** - * Unlinks a provider from a user account. - * - * @param user - The user to unlink the provider from. - * @param providerId - The provider ID. - * @returns A promise that resolves with the user. - */ -export function unlink( - user: FirebaseAuthTypes.User, - providerId: string, -): Promise; - -/** - * Updates the user's email address. - * - * @param user - The user to update the email for. - * @param newEmail - The new email address. - * @returns A promise that resolves when the email is updated. - */ -export function updateEmail(user: FirebaseAuthTypes.User, newEmail: string): Promise; - -/** - * Updates the user's password. - * - * @param user - The user to update the password for. - * @param newPassword - The new password. - * @returns A promise that resolves when the password is updated. - */ -export function updatePassword(user: FirebaseAuthTypes.User, newPassword: string): Promise; - -/** - * Updates the user's phone number. - * - * @param user - The user to update the phone number for. - * @param credential - The auth credentials. - * @returns A promise that resolves when the phone number is updated. - */ -export function updatePhoneNumber( - user: FirebaseAuthTypes.User, - credential: FirebaseAuthTypes.AuthCredential, -): Promise; - -/** - * Updates a user's profile data. - * - * @param user - The user to update the profile for. - * @param profile - An object containing the profile data to update. - * @returns A promise that resolves when the profile is updated. - */ -export function updateProfile( - user: FirebaseAuthTypes.User, - { displayName, photoURL: photoUrl }: { displayName?: string | null; photoURL?: string | null }, -): Promise; - -/** - * Sends a verification email to a new email address. - * - * @param user - The user to send the email to. - * @param newEmail - The new email address. - * @param actionCodeSettings - Optional. Action code settings. - * @returns A promise that resolves when the email is sent. - */ -export function verifyBeforeUpdateEmail( - user: FirebaseAuthTypes.User, - newEmail: string, - actionCodeSettings?: FirebaseAuthTypes.ActionCodeSettings | null, -): Promise; - -/** - * Extracts provider specific AdditionalUserInfo for the given credential. - * - * @param userCredential - The user credential. - * @returns The additional user information, or null if none is available. - */ -export function getAdditionalUserInfo( - userCredential: FirebaseAuthTypes.UserCredential, -): FirebaseAuthTypes.AdditionalUserInfo | null; - -/** - * Returns the custom auth domain for the auth instance. - * - * @param auth - The Auth instance. - * @returns {Promise} A promise that resolves with the custom auth domain. - */ -export function getCustomAuthDomain(auth: Auth): Promise; - -/** - * Validates the password against the password policy configured for the project or tenant. - * - * @remarks - * If no tenant ID is set on the `Auth` instance, then this method will use the password - * policy configured for the project. Otherwise, this method will use the policy configured - * for the tenant. If a password policy has not been configured, then the default policy - * configured for all projects will be used. - * - * If an auth flow fails because a submitted password does not meet the password policy - * requirements and this method has previously been called, then this method will use the - * most recent policy available when called again. - * - * When using this method, ensure you have the Identity Toolkit enabled on the - * Google Cloud Platform with the API Key for your application permitted to use it. - * - * @example - * ``` js - * import { getAuth, validatePassword } from "firebase/auth"; - * - * const status = await validatePassword(getAuth(), passwordFromUser); - * if (!status.isValid) { - * // Password could not be validated. Use the status to show what - * // requirements are met and which are missing. - * - * // If a criterion is undefined, it is not required by policy. If the - * // criterion is defined but false, it is required but not fulfilled by - * // the given password. For example: - * const needsLowerCase = status.containsLowercaseLetter !== true; - * } - * ``` - * - * @param auth The {@link Auth} instance. - * @param password The password to validate. - * - * @public - */ -export function validatePassword(auth: Auth, password: string): Promise; - -/** - * A structure indicating which password policy requirements were met or violated and what the - * requirements are. - * - * @public - */ -export interface PasswordValidationStatus { - /** - * Whether the password meets all requirements. - */ - readonly isValid: boolean; - /** - * Whether the password meets the minimum password length, or undefined if not required. - */ - readonly meetsMinPasswordLength?: boolean; - /** - * Whether the password meets the maximum password length, or undefined if not required. - */ - readonly meetsMaxPasswordLength?: boolean; - /** - * Whether the password contains a lowercase letter, or undefined if not required. - */ - readonly containsLowercaseLetter?: boolean; - /** - * Whether the password contains an uppercase letter, or undefined if not required. - */ - readonly containsUppercaseLetter?: boolean; - /** - * Whether the password contains a numeric character, or undefined if not required. - */ - readonly containsNumericCharacter?: boolean; - /** - * Whether the password contains a non-alphanumeric character, or undefined if not required. - */ - readonly containsNonAlphanumericCharacter?: boolean; - /** - * The policy used to validate the password. - */ - readonly passwordPolicy: PasswordPolicy; -} - -export { - AppleAuthProvider, - EmailAuthProvider, - FacebookAuthProvider, - GithubAuthProvider, - GoogleAuthProvider, - OAuthProvider, - OIDCAuthProvider, - PhoneAuthProvider, - PhoneMultiFactorGenerator, - TotpMultiFactorGenerator, - TotpSecret, - TwitterAuthProvider, - PhoneAuthState, -} from '../index'; diff --git a/packages/auth/lib/modular/index.js b/packages/auth/lib/modular/index.js deleted file mode 100644 index ff408213d0..0000000000 --- a/packages/auth/lib/modular/index.js +++ /dev/null @@ -1,651 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* - * Copyright (c) 2016-present Invertase Limited & Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this library 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 { getApp } from '@react-native-firebase/app'; -import { MultiFactorUser } from '../multiFactor'; -import { MODULAR_DEPRECATION_ARG } from '@react-native-firebase/app/dist/module/common'; - -/** - * @typedef {import('@firebase/app-types').FirebaseApp} FirebaseApp - * @typedef {import('..').FirebaseAuthTypes} FirebaseAuthTypes - * @typedef {import('..').FirebaseAuthTypes.Module} Auth - * @typedef {import('..').FirebaseAuthTypes.CallbackOrObserver} CallbackOrObserver - * @typedef {import('..').FirebaseAuthTypes.AuthListenerCallback} AuthListenerCallback - * @typedef {import('..').FirebaseAuthTypes.ActionCodeInfo} ActionCodeInfo - * @typedef {import('..').FirebaseAuthTypes.UserCredential} UserCredential - * @typedef {import('..').FirebaseAuthTypes.MultiFactorError} MultiFactorError - * @typedef {import('..').FirebaseAuthTypes.MultiFactorUser} MultiFactorUser - * @typedef {import('..').FirebaseAuthTypes.MultiFactorResolver} MultiFactorResolver - * @typedef {import('..').FirebaseAuthTypes.ConfirmationResult} ConfirmationResult - * @typedef {import('..').FirebaseAuthTypes.AuthCredential} AuthCredential - * @typedef {import('..').FirebaseAuthTypes.AuthProvider} AuthProvider - * @typedef {import('..').FirebaseAuthTypes.PhoneAuthListener} PhoneAuthListener - * @typedef {import('..').FirebaseAuthTypes.ActionCodeSettings} ActionCodeSettings - * @typedef {import('..').FirebaseAuthTypes.User} User - * @typedef {import('..').FirebaseAuthTypes.IdTokenResult} IdTokenResult - * @typedef {import('..').FirebaseAuthTypes.AdditionalUserInfo} AdditionalUserInfo - * @typedef {import('..').FirebaseAuthTypes.ActionCodeURL} ActionCodeURL - * @typedef {import('..').FirebaseAuthTypes.ApplicationVerifier} ApplicationVerifier - */ - -/** - * Returns the Auth instance associated with the provided FirebaseApp. - * @param {FirebaseApp} [app] - The Firebase app instance. - * @returns {Auth} - */ -export function getAuth(app) { - if (app) { - return getApp(app.name).auth(); - } - return getApp().auth(); -} - -/** - * This function allows more control over the Auth instance than getAuth(). - * @param {FirebaseApp} app - The Firebase app instance. - * @param {any} [deps] - Optional. Dependencies for the Auth instance. - * @returns {Auth} - */ -export function initializeAuth(app, deps) { - if (app) { - return getApp(app.name).auth(); - } - return getApp().auth(); -} - -/** - * Applies a verification code sent to the user by email or other out-of-band mechanism. - * @param {Auth} auth - The Auth instance. - * @param {string} oobCode - The out-of-band code sent to the user. - * @returns {Promise} - */ -export async function applyActionCode(auth, oobCode) { - return auth.applyActionCode.call(auth, oobCode, MODULAR_DEPRECATION_ARG); -} - -/** - * Adds a blocking callback that runs before an auth state change sets a new user. - * @param {Auth} auth - The Auth instance. - * @param {(user: User | null) => void} callback - A callback function to run before the auth state changes. - * @param {() => void} [onAbort] - Optional. A callback function to run if the operation is aborted. - */ -export function beforeAuthStateChanged(auth, callback, onAbort) { - throw new Error('beforeAuthStateChanged is unsupported by the native Firebase SDKs'); -} - -/** - * Checks a verification code sent to the user by email or other out-of-band mechanism. - * @param {Auth} auth - The Auth instance. - * @param {string} oobCode - The out-of-band code sent to the user. - * @returns {Promise} - */ -export async function checkActionCode(auth, oobCode) { - return auth.checkActionCode.call(auth, oobCode, MODULAR_DEPRECATION_ARG); -} - -/** - * Completes the password reset process, given a confirmation code and new password. - * @param {Auth} auth - The Auth instance. - * @param {string} oobCode - The out-of-band code sent to the user. - * @param {string} newPassword - The new password. - * @returns {Promise} - */ -export async function confirmPasswordReset(auth, oobCode, newPassword) { - return auth.confirmPasswordReset.call(auth, oobCode, newPassword, MODULAR_DEPRECATION_ARG); -} - -/** - * Changes the Auth instance to communicate with the Firebase Auth Emulator, instead of production Firebase Auth services. - * @param {Auth} auth - The Auth instance. - * @param {string} url - The URL of the Firebase Auth Emulator. - * @param {{ disableWarnings: boolean }} [options] - Optional. Options for the emulator connection. - */ -export function connectAuthEmulator(auth, url, options) { - auth.useEmulator.call(auth, url, options, MODULAR_DEPRECATION_ARG); -} - -/** - * Creates a new user account associated with the specified email address and password. - * @param {Auth} auth - The Auth instance. - * @param {string} email - The user's email address. - * @param {string} password - The user's password. - * @returns {Promise} - */ -export async function createUserWithEmailAndPassword(auth, email, password) { - return auth.createUserWithEmailAndPassword.call(auth, email, password, MODULAR_DEPRECATION_ARG); -} - -/** - * Gets the list of possible sign in methods for the given email address. - * @param {Auth} auth - The Auth instance. - * @param {string} email - The user's email address. - * @returns {Promise} - */ -export async function fetchSignInMethodsForEmail(auth, email) { - return auth.fetchSignInMethodsForEmail.call(auth, email, MODULAR_DEPRECATION_ARG); -} - -/** - * Provides a MultiFactorResolver suitable for completion of a multi-factor flow. - * @param {Auth} auth - The Auth instance. - * @param {MultiFactorError} error - The multi-factor error. - * @returns {MultiFactorResolver} - */ -export function getMultiFactorResolver(auth, error) { - return auth.getMultiFactorResolver.call(auth, error, MODULAR_DEPRECATION_ARG); -} - -/** - * Returns a UserCredential from the redirect-based sign-in flow. - * @param {Auth} auth - The Auth instance. - * @param {PopupRedirectResolver} [resolver] - Optional. The popup redirect resolver. - * @returns {Promise} - */ -export async function getRedirectResult(auth, resolver) { - throw new Error('getRedirectResult is unsupported by the native Firebase SDKs'); -} - -/** - * Checks if an incoming link is a sign-in with email link suitable for signInWithEmailLink(). - * @param {Auth} auth - The Auth instance. - * @param {string} emailLink - The email link to check. - * @returns {Promise} - */ -export function isSignInWithEmailLink(auth, emailLink) { - return auth.isSignInWithEmailLink.call(auth, emailLink, MODULAR_DEPRECATION_ARG); -} - -/** - * Adds an observer for changes to the user's sign-in state. - * @param {Auth} auth - The Auth instance. - * @param {CallbackOrObserver} nextOrObserver - A callback function or observer for auth state changes. - * @returns {() => void} - */ -export function onAuthStateChanged(auth, nextOrObserver) { - return auth.onAuthStateChanged.call(auth, nextOrObserver, MODULAR_DEPRECATION_ARG); -} - -/** - * Adds an observer for changes to the signed-in user's ID token. - * @param {Auth} auth - The Auth instance. - * @param {CallbackOrObserver} nextOrObserver - A callback function or observer for ID token changes. - * @returns {() => void} - */ -export function onIdTokenChanged(auth, nextOrObserver) { - return auth.onIdTokenChanged.call(auth, nextOrObserver, MODULAR_DEPRECATION_ARG); -} - -/** - * Revoke the given access token, Currently only supports Apple OAuth access tokens. - * @param auth - The Auth Instance. - * @param token - The Access Token - */ -export async function revokeAccessToken(auth, token) { - throw new Error('revokeAccessToken() is only supported on Web'); -} //TO DO: Add Support - -/** - * Sends a password reset email to the given email address. - * @param {Auth} auth - The Auth instance. - * @param {string} email - The user's email address. - * @param {ActionCodeSettings} [actionCodeSettings] - Optional. Action code settings. - * @returns {Promise} - */ -export async function sendPasswordResetEmail(auth, email, actionCodeSettings) { - return auth.sendPasswordResetEmail.call(auth, email, actionCodeSettings, MODULAR_DEPRECATION_ARG); -} - -/** - * Sends a sign-in email link to the user with the specified email. - * @param {Auth} auth - The Auth instance. - * @param {string} email - The user's email address. - * @param {ActionCodeSettings} [actionCodeSettings] - Optional. Action code settings. - * @returns {Promise} - */ -export async function sendSignInLinkToEmail(auth, email, actionCodeSettings) { - return auth.sendSignInLinkToEmail.call(auth, email, actionCodeSettings, MODULAR_DEPRECATION_ARG); -} - -/** - * Changes the type of persistence on the Auth instance for the currently saved Auth session and applies this type of persistence for future sign-in requests, including sign-in with redirect requests. - * @param {Auth} auth - The Auth instance. - * @param {Persistence} persistence - The persistence type. - * @returns {Promise} - */ -export async function setPersistence(auth, persistence) { - throw new Error('setPersistence is unsupported by the native Firebase SDKs'); -} - -/** - * Asynchronously signs in as an anonymous user. - * @param {Auth} auth - The Auth instance. - * @returns {Promise} - */ -export async function signInAnonymously(auth) { - return auth.signInAnonymously.call(auth, MODULAR_DEPRECATION_ARG); -} - -/** - * Asynchronously signs in with the given credentials. - * @param {Auth} auth - The Auth instance. - * @param {AuthCredential} credential - The auth credentials. - * @returns {Promise} - */ -export async function signInWithCredential(auth, credential) { - return auth.signInWithCredential.call(auth, credential, MODULAR_DEPRECATION_ARG); -} - -/** - * Asynchronously signs in using a custom token. - * @param {Auth} auth - The Auth instance. - * @param {string} customToken - The custom token. - * @returns {Promise} - */ -export async function signInWithCustomToken(auth, customToken) { - return auth.signInWithCustomToken.call(auth, customToken, MODULAR_DEPRECATION_ARG); -} - -/** - * Asynchronously signs in using an email and password. - * @param {Auth} auth - The Auth instance. - * @param {string} email - The user's email address. - * @param {string} password - The user's password. - * @returns {Promise} - */ -export async function signInWithEmailAndPassword(auth, email, password) { - return auth.signInWithEmailAndPassword.call(auth, email, password, MODULAR_DEPRECATION_ARG); -} - -/** - * Asynchronously signs in using an email and sign-in email link. - * @param {Auth} auth - The Auth instance. - * @param {string} email - The user's email address. - * @param {string} emailLink - The email link. - * @returns {Promise} - */ -export async function signInWithEmailLink(auth, email, emailLink) { - return auth.signInWithEmailLink.call(auth, email, emailLink, MODULAR_DEPRECATION_ARG); -} - -/** - * Asynchronously signs in using a phone number. - * @param {Auth} auth - The Auth instance. - * @param {string} phoneNumber - The user's phone number. - * @param {ApplicationVerifier} appVerifier - The application verifier. - * @returns {Promise} - */ -export async function signInWithPhoneNumber(auth, phoneNumber, appVerifier) { - return auth.signInWithPhoneNumber.call(auth, phoneNumber, appVerifier, MODULAR_DEPRECATION_ARG); -} - -/** - * Asynchronously verifies a phone number. - * @param {Auth} auth - The Auth instance. - * @param {string} phoneNumber - The user's phone number. - * @param {number | boolean} autoVerifyTimeoutOrForceResend - The auto verify timeout or force resend flag. - * @param {boolean} [forceResend] - Optional. Whether to force resend. - * @returns {PhoneAuthListener} - */ -export function verifyPhoneNumber(auth, phoneNumber, autoVerifyTimeoutOrForceResend, forceResend) { - return auth.verifyPhoneNumber.call( - auth, - phoneNumber, - autoVerifyTimeoutOrForceResend, - forceResend, - MODULAR_DEPRECATION_ARG, - ); -} - -/** - * Authenticates a Firebase client using a popup-based OAuth authentication flow. - * @param {Auth} auth - The Auth instance. - * @param {AuthProvider} provider - The auth provider. - * @param {PopupRedirectResolver} [resolver] - Optional. The popup redirect resolver. - * @returns {Promise} - */ -export async function signInWithPopup(auth, provider, resolver) { - return auth.signInWithPopup.call(auth, provider, resolver, MODULAR_DEPRECATION_ARG); -} - -/** - * Authenticates a Firebase client using a full-page redirect flow. - * @param {Auth} auth - The Auth instance. - * @param {AuthProvider} provider - The auth provider. - * @param {PopupRedirectResolver} [resolver] - Optional. The popup redirect resolver. - * @returns {Promise} - */ -export async function signInWithRedirect(auth, provider, resolver) { - return auth.signInWithRedirect.call(auth, provider, resolver, MODULAR_DEPRECATION_ARG); -} - -/** - * Signs out the current user. - * @param {Auth} auth - The Auth instance. - * @returns {Promise} - */ -export async function signOut(auth) { - return auth.signOut.call(auth, MODULAR_DEPRECATION_ARG); -} - -/** - * Asynchronously sets the provided user as Auth.currentUser on the Auth instance. - * @param {Auth} auth - The Auth instance. - * @param {User} user - The user to set as the current user. - * @returns {Promise} - */ -export async function updateCurrentUser(auth, user) { - throw new Error('updateCurrentUser is unsupported by the native Firebase SDKs'); -} - -/** - * Sets the current language to the default device/browser preference. - * @param {Auth} auth - The Auth instance. - */ -export function useDeviceLanguage(auth) { - throw new Error('useDeviceLanguage is unsupported by the native Firebase SDKs'); -} - -/** - * Sets the language code. - * @param {Auth} auth - The Auth instance. - * @param {string} languageCode - The language code. - */ -export function setLanguageCode(auth, languageCode) { - return auth.setLanguageCode.call(auth, languageCode, MODULAR_DEPRECATION_ARG); -} - -/** - * Configures a shared user access group to sync auth state across multiple apps via the Keychain. - * @param {Auth} auth - The Auth instance. - * @param {string} userAccessGroup - The user access group. - * @returns {Promise} - */ -export function useUserAccessGroup(auth, userAccessGroup) { - return auth.useUserAccessGroup.call(auth, userAccessGroup, MODULAR_DEPRECATION_ARG); -} - -/** - * Verifies the password reset code sent to the user by email or other out-of-band mechanism. - * @param {Auth} auth - The Auth instance. - * @param {string} code - The password reset code. - * @returns {Promise} - */ -export async function verifyPasswordResetCode(auth, code) { - return auth.verifyPasswordResetCode.call(auth, code, MODULAR_DEPRECATION_ARG); -} - -/** - * Parses the email action link string and returns an ActionCodeURL if the link is valid, otherwise returns null. - * @param {string} link - The email action link string. - * @returns {ActionCodeURL | null} - */ -export function parseActionCodeURL(link) { - throw new Error('parseActionCodeURL is unsupported by the native Firebase SDKs'); -} - -/** - * Deletes and signs out the user. - * @param {User} user - The user to delete. - * @returns {Promise} - */ -export async function deleteUser(user) { - return user.delete.call(user, MODULAR_DEPRECATION_ARG); -} - -/** - * Returns a JSON Web Token (JWT) used to identify the user to a Firebase service. - * @param {User} user - The user to get the token for. - * @param {boolean} [forceRefresh] - Optional. Whether to force refresh the token. - * @returns {Promise} - */ -export async function getIdToken(user, forceRefresh) { - return user.getIdToken.call(user, forceRefresh, MODULAR_DEPRECATION_ARG); -} - -/** - * Returns a deserialized JSON Web Token (JWT) used to identify the user to a Firebase service. - * @param {User} user - The user to get the token result for. - * @param {boolean} [forceRefresh] - Optional. Whether to force refresh the token. - * @returns {Promise} - */ -export async function getIdTokenResult(user, forceRefresh) { - return user.getIdTokenResult.call(user, forceRefresh, MODULAR_DEPRECATION_ARG); -} - -/** - * Links the user account with the given credentials. - * @param {User} user - The user to link the credentials with. - * @param {AuthCredential} credential - The auth credentials. - * @returns {Promise} - */ -export async function linkWithCredential(user, credential) { - return user.linkWithCredential.call(user, credential, MODULAR_DEPRECATION_ARG); -} - -/** - * Links the user account with the given phone number. - * @param {User} user - The user to link the phone number with. - * @param {string} phoneNumber - The phone number. - * @param {ApplicationVerifier} appVerifier - The application verifier. - * @returns {Promise} - */ -export async function linkWithPhoneNumber(user, phoneNumber, appVerifier) { - throw new Error('linkWithPhoneNumber is unsupported by the native Firebase SDKs'); -} - -/** - * Links the authenticated provider to the user account using a pop-up based OAuth flow. - * @param {User} user - The user to link the provider with. - * @param {AuthProvider} provider - The auth provider. - * @param {PopupRedirectResolver} [resolver] - Optional. The popup redirect resolver. - * @returns {Promise} - */ -export async function linkWithPopup(user, provider, resolver) { - return user.linkWithPopup.call(user, provider, resolver, MODULAR_DEPRECATION_ARG); -} - -/** - * Links the OAuthProvider to the user account using a full-page redirect flow. - * @param {User} user - The user to link the provider with. - * @param {AuthProvider} provider - The auth provider. - * @param {PopupRedirectResolver} [resolver] - Optional. The popup redirect resolver. - * @returns {Promise} - */ -export async function linkWithRedirect(user, provider, resolver) { - return user.linkWithRedirect.call(user, provider, resolver, MODULAR_DEPRECATION_ARG); -} - -/** - * The MultiFactorUser corresponding to the user. - * @param {User} user - The user to get the multi-factor user for. - * @returns {MultiFactorUser} - */ -export function multiFactor(user) { - return new MultiFactorUser(getAuth(), user); -} - -/** - * Re-authenticates a user using a fresh credential. - * @param {User} user - The user to re-authenticate. - * @param {AuthCredential} credential - The auth credentials. - * @returns {Promise} - */ -export async function reauthenticateWithCredential(user, credential) { - return user.reauthenticateWithCredential.call(user, credential, MODULAR_DEPRECATION_ARG); -} - -/** - * Re-authenticates a user using a fresh phone credential. - * @param {User} user - The user to re-authenticate. - * @param {string} phoneNumber - The phone number. - * @param {ApplicationVerifier} appVerifier - The application verifier. - * @returns {Promise} - */ -export async function reauthenticateWithPhoneNumber(user, phoneNumber, appVerifier) { - throw new Error('reauthenticateWithPhoneNumber is unsupported by the native Firebase SDKs'); -} - -/** - * Re-authenticate a user with a federated authentication provider (Microsoft, Yahoo). For native platforms, this will open a browser window. - * @param {User} user - The user to re-authenticate. - * @param {AuthProvider} provider - The auth provider. - * @param {PopupRedirectResolver} [resolver] - Optional. The popup redirect resolver. Web only. - * @returns {Promise} - */ -export async function reauthenticateWithPopup(user, provider, resolver) { - return user.reauthenticateWithPopup.call(user, provider, resolver, MODULAR_DEPRECATION_ARG); -} - -/** - * Re-authenticate a user with a federated authentication provider (Microsoft, Yahoo). For native platforms, this will open a browser window. - * @param {User} user - The user to re-authenticate. - * @param {AuthProvider} provider - The auth provider. - * @param {PopupRedirectResolver} [resolver] - Optional. The popup redirect resolver. Web only. - * @returns {Promise} - */ -export async function reauthenticateWithRedirect(user, provider, resolver) { - return user.reauthenticateWithRedirect.call(user, provider, resolver, MODULAR_DEPRECATION_ARG); -} - -/** - * Reloads user account data, if signed in. - * @param {User} user - The user to reload data for. - * @returns {Promise} - */ -export async function reload(user) { - return user.reload.call(user, MODULAR_DEPRECATION_ARG); -} - -/** - * Sends a verification email to a user. - * @param {User} user - The user to send the email to. - * @param {ActionCodeSettings} [actionCodeSettings] - Optional. Action code settings. - * @returns {Promise} - */ -export async function sendEmailVerification(user, actionCodeSettings) { - return user.sendEmailVerification.call(user, actionCodeSettings, MODULAR_DEPRECATION_ARG); -} - -/** - * Unlinks a provider from a user account. - * @param {User} user - The user to unlink the provider from. - * @param {string} providerId - The provider ID. - * @returns {Promise} - */ -export async function unlink(user, providerId) { - return user.unlink.call(user, providerId, MODULAR_DEPRECATION_ARG); -} - -/** - * Updates the user's email address. - * @param {User} user - The user to update the email for. - * @param {string} newEmail - The new email address. - * @returns {Promise} - */ -export async function updateEmail(user, newEmail) { - return user.updateEmail.call(user, newEmail, MODULAR_DEPRECATION_ARG); -} - -/** - * Updates the user's password. - * @param {User} user - The user to update the password for. - * @param {string} newPassword - The new password. - * @returns {Promise} - */ -export async function updatePassword(user, newPassword) { - return user.updatePassword.call(user, newPassword, MODULAR_DEPRECATION_ARG); -} - -/** - * Updates the user's phone number. - * @param {User} user - The user to update the phone number for. - * @param {AuthCredential} credential - The auth credentials. - * @returns {Promise} - */ -export async function updatePhoneNumber(user, credential) { - return user.updatePhoneNumber.call(user, credential, MODULAR_DEPRECATION_ARG); -} - -/** - * Updates a user's profile data. - * @param {User} user - The user to update the profile for. - * @param {{ displayName?: string | null, photoURL?: string | null }} profile - An object containing the profile data to update. - * @returns {Promise} - */ -export async function updateProfile(user, { displayName, photoURL: photoUrl }) { - return user.updateProfile.call( - user, - { displayName, photoURL: photoUrl }, - MODULAR_DEPRECATION_ARG, - ); -} - -/** - * Sends a verification email to a new email address. - * @param {User} user - The user to send the email to. - * @param {string} newEmail - The new email address. - * @param {ActionCodeSettings} [actionCodeSettings] - Optional. Action code settings. - * @returns {Promise} - */ -export async function verifyBeforeUpdateEmail(user, newEmail, actionCodeSettings) { - return user.verifyBeforeUpdateEmail.call( - user, - newEmail, - actionCodeSettings, - MODULAR_DEPRECATION_ARG, - ); -} - -/** - * Extracts provider specific AdditionalUserInfo for the given credential. - * @param {UserCredential} userCredential - The user credential. - * @returns {AdditionalUserInfo | null} - */ -export function getAdditionalUserInfo(userCredential) { - return userCredential.additionalUserInfo; -} - -/** - * Returns the custom auth domain for the auth instance. - * @param {Auth} auth - The Auth instance. - * @returns {Promise} - */ -export function getCustomAuthDomain(auth) { - return auth.getCustomAuthDomain.call(auth, MODULAR_DEPRECATION_ARG); -} - -/** - * Returns a password validation status - * @param {Auth} auth - The Auth instance. - * @param {string} password - The password to validate. - * @returns {Promise} - */ -export async function validatePassword(auth, password) { - if (!auth || !auth.app) { - throw new Error( - "firebase.auth().validatePassword(*) 'auth' must be a valid Auth instance with an 'app' property. Received: undefined", - ); - } - - if (password === null || password === undefined) { - throw new Error( - "firebase.auth().validatePassword(*) expected 'password' to be a non-null or a defined value.", - ); - } - - return auth.validatePassword.call(auth, password, MODULAR_DEPRECATION_ARG); -} diff --git a/packages/auth/lib/multiFactor.js b/packages/auth/lib/multiFactor.js deleted file mode 100644 index bfaca1278f..0000000000 --- a/packages/auth/lib/multiFactor.js +++ /dev/null @@ -1,50 +0,0 @@ -import { reload } from './modular'; -/** - * Return a MultiFactorUser instance the gateway to multi-factor operations. - */ -export function multiFactor(auth) { - return new MultiFactorUser(auth); -} - -export class MultiFactorUser { - constructor(auth, user) { - this._auth = auth; - if (user === undefined) { - user = auth.currentUser; - } - this._user = user; - this.enrolledFactors = user.multiFactor.enrolledFactors; - } - - getSession() { - return this._auth.native.getSession(); - } - - /** - * Finalize the enrollment process for the given multi-factor assertion. - * Optionally set a displayName. This method will reload the current user - * profile, which is necessary to see the multi-factor changes. - */ - async enroll(multiFactorAssertion, displayName) { - const { token, secret, totpSecret, verificationCode } = multiFactorAssertion; - if (token && secret) { - await this._auth.native.finalizeMultiFactorEnrollment(token, secret, displayName); - } else if (totpSecret && verificationCode) { - await this._auth.native.finalizeTotpEnrollment(totpSecret, verificationCode, displayName); - } else { - throw new Error('Invalid multi-factor assertion provided for enrollment.'); - } - - // We need to reload the user otherwise the changes are not visible - // TODO reload not working on Other platform - return reload(this._auth.currentUser); - } - - async unenroll(enrollmentId) { - await this._auth.native.unenrollMultiFactor(enrollmentId); - - if (this._auth.currentUser) { - return reload(this._auth.currentUser); - } - } -} diff --git a/packages/auth/lib/multiFactor.ts b/packages/auth/lib/multiFactor.ts new file mode 100644 index 0000000000..d95ffc7536 --- /dev/null +++ b/packages/auth/lib/multiFactor.ts @@ -0,0 +1,83 @@ +import { reload } from './modular'; +import type { + MultiFactorAssertion as ModularMultiFactorAssertion, + User, +} from './types/auth'; +import type { FirebaseAuthTypes } from './types/namespaced'; +import type { AuthInternal, MultiFactorEnrollmentAssertionInternal } from './types/internal'; + +type MultiFactorAuthHost = { + currentUser: FirebaseAuthTypes.User | null; + native: AuthInternal['native']; +}; +/** + * Return a MultiFactorUser instance the gateway to multi-factor operations. + */ +export function multiFactor(auth: MultiFactorAuthHost): FirebaseAuthTypes.MultiFactorUser { + return new MultiFactorUser(auth); +} + +export class MultiFactorUser { + readonly enrolledFactors: FirebaseAuthTypes.MultiFactorInfo[]; + private readonly _auth: MultiFactorAuthHost; + + constructor(auth: MultiFactorAuthHost, user?: FirebaseAuthTypes.User) { + this._auth = auth; + if (user === undefined) { + user = auth.currentUser as FirebaseAuthTypes.User; + } + + if (!user) { + throw new Error('A user is required to access multi-factor operations.'); + } + + this.enrolledFactors = user.multiFactor?.enrolledFactors ?? []; + } + + getSession(): Promise { + return this._auth.native.getSession(); + } + + /** + * Finalize the enrollment process for the given multi-factor assertion. + * Optionally set a displayName. This method will reload the current user + * profile, which is necessary to see the multi-factor changes. + */ + async enroll( + multiFactorAssertion: FirebaseAuthTypes.MultiFactorAssertion | ModularMultiFactorAssertion, + displayName?: string | null, + ): Promise { + const assertion = multiFactorAssertion as MultiFactorEnrollmentAssertionInternal; + + if (assertion.factorId === 'phone') { + await this._auth.native.finalizeMultiFactorEnrollment( + assertion.token, + assertion.secret, + displayName ?? undefined, + ); + } else if (assertion.factorId === 'totp') { + await this._auth.native.finalizeTotpEnrollment( + assertion.totpSecret, + assertion.verificationCode, + displayName ?? undefined, + ); + } else { + // Runtime guard for callers that bypass the typed MFA assertion helpers. + throw new Error('Invalid multi-factor assertion provided for enrollment.'); + } + + // We need to reload the user otherwise the changes are not visible + // TODO reload not working on Other platform + await reload(this._auth.currentUser as unknown as User); + } + + async unenroll(enrollmentId: FirebaseAuthTypes.MultiFactorInfo | string): Promise { + await this._auth.native.unenrollMultiFactor( + enrollmentId as string | FirebaseAuthTypes.MultiFactorInfo, + ); + + if (this._auth.currentUser) { + await reload(this._auth.currentUser as unknown as User); + } + } +} diff --git a/packages/auth/lib/index.js b/packages/auth/lib/namespaced.ts similarity index 56% rename from packages/auth/lib/index.js rename to packages/auth/lib/namespaced.ts index 3ddb6dc9f9..6951313aa6 100644 --- a/packages/auth/lib/index.js +++ b/packages/auth/lib/namespaced.ts @@ -25,13 +25,16 @@ import { isValidUrl, parseListenerOrObserver, } from '@react-native-firebase/app/dist/module/common'; +import type { ReactNativeFirebase } from '@react-native-firebase/app'; import { setReactNativeModule } from '@react-native-firebase/app/dist/module/internal/nativeModule'; import { FirebaseModule, createModuleNamespace, getFirebaseRoot, + type ModuleConfig, } from '@react-native-firebase/app/dist/module/internal'; import ConfirmationResult from './ConfirmationResult'; +import { PhoneAuthState } from './PhoneAuthState'; import PhoneAuthListener from './PhoneAuthListener'; import PhoneMultiFactorGenerator from './PhoneMultiFactorGenerator'; import TotpMultiFactorGenerator from './TotpMultiFactorGenerator'; @@ -48,34 +51,32 @@ import OAuthProvider from './providers/OAuthProvider'; import OIDCAuthProvider from './providers/OIDCAuthProvider'; import PhoneAuthProvider from './providers/PhoneAuthProvider'; import TwitterAuthProvider from './providers/TwitterAuthProvider'; -import { TotpSecret } from './TotpSecret'; -import version from './version'; +import { version } from './version'; import fallBackModule from './web/RNFBAuthModule'; import { PasswordPolicyMixin } from './password-policy/PasswordPolicyMixin'; - -const PhoneAuthState = { - CODE_SENT: 'sent', - AUTO_VERIFY_TIMEOUT: 'timeout', - AUTO_VERIFIED: 'verified', - ERROR: 'error', +import type { CallbackOrObserver, FirebaseAuthTypes } from './types/namespaced'; +import type { + AuthIdTokenChangedEventInternal, + AuthInternal, + AuthStateChangedEventInternal, + NativePhoneAuthCredentialInternal, + NativeUserCredentialInternal, + NativeUserInternal, + PasswordPolicyInternal, + PasswordValidationStatusInternal, + PhoneAuthStateChangedEventInternal, +} from './types/internal'; + +type AuthProviderWithObjectInternal = FirebaseAuthTypes.AuthProvider & { + toObject(): Record; }; -export { - AppleAuthProvider, - EmailAuthProvider, - PhoneAuthProvider, - GoogleAuthProvider, - GithubAuthProvider, - TwitterAuthProvider, - FacebookAuthProvider, - PhoneMultiFactorGenerator, - TotpMultiFactorGenerator, - TotpSecret, - OAuthProvider, - OIDCAuthProvider, - PhoneAuthState, +type AuthErrorWithCodeInternal = Error & { + code?: string; }; +const nativeEvents = ['auth_state_changed', 'auth_id_token_changed', 'phone_auth_state_changed']; + const statics = { AppleAuthProvider, EmailAuthProvider, @@ -96,37 +97,57 @@ const statics = { const namespace = 'auth'; const nativeModuleName = 'RNFBAuthModule'; -class FirebaseAuthModule extends FirebaseModule { - constructor(...args) { - super(...args); +class FirebaseAuthModule extends FirebaseModule { + _user: FirebaseAuthTypes.User | null; + _settings: FirebaseAuthTypes.AuthSettings | null; + _authResult: boolean; + _languageCode: string; + _tenantId: string | null; + _projectPasswordPolicy: PasswordPolicyInternal | null; + _tenantPasswordPolicies: Record; + _getPasswordPolicyInternal!: () => PasswordPolicyInternal | null; + _updatePasswordPolicy!: () => Promise; + _recachePasswordPolicy!: () => Promise; + validatePassword!: (password: string) => Promise; + + constructor( + app: ReactNativeFirebase.FirebaseAppBase, + config: ModuleConfig, + customUrlOrRegion?: string | null, + ) { + super(app, config, customUrlOrRegion); this._user = null; this._settings = null; this._authResult = false; - this._languageCode = this.native.APP_LANGUAGE[this.app._name]; + this._languageCode = this.native.APP_LANGUAGE[this.app.name] ?? ''; this._tenantId = null; this._projectPasswordPolicy = null; this._tenantPasswordPolicies = {}; if (!this.languageCode) { - this._languageCode = this.native.APP_LANGUAGE['[DEFAULT]']; + this._languageCode = this.native.APP_LANGUAGE['[DEFAULT]'] ?? ''; } - if (this.native.APP_USER[this.app._name]) { - this._setUser(this.native.APP_USER[this.app._name]); + const initialUser = this.native.APP_USER[this.app.name]; + if (initialUser) { + this._setUser(initialUser); } this.emitter.addListener(this.eventNameForApp('auth_state_changed'), event => { - this._setUser(event.user); + const authEvent = event as AuthStateChangedEventInternal; + this._setUser(authEvent.user); this.emitter.emit(this.eventNameForApp('onAuthStateChanged'), this._user); }); this.emitter.addListener(this.eventNameForApp('phone_auth_state_changed'), event => { - const eventKey = `phone:auth:${event.requestKey}:${event.type}`; - this.emitter.emit(eventKey, event.state); + const phoneAuthEvent = event as PhoneAuthStateChangedEventInternal; + const eventKey = `phone:auth:${phoneAuthEvent.requestKey}:${phoneAuthEvent.type}`; + this.emitter.emit(eventKey, phoneAuthEvent.state); }); - this.emitter.addListener(this.eventNameForApp('auth_id_token_changed'), auth => { - this._setUser(auth.user); + this.emitter.addListener(this.eventNameForApp('auth_id_token_changed'), event => { + const authEvent = event as AuthIdTokenChangedEventInternal; + this._setUser(authEvent.user); this.emitter.emit(this.eventNameForApp('onIdTokenChanged'), this._user); }); @@ -143,11 +164,11 @@ class FirebaseAuthModule extends FirebaseModule { } } - get languageCode() { + get languageCode(): string { return this._languageCode; } - set languageCode(code) { + set languageCode(code: string | null) { // For modular API, not recommended to set languageCode directly as it should be set in the native SDKs first if (!isString(code) && !isNull(code)) { throw new Error( @@ -156,47 +177,57 @@ class FirebaseAuthModule extends FirebaseModule { } // as this is a setter, we can't use async/await. So we set it first so it is available immediately if (code === null) { - this._languageCode = this.native.APP_LANGUAGE[this.app._name]; + this._languageCode = this.native.APP_LANGUAGE[this.app.name] ?? ''; if (!this.languageCode) { - this._languageCode = this.native.APP_LANGUAGE['[DEFAULT]']; + this._languageCode = this.native.APP_LANGUAGE['[DEFAULT]'] ?? ''; } } else { this._languageCode = code; } // This sets it natively - this.setLanguageCode(code); + void this.setLanguageCode(code); } - get config() { + get config(): Record { // for modular API, firebase JS SDK has a config object which is not available in native SDKs return {}; } - get tenantId() { + get tenantId(): string | null { return this._tenantId; } - get settings() { + get settings(): FirebaseAuthTypes.AuthSettings { if (!this._settings) { - this._settings = new Settings(this); + this._settings = new Settings( + this as unknown as AuthInternal, + ) as FirebaseAuthTypes.AuthSettings; } return this._settings; } - get currentUser() { + get currentUser(): FirebaseAuthTypes.User | null { return this._user; } - _setUser(user) { - this._user = user ? createDeprecationProxy(new User(this, user)) : null; + _setUser(user?: NativeUserInternal | null): FirebaseAuthTypes.User | null { + this._user = user + ? (createDeprecationProxy( + new User(this as unknown as AuthInternal, user), + ) as FirebaseAuthTypes.User) + : null; this._authResult = true; this.emitter.emit(this.eventNameForApp('onUserChanged'), this._user); return this._user; } - _setUserCredential(userCredential) { - const user = createDeprecationProxy(new User(this, userCredential.user)); + _setUserCredential( + userCredential: NativeUserCredentialInternal, + ): FirebaseAuthTypes.UserCredential { + const user = createDeprecationProxy( + new User(this as unknown as AuthInternal, userCredential.user), + ) as FirebaseAuthTypes.User; this._user = user; this._authResult = true; this.emitter.emit(this.eventNameForApp('onUserChanged'), this._user); @@ -206,7 +237,7 @@ class FirebaseAuthModule extends FirebaseModule { }; } - async setLanguageCode(code) { + async setLanguageCode(code: string | null): Promise { if (!isString(code) && !isNull(code)) { throw new Error( "firebase.auth().setLanguageCode(*) expected 'languageCode' to be a string or null value", @@ -216,17 +247,17 @@ class FirebaseAuthModule extends FirebaseModule { await this.native.setLanguageCode(code); if (code === null) { - this._languageCode = this.native.APP_LANGUAGE[this.app._name]; + this._languageCode = this.native.APP_LANGUAGE[this.app.name] ?? ''; if (!this.languageCode) { - this._languageCode = this.native.APP_LANGUAGE['[DEFAULT]']; + this._languageCode = this.native.APP_LANGUAGE['[DEFAULT]'] ?? ''; } } else { this._languageCode = code; } } - async setTenantId(tenantId) { + async setTenantId(tenantId: string): Promise { if (!isString(tenantId)) { throw new Error("firebase.auth().setTenantId(*) expected 'tenantId' to be a string"); } @@ -234,8 +265,12 @@ class FirebaseAuthModule extends FirebaseModule { await this.native.setTenantId(tenantId); } - onAuthStateChanged(listenerOrObserver) { - const listener = parseListenerOrObserver(listenerOrObserver); + onAuthStateChanged( + listenerOrObserver: CallbackOrObserver, + ): () => void { + const listener = parseListenerOrObserver( + listenerOrObserver, + ) as FirebaseAuthTypes.AuthListenerCallback; const subscription = this.emitter.addListener( this.eventNameForApp('onAuthStateChanged'), listener, @@ -249,8 +284,12 @@ class FirebaseAuthModule extends FirebaseModule { return () => subscription.remove(); } - onIdTokenChanged(listenerOrObserver) { - const listener = parseListenerOrObserver(listenerOrObserver); + onIdTokenChanged( + listenerOrObserver: CallbackOrObserver, + ): () => void { + const listener = parseListenerOrObserver( + listenerOrObserver, + ) as FirebaseAuthTypes.AuthListenerCallback; const subscription = this.emitter.addListener( this.eventNameForApp('onIdTokenChanged'), listener, @@ -264,8 +303,12 @@ class FirebaseAuthModule extends FirebaseModule { return () => subscription.remove(); } - onUserChanged(listenerOrObserver) { - const listener = parseListenerOrObserver(listenerOrObserver); + onUserChanged( + listenerOrObserver: CallbackOrObserver, + ): () => void { + const listener = parseListenerOrObserver( + listenerOrObserver, + ) as FirebaseAuthTypes.AuthListenerCallback; const subscription = this.emitter.addListener(this.eventNameForApp('onUserChanged'), listener); if (this._authResult) { Promise.resolve().then(() => { @@ -278,33 +321,48 @@ class FirebaseAuthModule extends FirebaseModule { }; } - signOut() { + signOut(): Promise { return this.native.signOut().then(() => { - this._setUser(); + this._setUser(null); }); } - signInAnonymously() { + signInAnonymously(): Promise { return this.native .signInAnonymously() - .then(userCredential => this._setUserCredential(userCredential)); + .then((userCredential: NativeUserCredentialInternal) => + this._setUserCredential(userCredential), + ); } - signInWithPhoneNumber(phoneNumber, forceResend) { + signInWithPhoneNumber( + phoneNumber: string, + forceResend?: boolean, + ): Promise { if (isAndroid) { return this.native .signInWithPhoneNumber(phoneNumber, forceResend || false) - .then(result => new ConfirmationResult(this, result.verificationId)); + .then( + (result: NativePhoneAuthCredentialInternal) => + new ConfirmationResult(this as unknown as AuthInternal, result.verificationId), + ); } return this.native .signInWithPhoneNumber(phoneNumber) - .then(result => new ConfirmationResult(this, result.verificationId)); + .then( + (result: NativePhoneAuthCredentialInternal) => + new ConfirmationResult(this as unknown as AuthInternal, result.verificationId), + ); } - verifyPhoneNumber(phoneNumber, autoVerifyTimeoutOrForceResend, forceResend) { + verifyPhoneNumber( + phoneNumber: string, + autoVerifyTimeoutOrForceResend?: number | boolean, + forceResend?: boolean, + ): FirebaseAuthTypes.PhoneAuthListener { let _forceResend = forceResend; - let _autoVerifyTimeout = 60; + let _autoVerifyTimeout: number | undefined = 60; if (isBoolean(autoVerifyTimeoutOrForceResend)) { _forceResend = autoVerifyTimeoutOrForceResend; @@ -312,39 +370,64 @@ class FirebaseAuthModule extends FirebaseModule { _autoVerifyTimeout = autoVerifyTimeoutOrForceResend; } - return new PhoneAuthListener(this, phoneNumber, _autoVerifyTimeout, _forceResend); + return new PhoneAuthListener( + this as unknown as AuthInternal, + phoneNumber, + _autoVerifyTimeout, + _forceResend, + ) as FirebaseAuthTypes.PhoneAuthListener; } - verifyPhoneNumberWithMultiFactorInfo(multiFactorHint, session) { + verifyPhoneNumberWithMultiFactorInfo( + multiFactorHint: FirebaseAuthTypes.MultiFactorInfo, + session: FirebaseAuthTypes.MultiFactorSession, + ): Promise { return this.native.verifyPhoneNumberWithMultiFactorInfo(multiFactorHint.uid, session); } - verifyPhoneNumberForMultiFactor(phoneInfoOptions) { + verifyPhoneNumberForMultiFactor( + phoneInfoOptions: FirebaseAuthTypes.PhoneMultiFactorEnrollInfoOptions, + ): Promise { const { phoneNumber, session } = phoneInfoOptions; return this.native.verifyPhoneNumberForMultiFactor(phoneNumber, session); } - resolveMultiFactorSignIn(session, verificationId, verificationCode) { + resolveMultiFactorSignIn( + session: FirebaseAuthTypes.MultiFactorSession, + verificationId: string, + verificationCode: string, + ): Promise { return this.native .resolveMultiFactorSignIn(session, verificationId, verificationCode) - .then(userCredential => { + .then((userCredential: NativeUserCredentialInternal) => { return this._setUserCredential(userCredential); }); } - resolveTotpSignIn(session, uid, totpSecret) { - return this.native.resolveTotpSignIn(session, uid, totpSecret).then(userCredential => { - return this._setUserCredential(userCredential); - }); + resolveTotpSignIn( + session: FirebaseAuthTypes.MultiFactorSession, + uid: string, + totpSecret: string, + ): Promise { + return this.native + .resolveTotpSignIn(session, uid, totpSecret) + .then((userCredential: NativeUserCredentialInternal) => { + return this._setUserCredential(userCredential); + }); } - createUserWithEmailAndPassword(email, password) { + createUserWithEmailAndPassword( + email: string, + password: string, + ): Promise { return ( this.native .createUserWithEmailAndPassword(email, password) - .then(userCredential => this._setUserCredential(userCredential)) + .then((userCredential: NativeUserCredentialInternal) => + this._setUserCredential(userCredential), + ) /* istanbul ignore next - native error handling cannot be unit tested */ - .catch(error => { + .catch((error: AuthErrorWithCodeInternal) => { if (error.code === 'auth/password-does-not-meet-requirements') { return this._recachePasswordPolicy() .catch(() => { @@ -359,13 +442,18 @@ class FirebaseAuthModule extends FirebaseModule { ); } - signInWithEmailAndPassword(email, password) { + signInWithEmailAndPassword( + email: string, + password: string, + ): Promise { return ( this.native .signInWithEmailAndPassword(email, password) - .then(userCredential => this._setUserCredential(userCredential)) + .then((userCredential: NativeUserCredentialInternal) => + this._setUserCredential(userCredential), + ) /* istanbul ignore next - native error handling cannot be unit tested */ - .catch(error => { + .catch((error: AuthErrorWithCodeInternal) => { if (error.code === 'auth/password-does-not-meet-requirements') { return this._recachePasswordPolicy() .catch(() => { @@ -380,46 +468,60 @@ class FirebaseAuthModule extends FirebaseModule { ); } - signInWithCustomToken(customToken) { + signInWithCustomToken(customToken: string): Promise { return this.native .signInWithCustomToken(customToken) - .then(userCredential => this._setUserCredential(userCredential)); + .then((userCredential: NativeUserCredentialInternal) => + this._setUserCredential(userCredential), + ); } - signInWithCredential(credential) { + signInWithCredential( + credential: FirebaseAuthTypes.AuthCredential, + ): Promise { return this.native .signInWithCredential(credential.providerId, credential.token, credential.secret) - .then(userCredential => this._setUserCredential(userCredential)); + .then((userCredential: NativeUserCredentialInternal) => + this._setUserCredential(userCredential), + ); } - revokeToken(authorizationCode) { + revokeToken(authorizationCode: string): Promise { return this.native.revokeToken(authorizationCode); } - sendPasswordResetEmail(email, actionCodeSettings = null) { + sendPasswordResetEmail( + email: string, + actionCodeSettings: FirebaseAuthTypes.ActionCodeSettings | null = null, + ): Promise { return this.native.sendPasswordResetEmail(email, actionCodeSettings); } - sendSignInLinkToEmail(email, actionCodeSettings = {}) { + sendSignInLinkToEmail( + email: string, + actionCodeSettings?: FirebaseAuthTypes.ActionCodeSettings, + ): Promise { return this.native.sendSignInLinkToEmail(email, actionCodeSettings); } - isSignInWithEmailLink(emailLink) { + isSignInWithEmailLink(emailLink: string): Promise { return this.native.isSignInWithEmailLink(emailLink); } - signInWithEmailLink(email, emailLink) { + signInWithEmailLink(email: string, emailLink: string): Promise { return this.native .signInWithEmailLink(email, emailLink) - .then(userCredential => this._setUserCredential(userCredential)); + .then((userCredential: NativeUserCredentialInternal) => + this._setUserCredential(userCredential), + ); } - confirmPasswordReset(code, newPassword) { + confirmPasswordReset(code: string, newPassword: string): Promise { return ( this.native .confirmPasswordReset(code, newPassword) /* istanbul ignore next - native error handling cannot be unit tested */ - .catch(error => { + .catch((error: AuthErrorWithCodeInternal) => { if (error.code === 'auth/password-does-not-meet-requirements') { return this._recachePasswordPolicy() .catch(() => { @@ -434,61 +536,69 @@ class FirebaseAuthModule extends FirebaseModule { ); } - applyActionCode(code) { + applyActionCode(code: string): Promise { return this.native.applyActionCode(code).then(user => { this._setUser(user); }); } - checkActionCode(code) { + checkActionCode(code: string): Promise { return this.native.checkActionCode(code); } - fetchSignInMethodsForEmail(email) { + fetchSignInMethodsForEmail(email: string): Promise { return this.native.fetchSignInMethodsForEmail(email); } - verifyPasswordResetCode(code) { + verifyPasswordResetCode(code: string): Promise { return this.native.verifyPasswordResetCode(code); } - useUserAccessGroup(userAccessGroup) { + useUserAccessGroup(userAccessGroup: string): Promise { if (isAndroid) { return Promise.resolve(); } - return this.native.useUserAccessGroup(userAccessGroup); + return this.native.useUserAccessGroup(userAccessGroup).then(() => undefined); } - getRedirectResult() { + getRedirectResult(): Promise { throw new Error( 'firebase.auth().getRedirectResult() is unsupported by the native Firebase SDKs.', ); } - setPersistence() { + setPersistence(): Promise { throw new Error('firebase.auth().setPersistence() is unsupported by the native Firebase SDKs.'); } - signInWithPopup(provider) { + signInWithPopup( + provider: FirebaseAuthTypes.AuthProvider, + ): Promise { return this.native - .signInWithProvider(provider.toObject()) - .then(userCredential => this._setUserCredential(userCredential)); + .signInWithProvider((provider as AuthProviderWithObjectInternal).toObject()) + .then((userCredential: NativeUserCredentialInternal) => + this._setUserCredential(userCredential), + ); } - signInWithRedirect(provider) { + signInWithRedirect( + provider: FirebaseAuthTypes.AuthProvider, + ): Promise { return this.native - .signInWithProvider(provider.toObject()) - .then(userCredential => this._setUserCredential(userCredential)); + .signInWithProvider((provider as AuthProviderWithObjectInternal).toObject()) + .then((userCredential: NativeUserCredentialInternal) => + this._setUserCredential(userCredential), + ); } // firebase issue - https://github.com/invertase/react-native-firebase/pull/655#issuecomment-349904680 - useDeviceLanguage() { + useDeviceLanguage(): void { throw new Error( 'firebase.auth().useDeviceLanguage() is unsupported by the native Firebase SDKs.', ); } - useEmulator(url) { + useEmulator(url: string): void { if (!url || !isString(url) || !isValidUrl(url)) { throw new Error('firebase.auth().useEmulator() takes a non-empty string URL'); } @@ -521,23 +631,36 @@ class FirebaseAuthModule extends FirebaseModule { throw new Error('firebase.auth().useEmulator() unable to parse host and port from URL'); } const host = urlMatches[1]; - const port = parseInt(urlMatches[2], 10); + const portString = urlMatches[2]; + if (!host) { + throw new Error('firebase.auth().useEmulator() unable to parse host from URL'); + } + const port = portString ? parseInt(portString, 10) : undefined; this.native.useEmulator(host, port); - return [host, port]; // undocumented return, useful for unit testing + // @ts-ignore - undocumented return, useful for unit testing + return [host, port]; } - getMultiFactorResolver(error) { - return getMultiFactorResolver(this, error); + getMultiFactorResolver( + error: FirebaseAuthTypes.MultiFactorError, + ): FirebaseAuthTypes.MultiFactorResolver { + return getMultiFactorResolver( + this as unknown as AuthInternal, + error, + ) as FirebaseAuthTypes.MultiFactorResolver; } - multiFactor(user) { - if (user.userId !== this.currentUser.userId) { + multiFactor(user: FirebaseAuthTypes.User): FirebaseAuthTypes.MultiFactorUser { + if (!this.currentUser || user.uid !== this.currentUser.uid) { throw new Error('firebase.auth().multiFactor() only operates on currentUser'); } - return new MultiFactorUser(this, user); + return new MultiFactorUser( + this as unknown as AuthInternal, + user, + ) as FirebaseAuthTypes.MultiFactorUser; } - getCustomAuthDomain() { + getCustomAuthDomain(): Promise { return this.native.getCustomAuthDomain(); } } @@ -555,14 +678,12 @@ export default createModuleNamespace({ version, namespace, nativeModuleName, - nativeEvents: ['auth_state_changed', 'auth_id_token_changed', 'phone_auth_state_changed'], + nativeEvents, hasMultiAppSupport: true, hasCustomUrlOrRegionSupport: false, ModuleClass: FirebaseAuthModule, }); -export * from './modular/index'; - // import auth, { firebase } from '@react-native-firebase/auth'; // auth().X(...); // firebase.auth().X(...); diff --git a/packages/auth/lib/password-policy/PasswordPolicyImpl.js b/packages/auth/lib/password-policy/PasswordPolicyImpl.ts similarity index 75% rename from packages/auth/lib/password-policy/PasswordPolicyImpl.js rename to packages/auth/lib/password-policy/PasswordPolicyImpl.ts index cdfb99a118..77c84b732b 100644 --- a/packages/auth/lib/password-policy/PasswordPolicyImpl.js +++ b/packages/auth/lib/password-policy/PasswordPolicyImpl.ts @@ -15,6 +15,13 @@ * */ +import type { + PasswordPolicyCustomStrengthOptionsInternal, + PasswordPolicyInternal, + PasswordPolicyResponseInternal, + PasswordValidationStatusInternal, +} from '../types/internal'; + // Minimum min password length enforced by the backend, even if no minimum length is set. const MINIMUM_MIN_PASSWORD_LENGTH = 6; @@ -23,14 +30,20 @@ const MINIMUM_MIN_PASSWORD_LENGTH = 6; * * @internal */ -export class PasswordPolicyImpl { - constructor(response) { +export class PasswordPolicyImpl implements PasswordPolicyInternal { + customStrengthOptions: PasswordPolicyCustomStrengthOptionsInternal; + allowedNonAlphanumericCharacters: string; + enforcementState: string; + forceUpgradeOnSignin: boolean; + schemaVersion: number; + + constructor(response: PasswordPolicyResponseInternal) { // Only include custom strength options defined in the response. - const responseOptions = response.customStrengthOptions; + const responseOptions = response.customStrengthOptions ?? {}; this.customStrengthOptions = {}; this.customStrengthOptions.minPasswordLength = responseOptions.minPasswordLength ?? MINIMUM_MIN_PASSWORD_LENGTH; - if (responseOptions.maxPasswordLength) { + if (responseOptions.maxPasswordLength !== undefined) { this.customStrengthOptions.maxPasswordLength = responseOptions.maxPasswordLength; } if (responseOptions.containsLowercaseCharacter !== undefined) { @@ -53,7 +66,7 @@ export class PasswordPolicyImpl { this.enforcementState = response.enforcementState === 'ENFORCEMENT_STATE_UNSPECIFIED' ? 'OFF' - : response.enforcementState; + : (response.enforcementState ?? 'OFF'); // Use an empty string if no non-alphanumeric characters are specified in the response. this.allowedNonAlphanumericCharacters = @@ -63,8 +76,8 @@ export class PasswordPolicyImpl { this.schemaVersion = response.schemaVersion; } - validatePassword(password) { - const status = { + validatePassword(password: string): PasswordValidationStatusInternal { + const status: PasswordValidationStatusInternal = { isValid: true, passwordPolicy: this, }; @@ -82,18 +95,24 @@ export class PasswordPolicyImpl { return status; } - validatePasswordLengthOptions(password, status) { + private validatePasswordLengthOptions( + password: string, + status: PasswordValidationStatusInternal, + ): void { const minPasswordLength = this.customStrengthOptions.minPasswordLength; const maxPasswordLength = this.customStrengthOptions.maxPasswordLength; - if (minPasswordLength) { + if (minPasswordLength !== undefined) { status.meetsMinPasswordLength = password.length >= minPasswordLength; } - if (maxPasswordLength) { + if (maxPasswordLength !== undefined) { status.meetsMaxPasswordLength = password.length <= maxPasswordLength; } } - validatePasswordCharacterOptions(password, status) { + private validatePasswordCharacterOptions( + password: string, + status: PasswordValidationStatusInternal, + ): void { this.updatePasswordCharacterOptionsStatuses(status, false, false, false, false); for (let i = 0; i < password.length; i++) { @@ -108,13 +127,13 @@ export class PasswordPolicyImpl { } } - updatePasswordCharacterOptionsStatuses( - status, - containsLowercaseCharacter, - containsUppercaseCharacter, - containsNumericCharacter, - containsNonAlphanumericCharacter, - ) { + private updatePasswordCharacterOptionsStatuses( + status: PasswordValidationStatusInternal, + containsLowercaseCharacter: boolean, + containsUppercaseCharacter: boolean, + containsNumericCharacter: boolean, + containsNonAlphanumericCharacter: boolean, + ): void { if (this.customStrengthOptions.containsLowercaseLetter) { status.containsLowercaseLetter ||= containsLowercaseCharacter; } @@ -129,4 +148,3 @@ export class PasswordPolicyImpl { } } } -export default PasswordPolicyImpl; diff --git a/packages/auth/lib/password-policy/PasswordPolicyMixin.js b/packages/auth/lib/password-policy/PasswordPolicyMixin.ts similarity index 64% rename from packages/auth/lib/password-policy/PasswordPolicyMixin.js rename to packages/auth/lib/password-policy/PasswordPolicyMixin.ts index e8c916dd3a..803073eab0 100644 --- a/packages/auth/lib/password-policy/PasswordPolicyMixin.js +++ b/packages/auth/lib/password-policy/PasswordPolicyMixin.ts @@ -17,23 +17,32 @@ import { fetchPasswordPolicy } from './passwordPolicyApi'; import { PasswordPolicyImpl } from './PasswordPolicyImpl'; +import type { + PasswordPolicyHostInternal, + PasswordPolicyInternal, + PasswordPolicyMixinInternal, + PasswordValidationStatusInternal, +} from '../types/internal'; const EXPECTED_PASSWORD_POLICY_SCHEMA_VERSION = 1; +type PasswordPolicyMixinTarget = PasswordPolicyHostInternal & PasswordPolicyMixinInternal; +type PasswordPolicyMixinShape = PasswordPolicyMixinInternal & ThisType; + /** * Password policy mixin - provides password policy caching and validation. * Expects the target object to have: _tenantId, _projectPasswordPolicy, * _tenantPasswordPolicies, and app.options.apiKey */ -export const PasswordPolicyMixin = { - _getPasswordPolicyInternal() { +export const PasswordPolicyMixin: PasswordPolicyMixinShape = { + _getPasswordPolicyInternal(): PasswordPolicyInternal | null { if (this._tenantId === null) { return this._projectPasswordPolicy; } - return this._tenantPasswordPolicies[this._tenantId]; + return this._tenantPasswordPolicies[this._tenantId] ?? null; }, - async _updatePasswordPolicy() { + async _updatePasswordPolicy(): Promise { const response = await fetchPasswordPolicy(this); const passwordPolicy = new PasswordPolicyImpl(response); if (this._tenantId === null) { @@ -43,17 +52,25 @@ export const PasswordPolicyMixin = { } }, - async _recachePasswordPolicy() { + async _recachePasswordPolicy(): Promise { if (this._getPasswordPolicyInternal()) { await this._updatePasswordPolicy(); } }, - async validatePassword(password) { - if (!this._getPasswordPolicyInternal()) { + async validatePassword(password: string): Promise { + let passwordPolicy = this._getPasswordPolicyInternal(); + + if (!passwordPolicy) { await this._updatePasswordPolicy(); + passwordPolicy = this._getPasswordPolicyInternal(); + } + + if (!passwordPolicy) { + throw new Error( + 'firebase.auth().validatePassword(*) Failed to load password policy for validation.', + ); } - const passwordPolicy = this._getPasswordPolicyInternal(); if (passwordPolicy.schemaVersion !== EXPECTED_PASSWORD_POLICY_SCHEMA_VERSION) { throw new Error( diff --git a/packages/auth/lib/password-policy/passwordPolicyApi.js b/packages/auth/lib/password-policy/passwordPolicyApi.ts similarity index 61% rename from packages/auth/lib/password-policy/passwordPolicyApi.js rename to packages/auth/lib/password-policy/passwordPolicyApi.ts index 7d375deae8..c61623b8a8 100644 --- a/packages/auth/lib/password-policy/passwordPolicyApi.js +++ b/packages/auth/lib/password-policy/passwordPolicyApi.ts @@ -15,6 +15,16 @@ * */ +import type { PasswordPolicyHostInternal, PasswordPolicyResponseInternal } from '../types/internal'; + +function getErrorMessage(error: unknown): string { + if (error instanceof Error) { + return error.message; + } + + return String(error); +} + /** * Performs an API request to Firebase Console to get password policy json. * @@ -22,23 +32,29 @@ * @returns {Promise} A promise that resolves to the API response. * @throws {Error} Throws an error if the request fails or encounters an issue. */ -export async function fetchPasswordPolicy(auth) { +export async function fetchPasswordPolicy( + auth: PasswordPolicyHostInternal, +): Promise { + let response: Response; + try { // Identity toolkit API endpoint for password policy. Ensure this is enabled on Google cloud. const baseURL = 'https://identitytoolkit.googleapis.com/v2/passwordPolicy?key='; const apiKey = auth.app.options.apiKey; - const response = await fetch(`${baseURL}${apiKey}`); - if (!response.ok) { - const errorDetails = await response.text(); - throw new Error( - `firebase.auth().validatePassword(*) failed to fetch password policy from Firebase Console: ${response.statusText}. Details: ${errorDetails}`, - ); - } - return await response.json(); + response = await fetch(`${baseURL}${apiKey}`); } catch (error) { throw new Error( - `firebase.auth().validatePassword(*) Failed to fetch password policy: ${error.message}`, + `firebase.auth().validatePassword(*) Failed to fetch password policy: ${getErrorMessage(error)}`, ); } + + if (!response.ok) { + const errorDetails = await response.text(); + throw new Error( + `firebase.auth().validatePassword(*) failed to fetch password policy from Firebase Console: ${response.statusText}. Details: ${errorDetails}`, + ); + } + + return (await response.json()) as PasswordPolicyResponseInternal; } diff --git a/packages/auth/lib/providers/AppleAuthProvider.js b/packages/auth/lib/providers/AppleAuthProvider.ts similarity index 82% rename from packages/auth/lib/providers/AppleAuthProvider.js rename to packages/auth/lib/providers/AppleAuthProvider.ts index 172cc1d12e..5e1b46630d 100644 --- a/packages/auth/lib/providers/AppleAuthProvider.js +++ b/packages/auth/lib/providers/AppleAuthProvider.ts @@ -15,7 +15,9 @@ * */ -const providerId = 'apple.com'; +import type { AuthCredential } from '../types/auth'; + +const providerId = 'apple.com' as const; export default class AppleAuthProvider { constructor() { @@ -26,10 +28,10 @@ export default class AppleAuthProvider { return providerId; } - static credential(token, secret) { + static credential(token: string, secret?: string): AuthCredential { return { token, - secret, + secret: secret ?? '', providerId, }; } diff --git a/packages/auth/lib/providers/EmailAuthProvider.js b/packages/auth/lib/providers/EmailAuthProvider.ts similarity index 80% rename from packages/auth/lib/providers/EmailAuthProvider.js rename to packages/auth/lib/providers/EmailAuthProvider.ts index b722a7d29b..de1a28cdbe 100644 --- a/packages/auth/lib/providers/EmailAuthProvider.js +++ b/packages/auth/lib/providers/EmailAuthProvider.ts @@ -15,8 +15,10 @@ * */ -const linkProviderId = 'emailLink'; -const passwordProviderId = 'password'; +import type { AuthCredential } from '../types/auth'; + +const linkProviderId = 'emailLink' as const; +const passwordProviderId = 'password' as const; export default class EmailAuthProvider { constructor() { @@ -35,7 +37,7 @@ export default class EmailAuthProvider { return passwordProviderId; } - static credential(email, password) { + static credential(email: string, password: string): AuthCredential { return { token: email, secret: password, @@ -43,7 +45,7 @@ export default class EmailAuthProvider { }; } - static credentialWithLink(email, emailLink) { + static credentialWithLink(email: string, emailLink: string): AuthCredential { return { token: email, secret: emailLink, diff --git a/packages/auth/lib/providers/FacebookAuthProvider.js b/packages/auth/lib/providers/FacebookAuthProvider.ts similarity index 82% rename from packages/auth/lib/providers/FacebookAuthProvider.js rename to packages/auth/lib/providers/FacebookAuthProvider.ts index 50ae4d9bd5..220d461a11 100644 --- a/packages/auth/lib/providers/FacebookAuthProvider.js +++ b/packages/auth/lib/providers/FacebookAuthProvider.ts @@ -15,7 +15,9 @@ * */ -const providerId = 'facebook.com'; +import type { AuthCredential } from '../types/auth'; + +const providerId = 'facebook.com' as const; export default class FacebookAuthProvider { constructor() { @@ -26,10 +28,10 @@ export default class FacebookAuthProvider { return providerId; } - static credential(token, secret = '') { + static credential(token: string, secret?: string): AuthCredential { return { token, - secret, + secret: secret ?? '', providerId, }; } diff --git a/packages/auth/lib/providers/GithubAuthProvider.js b/packages/auth/lib/providers/GithubAuthProvider.ts similarity index 86% rename from packages/auth/lib/providers/GithubAuthProvider.js rename to packages/auth/lib/providers/GithubAuthProvider.ts index c0c9c6a253..af74e2234a 100644 --- a/packages/auth/lib/providers/GithubAuthProvider.js +++ b/packages/auth/lib/providers/GithubAuthProvider.ts @@ -15,7 +15,9 @@ * */ -const providerId = 'github.com'; +import type { AuthCredential } from '../types/auth'; + +const providerId = 'github.com' as const; export default class GithubAuthProvider { constructor() { @@ -26,7 +28,7 @@ export default class GithubAuthProvider { return providerId; } - static credential(token) { + static credential(token: string): AuthCredential { return { token, secret: '', diff --git a/packages/auth/lib/providers/GoogleAuthProvider.js b/packages/auth/lib/providers/GoogleAuthProvider.ts similarity index 82% rename from packages/auth/lib/providers/GoogleAuthProvider.js rename to packages/auth/lib/providers/GoogleAuthProvider.ts index 03cb7b55f0..3bcb8b6d6a 100644 --- a/packages/auth/lib/providers/GoogleAuthProvider.js +++ b/packages/auth/lib/providers/GoogleAuthProvider.ts @@ -15,7 +15,9 @@ * */ -const providerId = 'google.com'; +import type { AuthCredential } from '../types/auth'; + +const providerId = 'google.com' as const; export default class GoogleAuthProvider { constructor() { @@ -26,10 +28,10 @@ export default class GoogleAuthProvider { return providerId; } - static credential(token, secret) { + static credential(token: string, secret?: string): AuthCredential { return { token, - secret, + secret: secret ?? '', providerId, }; } diff --git a/packages/auth/lib/providers/OAuthProvider.js b/packages/auth/lib/providers/OAuthProvider.js deleted file mode 100644 index feab7e004f..0000000000 --- a/packages/auth/lib/providers/OAuthProvider.js +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2016-present Invertase Limited & Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this library 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. - * - */ - -export default class OAuthProvider { - /** @internal */ - #providerId = null; - /** @internal */ - #customParameters = {}; - /** @internal */ - #scopes = []; - - constructor(providerId) { - this.#providerId = providerId; - } - - static credential(idToken, accessToken) { - return { - token: idToken, - secret: accessToken, - providerId: 'oauth', - }; - } - - get PROVIDER_ID() { - return this.#providerId; - } - - setCustomParameters(customOAuthParameters) { - this.#customParameters = customOAuthParameters; - return this; - } - - getCustomParameters() { - return this.#customParameters; - } - - addScope(scope) { - if (!this.#scopes.includes(scope)) { - this.#scopes.push(scope); - } - return this; - } - - getScopes() { - return [...this.#scopes]; - } - - /** @internal */ - toObject() { - return { - providerId: this.#providerId, - scopes: this.#scopes, - customParameters: this.#customParameters, - }; - } -} diff --git a/packages/auth/lib/providers/OAuthProvider.ts b/packages/auth/lib/providers/OAuthProvider.ts new file mode 100644 index 0000000000..d7c5ea99f2 --- /dev/null +++ b/packages/auth/lib/providers/OAuthProvider.ts @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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 type { AuthCredential, CustomParameters } from '../types/auth'; + +export default class OAuthProvider { + /** @internal */ + private providerId: string; + /** @internal */ + private customParameters: CustomParameters = {}; + /** @internal */ + private scopes: string[] = []; + + constructor(providerId: string) { + this.providerId = providerId; + } + + static credential(idToken: string, accessToken?: string): AuthCredential { + return { + token: idToken, + secret: accessToken ?? '', + providerId: 'oauth', + }; + } + + get PROVIDER_ID() { + return this.providerId; + } + + setCustomParameters(customOAuthParameters: CustomParameters): OAuthProvider { + this.customParameters = customOAuthParameters; + return this; + } + + getCustomParameters(): CustomParameters { + return this.customParameters; + } + + addScope(scope: string): OAuthProvider { + if (!this.scopes.includes(scope)) { + this.scopes.push(scope); + } + return this; + } + + getScopes(): string[] { + return [...this.scopes]; + } + + /** @internal */ + toObject(): { providerId: string; scopes: string[]; customParameters: CustomParameters } { + return { + providerId: this.providerId, + scopes: this.scopes, + customParameters: this.customParameters, + }; + } +} diff --git a/packages/auth/lib/providers/OIDCAuthProvider.js b/packages/auth/lib/providers/OIDCAuthProvider.ts similarity index 80% rename from packages/auth/lib/providers/OIDCAuthProvider.js rename to packages/auth/lib/providers/OIDCAuthProvider.ts index d262a29548..37fb1e35a7 100644 --- a/packages/auth/lib/providers/OIDCAuthProvider.js +++ b/packages/auth/lib/providers/OIDCAuthProvider.ts @@ -15,7 +15,9 @@ * */ -const providerId = 'oidc.'; +import type { AuthCredential } from '../types/auth'; + +const providerId = 'oidc.' as const; export default class OIDCAuthProvider { constructor() { @@ -26,10 +28,10 @@ export default class OIDCAuthProvider { return providerId; } - static credential(oidcSuffix, idToken, accessToken) { + static credential(oidcSuffix: string, idToken: string, accessToken?: string): AuthCredential { return { token: idToken, - secret: accessToken, + secret: accessToken ?? '', providerId: providerId + oidcSuffix, }; } diff --git a/packages/auth/lib/providers/PhoneAuthProvider.js b/packages/auth/lib/providers/PhoneAuthProvider.js deleted file mode 100644 index 2fb238049c..0000000000 --- a/packages/auth/lib/providers/PhoneAuthProvider.js +++ /dev/null @@ -1,52 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* - * Copyright (c) 2016-present Invertase Limited & Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this library 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. - * - */ - -const providerId = 'phone'; - -export default class PhoneAuthProvider { - constructor(auth) { - if (auth === undefined) { - throw new Error('`new PhoneAuthProvider()` is not supported on the native Firebase SDKs.'); - } - this._auth = auth; - } - - static get PROVIDER_ID() { - return providerId; - } - - static credential(verificationId, code) { - return { - token: verificationId, - secret: code, - providerId, - }; - } - - verifyPhoneNumber(phoneInfoOptions, appVerifier) { - if (phoneInfoOptions.multiFactorHint) { - return this._auth.app - .auth() - .verifyPhoneNumberWithMultiFactorInfo( - phoneInfoOptions.multiFactorHint, - phoneInfoOptions.session, - ); - } - return this._auth.app.auth().verifyPhoneNumberForMultiFactor(phoneInfoOptions); - } -} diff --git a/packages/auth/lib/providers/PhoneAuthProvider.ts b/packages/auth/lib/providers/PhoneAuthProvider.ts new file mode 100644 index 0000000000..b6e09cc9ad --- /dev/null +++ b/packages/auth/lib/providers/PhoneAuthProvider.ts @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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 type { + ApplicationVerifier, + AuthCredential, + MultiFactorInfo, + PhoneMultiFactorEnrollInfoOptions, + PhoneMultiFactorSignInInfoOptions, +} from '../types/auth'; + +const providerId = 'phone' as const; + +type PhoneAuthProviderAuth = { + app: { + auth(): { + verifyPhoneNumberWithMultiFactorInfo( + hint: Pick, + session: PhoneMultiFactorSignInInfoOptions['session'], + ): Promise; + verifyPhoneNumberForMultiFactor( + phoneInfoOptions: PhoneMultiFactorEnrollInfoOptions, + ): Promise; + }; + }; +}; + +type SupportedPhoneInfoOptions = + | PhoneMultiFactorEnrollInfoOptions + | PhoneMultiFactorSignInInfoOptions; + +function isPhoneMultiFactorSignInOptions( + phoneInfoOptions: SupportedPhoneInfoOptions, +): phoneInfoOptions is PhoneMultiFactorSignInInfoOptions & { multiFactorHint: MultiFactorInfo } { + return 'multiFactorHint' in phoneInfoOptions && phoneInfoOptions.multiFactorHint !== undefined; +} + +function isPhoneMultiFactorEnrollOptions( + phoneInfoOptions: SupportedPhoneInfoOptions, +): phoneInfoOptions is PhoneMultiFactorEnrollInfoOptions { + return 'phoneNumber' in phoneInfoOptions; +} + +export default class PhoneAuthProvider { + private readonly _auth: PhoneAuthProviderAuth; + + constructor(auth: PhoneAuthProviderAuth) { + if (auth === undefined) { + throw new Error('`new PhoneAuthProvider()` is not supported on the native Firebase SDKs.'); + } + this._auth = auth; + } + + static get PROVIDER_ID() { + return providerId; + } + + static credential(verificationId: string, code: string): AuthCredential { + return { + token: verificationId, + secret: code, + providerId, + }; + } + + verifyPhoneNumber( + phoneInfoOptions: SupportedPhoneInfoOptions, + _appVerifier?: ApplicationVerifier, + ): Promise { + if (isPhoneMultiFactorSignInOptions(phoneInfoOptions)) { + return this._auth.app + .auth() + .verifyPhoneNumberWithMultiFactorInfo( + phoneInfoOptions.multiFactorHint, + phoneInfoOptions.session, + ); + } + + if (isPhoneMultiFactorEnrollOptions(phoneInfoOptions)) { + return this._auth.app.auth().verifyPhoneNumberForMultiFactor(phoneInfoOptions); + } + + throw new Error( + '`PhoneAuthProvider.verifyPhoneNumber()` requires either a multi-factor hint, a multi-factor uid, or enrollment phone info.', + ); + } +} diff --git a/packages/auth/lib/providers/TwitterAuthProvider.js b/packages/auth/lib/providers/TwitterAuthProvider.ts similarity index 84% rename from packages/auth/lib/providers/TwitterAuthProvider.js rename to packages/auth/lib/providers/TwitterAuthProvider.ts index 62150cc2a7..f5f54288da 100644 --- a/packages/auth/lib/providers/TwitterAuthProvider.js +++ b/packages/auth/lib/providers/TwitterAuthProvider.ts @@ -15,7 +15,9 @@ * */ -const providerId = 'twitter.com'; +import type { AuthCredential } from '../types/auth'; + +const providerId = 'twitter.com' as const; export default class TwitterAuthProvider { constructor() { @@ -26,7 +28,7 @@ export default class TwitterAuthProvider { return providerId; } - static credential(token, secret) { + static credential(token: string, secret: string): AuthCredential { return { token, secret, diff --git a/packages/auth/lib/types/auth.ts b/packages/auth/lib/types/auth.ts new file mode 100644 index 0000000000..579fbe045e --- /dev/null +++ b/packages/auth/lib/types/auth.ts @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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 type { FirebaseApp, ReactNativeFirebase } from '@react-native-firebase/app'; +import type { FirebaseAuthTypes } from './namespaced'; + +export type CompleteFn = () => void; +export type ErrorFn = (error: Error) => void; +export type NextFn = (value: T) => void; +export type Unsubscribe = () => void; + +interface Observer { + next?: NextFn | null; + error?: ErrorFn | null; + complete?: CompleteFn | null; +} + +export const ActionCodeOperation = { + EMAIL_SIGNIN: 'EMAIL_SIGNIN', + PASSWORD_RESET: 'PASSWORD_RESET', + RECOVER_EMAIL: 'RECOVER_EMAIL', + REVERT_SECOND_FACTOR_ADDITION: 'REVERT_SECOND_FACTOR_ADDITION', + VERIFY_AND_CHANGE_EMAIL: 'VERIFY_AND_CHANGE_EMAIL', + VERIFY_EMAIL: 'VERIFY_EMAIL', +} as const; + +export const FactorId = { + PHONE: 'phone', + TOTP: 'totp', +} as const; + +export const OperationType = { + LINK: 'link', + REAUTHENTICATE: 'reauthenticate', + SIGN_IN: 'signIn', +} as const; + +export const ProviderId = { + FACEBOOK: 'facebook.com', + GITHUB: 'github.com', + GOOGLE: 'google.com', + PASSWORD: 'password', + PHONE: 'phone', + TWITTER: 'twitter.com', +} as const; + +export const SignInMethod = { + EMAIL_LINK: 'emailLink', + EMAIL_PASSWORD: 'password', + FACEBOOK: 'facebook.com', + GITHUB: 'github.com', + GOOGLE: 'google.com', + PHONE: 'phone', + TWITTER: 'twitter.com', +} as const; + +export interface Auth { + readonly app: FirebaseApp; + readonly name: string; + readonly config: Config; + setPersistence(persistence: Persistence): Promise; + languageCode: string | null; + tenantId: string | null; + readonly settings: AuthSettings; + onAuthStateChanged( + nextOrObserver: NextOrObserver, + error?: ErrorFn, + completed?: CompleteFn, + ): Unsubscribe; + beforeAuthStateChanged( + callback: (user: User | null) => void | Promise, + onAbort?: () => void, + ): Unsubscribe; + onIdTokenChanged( + nextOrObserver: NextOrObserver, + error?: ErrorFn, + completed?: CompleteFn, + ): Unsubscribe; + authStateReady(): Promise; + readonly currentUser: User | null; + readonly emulatorConfig: EmulatorConfig | null; + updateCurrentUser(user: User | null): Promise; + useDeviceLanguage(): void; + signOut(): Promise; +} + +export interface AuthError extends ReactNativeFirebase.NativeFirebaseError { + readonly customData: { + readonly appName: string; + readonly email?: string; + readonly phoneNumber?: string; + readonly tenantId?: string; + }; +} + +export type NativeFirebaseAuthError = FirebaseAuthTypes.NativeFirebaseAuthError; +export type AuthCredential = FirebaseAuthTypes.AuthCredential; +export type OIDCProvider = FirebaseAuthTypes.OIDCProvider; +export interface MultiFactorError extends AuthError { + readonly customData: AuthError['customData'] & { + readonly operationType: (typeof OperationType)[keyof typeof OperationType]; + }; +} +export type PhoneAuthListener = FirebaseAuthTypes.PhoneAuthListener; +export type PhoneAuthError = FirebaseAuthTypes.PhoneAuthError; +export type PhoneAuthSnapshot = FirebaseAuthTypes.PhoneAuthSnapshot; +export type ActionCodeURL = FirebaseAuthTypes.ActionCodeURL; + +export interface Config { + apiKey: string; + apiHost: string; + apiScheme: string; + tokenApiHost: string; + sdkClientVersion: string; + authDomain?: string; +} + +export interface AuthErrorMap {} + +export interface PopupRedirectResolver {} + +export interface Dependencies { + persistence?: Persistence | Persistence[]; + popupRedirectResolver?: PopupRedirectResolver; + errorMap?: AuthErrorMap; +} + +export interface ApplicationVerifier { + readonly type: string; + verify(): Promise; +} + +export type CustomParameters = Record; + +export interface AuthProvider { + readonly providerId: string; +} + +export interface AuthSettings { + appVerificationDisabledForTesting: boolean; +} + +export interface EmulatorConfig { + readonly protocol: string; + readonly host: string; + readonly port: number | null; + readonly options: { + readonly disableWarnings: boolean; + }; +} + +export interface PasswordPolicy { + readonly customStrengthOptions: { + readonly minPasswordLength?: number; + readonly maxPasswordLength?: number; + readonly containsLowercaseLetter?: boolean; + readonly containsUppercaseLetter?: boolean; + readonly containsNumericCharacter?: boolean; + readonly containsNonAlphanumericCharacter?: boolean; + }; + readonly allowedNonAlphanumericCharacters: string; + readonly enforcementState: string; + readonly forceUpgradeOnSignin: boolean; +} + +export interface PasswordValidationStatus { + readonly isValid: boolean; + readonly meetsMinPasswordLength?: boolean; + readonly meetsMaxPasswordLength?: boolean; + readonly containsLowercaseLetter?: boolean; + readonly containsUppercaseLetter?: boolean; + readonly containsNumericCharacter?: boolean; + readonly containsNonAlphanumericCharacter?: boolean; + readonly passwordPolicy: PasswordPolicy; +} + +export interface Persistence { + readonly type: 'SESSION' | 'LOCAL' | 'NONE' | 'COOKIE'; +} + +export type NextOrObserver = NextFn | Observer; + +export interface ParsedToken { + exp?: string; + sub?: string; + auth_time?: string; + iat?: string; + firebase?: { + sign_in_provider?: string; + sign_in_second_factor?: string; + identities?: Record; + }; + [key: string]: unknown; +} + +export type UserProfile = Record; + +export interface AdditionalUserInfo { + readonly isNewUser: boolean; + readonly profile: Record | null; + readonly providerId: string | null; + readonly username?: string | null; +} + +export interface UserInfo { + readonly displayName: string | null; + readonly email: string | null; + readonly phoneNumber: string | null; + readonly photoURL: string | null; + readonly providerId: string; + readonly uid: string; +} + +export interface UserMetadata { + readonly creationTime?: string; + readonly lastSignInTime?: string; +} + +export interface IdTokenResult { + authTime: string; + expirationTime: string; + issuedAtTime: string; + signInProvider: string | null; + signInSecondFactor: string | null; + token: string; + claims: ParsedToken; +} + +export interface User extends UserInfo { + readonly emailVerified: boolean; + readonly isAnonymous: boolean; + readonly metadata: UserMetadata; + readonly providerData: UserInfo[]; + readonly refreshToken: string; + readonly tenantId: string | null; + delete(): Promise; + getIdToken(forceRefresh?: boolean): Promise; + getIdTokenResult(forceRefresh?: boolean): Promise; + reload(): Promise; + toJSON(): object; +} + +export interface UserCredential { + user: User; + providerId: string | null; + operationType: (typeof OperationType)[keyof typeof OperationType]; +} + +export interface ConfirmationResult { + readonly verificationId: string; + confirm(verificationCode: string): Promise; +} + +export interface ActionCodeSettings { + android?: { + installApp?: boolean; + minimumVersion?: string; + packageName: string; + }; + handleCodeInApp?: boolean; + iOS?: { + bundleId: string; + }; + url: string; + dynamicLinkDomain?: string; + linkDomain?: string; +} + +export interface ActionCodeInfo { + data: { + email?: string | null; + multiFactorInfo?: MultiFactorInfo | null; + previousEmail?: string | null; + }; + operation: (typeof ActionCodeOperation)[keyof typeof ActionCodeOperation]; +} + +export interface MultiFactorAssertion { + readonly factorId: (typeof FactorId)[keyof typeof FactorId]; +} + +export interface MultiFactorInfo { + readonly uid: string; + readonly displayName?: string | null; + readonly enrollmentTime: string; + readonly factorId: (typeof FactorId)[keyof typeof FactorId]; +} + +export interface MultiFactorSession {} + +export interface MultiFactorResolver { + readonly hints: MultiFactorInfo[]; + readonly session: MultiFactorSession; + resolveSignIn(assertion: MultiFactorAssertion): Promise; +} + +export interface MultiFactorUser { + readonly enrolledFactors: MultiFactorInfo[]; + getSession(): Promise; + enroll(assertion: MultiFactorAssertion, displayName?: string | null): Promise; + unenroll(option: MultiFactorInfo | string): Promise; +} + +export interface PhoneMultiFactorAssertion extends MultiFactorAssertion {} + +export interface PhoneMultiFactorEnrollInfoOptions { + phoneNumber: string; + session: MultiFactorSession; +} + +export interface PhoneMultiFactorInfo extends MultiFactorInfo { + readonly phoneNumber: string; +} + +export interface PhoneMultiFactorSignInInfoOptions { + multiFactorHint?: MultiFactorInfo; + multiFactorUid?: string; + session: MultiFactorSession; +} + +export interface PhoneSingleFactorInfoOptions { + phoneNumber: string; +} + +export type PhoneInfoOptions = + | PhoneSingleFactorInfoOptions + | PhoneMultiFactorEnrollInfoOptions + | PhoneMultiFactorSignInInfoOptions; + +export interface TotpMultiFactorAssertion extends MultiFactorAssertion {} + +export interface TotpMultiFactorInfo extends MultiFactorInfo {} diff --git a/packages/auth/lib/types/internal.ts b/packages/auth/lib/types/internal.ts new file mode 100644 index 0000000000..27c8213ef5 --- /dev/null +++ b/packages/auth/lib/types/internal.ts @@ -0,0 +1,473 @@ +/* + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library 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 type { ReactNativeFirebase } from '@react-native-firebase/app'; +import type {} from '@react-native-firebase/app/dist/module/internal/NativeModules'; +import type { + ModuleConfig, + NativeErrorUserInfo, +} from '@react-native-firebase/app/dist/module/types/internal'; +import type EventEmitter from 'react-native/Libraries/vendor/emitter/EventEmitter'; +import type { + ActionCodeInfo, + ActionCodeSettings, + Auth, + AuthCredential, + AuthProvider, + IdTokenResult, + MultiFactorAssertion, + PhoneAuthListener, + User, + UserCredential, +} from './auth'; +import type { CallbackOrObserver, FirebaseAuthTypes } from './namespaced'; + +export type AuthModularDeprecationArg = string; + +export type WithAuthDeprecationArg = F extends (...args: infer P) => infer R + ? (...args: [...P, AuthModularDeprecationArg]) => R + : never; + +export interface AppWithAuthInternal { + auth(deprecationArg?: AuthModularDeprecationArg): Auth; +} + +export type AuthListenerCallbackInternal = (user: User | null) => void; + +export type AuthProviderWithObjectInternal = AuthProvider & { + toObject(): Record; +}; + +export type UserCredentialWithAdditionalUserInfoInternal = UserCredential & { + user: FirebaseAuthTypes.User; + additionalUserInfo?: FirebaseAuthTypes.AdditionalUserInfo; +}; + +export type UserCredentialResultInternal = FirebaseAuthTypes.UserCredential & + Partial>; + +export type ActionCodeInfoResultInternal = FirebaseAuthTypes.ActionCodeInfo | ActionCodeInfo; + +export type ConfirmationResultResultInternal = { + verificationId: string | null; + confirm(verificationCode: string): Promise; +}; + +export type MultiFactorResolverResultInternal = { + hints: FirebaseAuthTypes.MultiFactorInfo[]; + session: FirebaseAuthTypes.MultiFactorSession; + resolveSignIn( + assertion: FirebaseAuthTypes.MultiFactorAssertion | MultiFactorAssertion, + ): Promise; +}; + +export type MultiFactorUserSourceInternal = FirebaseAuthTypes.User; + +export type MultiFactorUserResultInternal = { + enrolledFactors: FirebaseAuthTypes.MultiFactorInfo[]; + getSession(): Promise; + enroll( + assertion: FirebaseAuthTypes.MultiFactorAssertion | MultiFactorAssertion, + displayName?: string | null, + ): Promise; + unenroll(option: FirebaseAuthTypes.MultiFactorInfo | string): Promise; +}; + +export type MultiFactorEnrollmentAssertionInternal = + | { + factorId: 'phone'; + token: string; + secret: string; + } + | { + factorId: 'totp'; + totpSecret: string; + verificationCode: string; + }; + +export interface NativeUserMetadataInternal { + creationTime: string; + lastSignInTime: string; +} + +export interface NativeUserInfoInternal { + uid: string; + providerId: string; + displayName?: string | null; + email?: string | null; + phoneNumber?: string | null; + photoURL?: string | null; + tenantId?: string | null; +} + +export interface NativeUserInternal { + uid: string; + providerId: string; + providerData: NativeUserInfoInternal[]; + displayName?: string | null; + email?: string | null; + emailVerified?: boolean; + isAnonymous?: boolean; + metadata: NativeUserMetadataInternal; + multiFactor?: FirebaseAuthTypes.MultiFactor | null; + phoneNumber?: string | null; + photoURL?: string | null; + tenantId?: string | null; +} + +export interface NativeUserCredentialInternal { + additionalUserInfo?: FirebaseAuthTypes.AdditionalUserInfo; + user: NativeUserInternal; +} + +export interface NativePhoneAuthCredentialInternal { + verificationId: string; + code?: string | null; +} + +export interface NativePhoneAuthErrorInternal { + verificationId: string; + error: NativeErrorUserInfo; +} + +export interface AuthStateChangedEventInternal { + user: NativeUserInternal | null; +} + +export interface AuthIdTokenChangedEventInternal { + user: NativeUserInternal | null; +} + +export interface PhoneAuthStateChangedEventInternal { + requestKey: string; + type: + | 'onCodeSent' + | 'onVerificationFailed' + | 'onVerificationComplete' + | 'onCodeAutoRetrievalTimeout'; + state: NativePhoneAuthCredentialInternal | NativePhoneAuthErrorInternal; +} + +export type AuthNativeEventInternal = + | AuthStateChangedEventInternal + | AuthIdTokenChangedEventInternal + | PhoneAuthStateChangedEventInternal; + +export interface PasswordPolicyCustomStrengthOptionsInternal { + minPasswordLength?: number; + maxPasswordLength?: number; + containsLowercaseLetter?: boolean; + containsUppercaseLetter?: boolean; + containsNumericCharacter?: boolean; + containsNonAlphanumericCharacter?: boolean; +} + +export interface PasswordPolicyInternal { + readonly customStrengthOptions: PasswordPolicyCustomStrengthOptionsInternal; + readonly allowedNonAlphanumericCharacters: string; + readonly enforcementState: string; + readonly forceUpgradeOnSignin: boolean; + readonly schemaVersion: number; + validatePassword(password: string): PasswordValidationStatusInternal; +} + +export interface PasswordPolicyResponseCustomStrengthOptionsInternal { + minPasswordLength?: number; + maxPasswordLength?: number; + containsLowercaseCharacter?: boolean; + containsUppercaseCharacter?: boolean; + containsNumericCharacter?: boolean; + containsNonAlphanumericCharacter?: boolean; +} + +export interface PasswordPolicyResponseInternal { + customStrengthOptions?: PasswordPolicyResponseCustomStrengthOptionsInternal; + allowedNonAlphanumericCharacters?: string[]; + enforcementState?: string; + forceUpgradeOnSignin?: boolean; + schemaVersion: number; +} + +export type PasswordValidationStatusInternal = { + isValid: boolean; + meetsMinPasswordLength?: boolean; + meetsMaxPasswordLength?: boolean; + containsLowercaseLetter?: boolean; + containsUppercaseLetter?: boolean; + containsNumericCharacter?: boolean; + containsNonAlphanumericCharacter?: boolean; + passwordPolicy: PasswordPolicyInternal; +}; + +export interface PasswordPolicyHostInternal { + app: ReactNativeFirebase.FirebaseApp; + _tenantId: string | null; + _projectPasswordPolicy: PasswordPolicyInternal | null; + _tenantPasswordPolicies: Record; +} + +export interface PasswordPolicyMixinInternal { + _getPasswordPolicyInternal(): PasswordPolicyInternal | null; + _updatePasswordPolicy(): Promise; + _recachePasswordPolicy(): Promise; + validatePassword(password: string): Promise; +} + +export interface RNFBAuthModule { + APP_LANGUAGE: Record; + APP_USER: Record; + addAuthStateListener(): void | Promise; + addIdTokenListener(): void | Promise; + configureAuthDomain(): void | Promise; + setLanguageCode(code: string | null): Promise; + setTenantId(tenantId: string): Promise; + signOut(): Promise; + signInAnonymously(): Promise; + signInWithPhoneNumber( + phoneNumber: string, + forceResend?: boolean, + ): Promise; + verifyPhoneNumber( + phoneNumber: string, + requestKey: string, + timeout?: number, + forceResend?: boolean, + ): void | Promise; + verifyPhoneNumberWithMultiFactorInfo( + uid: string, + session: FirebaseAuthTypes.MultiFactorSession, + ): Promise; + verifyPhoneNumberForMultiFactor( + phoneNumber: string, + session: FirebaseAuthTypes.MultiFactorSession, + ): Promise; + resolveMultiFactorSignIn( + session: FirebaseAuthTypes.MultiFactorSession, + verificationId: string, + verificationCode: string, + ): Promise; + resolveTotpSignIn( + session: FirebaseAuthTypes.MultiFactorSession, + uid: string, + totpSecret: string, + ): Promise; + createUserWithEmailAndPassword( + email: string, + password: string, + ): Promise; + signInWithEmailAndPassword( + email: string, + password: string, + ): Promise; + signInWithCustomToken(customToken: string): Promise; + signInWithCredential( + providerId: string, + token: string, + secret?: string | null, + ): Promise; + revokeToken(authorizationCode: string): Promise; + sendPasswordResetEmail( + email: string, + actionCodeSettings?: FirebaseAuthTypes.ActionCodeSettings | null, + ): Promise; + sendSignInLinkToEmail( + email: string, + actionCodeSettings?: FirebaseAuthTypes.ActionCodeSettings, + ): Promise; + isSignInWithEmailLink(emailLink: string): Promise; + signInWithEmailLink(email: string, emailLink: string): Promise; + confirmPasswordReset(code: string, newPassword: string): Promise; + applyActionCode(code: string): Promise; + checkActionCode(code: string): Promise; + fetchSignInMethodsForEmail(email: string): Promise; + verifyPasswordResetCode(code: string): Promise; + useUserAccessGroup(userAccessGroup: string): Promise; + signInWithProvider(provider: Record): Promise; + useEmulator(host: string, port?: number): void; + getCustomAuthDomain(): Promise; + confirmationResultConfirm(verificationCode: string): Promise; + delete(): Promise; + getIdToken(forceRefresh: boolean): Promise; + getIdTokenResult(forceRefresh: boolean): Promise; + linkWithCredential( + providerId: string, + token: string, + secret?: string | null, + ): Promise; + linkWithProvider(provider: Record): Promise; + reauthenticateWithCredential( + providerId: string, + token: string, + secret?: string | null, + ): Promise; + reauthenticateWithProvider( + provider: Record, + ): Promise; + reload(): Promise; + sendEmailVerification( + actionCodeSettings?: FirebaseAuthTypes.ActionCodeSettings, + ): Promise; + unlink(providerId: string): Promise; + updateEmail(email: string): Promise; + updatePassword(password: string): Promise; + updatePhoneNumber( + providerId: string, + token: string, + secret?: string | null, + ): Promise; + updateProfile( + updates: { displayName?: string | null; photoURL?: string | null }, + ): Promise; + verifyBeforeUpdateEmail( + newEmail: string, + actionCodeSettings?: FirebaseAuthTypes.ActionCodeSettings, + ): Promise; + forceRecaptchaFlowForTesting(forceRecaptchaFlow: boolean): void | Promise; + setAppVerificationDisabledForTesting(disabled: boolean): void | Promise; + setAutoRetrievedSmsCodeForPhoneNumber(phoneNumber: string, smsCode: string): Promise; + getSession(): Promise; + finalizeMultiFactorEnrollment(token: string, secret: string, displayName?: string): Promise; + finalizeTotpEnrollment( + totpSecret: string, + verificationCode: string, + displayName?: string, + ): Promise; + unenrollMultiFactor(enrollmentId: string | FirebaseAuthTypes.MultiFactorInfo): Promise; + getMultiFactorResolver(error: unknown): MultiFactorResolverResultInternal | null; + generateTotpSecret(session: FirebaseAuthTypes.MultiFactorSession): Promise<{ secretKey: string }>; + generateQrCodeUrl(secretKey: string, accountName: string, issuer: string): Promise; + openInOtpApp(secretKey: string, qrCodeUrl: string): string | void; + assertionForSignIn( + uid: string, + verificationCode: string, + ): { uid: string; verificationCode: string }; +} + +export type AuthInternal = Auth & { + app: ReactNativeFirebase.FirebaseApp; + currentUser: FirebaseAuthTypes.User | null; + applyActionCode(code: string): Promise; + checkActionCode(code: string): Promise; + confirmPasswordReset(code: string, newPassword: string): Promise; + createUserWithEmailAndPassword( + email: string, + password: string, + ): Promise; + fetchSignInMethodsForEmail(email: string): Promise; + getCustomAuthDomain(): Promise; + getMultiFactorResolver(error: unknown): MultiFactorResolverResultInternal | null; + isSignInWithEmailLink(emailLink: string): Promise; + onAuthStateChanged( + listenerOrObserver: CallbackOrObserver, + ): () => void; + onIdTokenChanged( + listenerOrObserver: CallbackOrObserver, + ): () => void; + sendPasswordResetEmail( + email: string, + actionCodeSettings?: ActionCodeSettings | null, + ): Promise; + sendSignInLinkToEmail( + email: string, + actionCodeSettings?: ActionCodeSettings, + ): Promise; + setLanguageCode(code: string | null): Promise; + signInAnonymously(): Promise; + signInWithCredential(credential: AuthCredential): Promise; + signInWithCustomToken(customToken: string): Promise; + signInWithEmailAndPassword( + email: string, + password: string, + ): Promise; + signInWithEmailLink( + email: string, + emailLink: string, + ): Promise; + signInWithPhoneNumber( + phoneNumber: string, + forceResend?: boolean, + ): Promise; + signInWithPopup( + provider: AuthProviderWithObjectInternal, + ): Promise; + signInWithRedirect( + provider: AuthProviderWithObjectInternal, + ): Promise; + signOut(): Promise; + useEmulator(url: string): void; + useUserAccessGroup(userAccessGroup: string): Promise; + verifyPhoneNumber( + phoneNumber: string, + autoVerifyTimeoutOrForceResend?: number | boolean, + forceResend?: boolean, + ): PhoneAuthListener; + verifyPasswordResetCode(code: string): Promise; + native: RNFBAuthModule; + emitter: EventEmitter; + eventNameForApp(...args: Array): string; + _config: ModuleConfig; + _tenantId: string | null; + _projectPasswordPolicy: PasswordPolicyInternal | null; + _tenantPasswordPolicies: Record; + _setUser(user?: NativeUserInternal | null): FirebaseAuthTypes.User | null; + _setUserCredential( + userCredential: NativeUserCredentialInternal, + ): UserCredentialWithAdditionalUserInfoInternal; + resolveMultiFactorSignIn( + session: FirebaseAuthTypes.MultiFactorSession, + verificationId: string, + verificationCode: string, + ): Promise; + resolveTotpSignIn( + session: FirebaseAuthTypes.MultiFactorSession, + uid: string, + totpSecret: string, + ): Promise; +} & PasswordPolicyMixinInternal; + +export type UserInternal = FirebaseAuthTypes.User & { + _auth?: AuthInternal; + getIdTokenResult(forceRefresh?: boolean): Promise; + linkWithCredential(credential: AuthCredential): Promise; + linkWithPopup(provider: AuthProviderWithObjectInternal): Promise; + linkWithRedirect( + provider: AuthProviderWithObjectInternal, + ): Promise; + reauthenticateWithCredential( + credential: AuthCredential, + ): Promise; + reauthenticateWithPopup( + provider: AuthProviderWithObjectInternal, + ): Promise; + reauthenticateWithRedirect(provider: AuthProviderWithObjectInternal): Promise; + sendEmailVerification(actionCodeSettings?: ActionCodeSettings): Promise; + unlink(providerId: string): Promise; + updateEmail(email: string): Promise; + updatePassword(password: string): Promise; + updatePhoneNumber(credential: AuthCredential): Promise; + updateProfile(updates: { displayName?: string | null; photoURL?: string | null }): Promise; + verifyBeforeUpdateEmail(newEmail: string, actionCodeSettings?: ActionCodeSettings): Promise; +}; + +export type ConfirmationResultInternal = FirebaseAuthTypes.ConfirmationResult; +export type MultiFactorResolverInternal = FirebaseAuthTypes.MultiFactorResolver; + +declare module '@react-native-firebase/app/dist/module/internal/NativeModules' { + interface ReactNativeFirebaseNativeModules { + RNFBAuthModule: RNFBAuthModule; + } +} diff --git a/packages/auth/lib/index.d.ts b/packages/auth/lib/types/namespaced.ts similarity index 98% rename from packages/auth/lib/index.d.ts rename to packages/auth/lib/types/namespaced.ts index 7e87730bb7..c97bb8ef38 100644 --- a/packages/auth/lib/index.d.ts +++ b/packages/auth/lib/types/namespaced.ts @@ -48,10 +48,45 @@ import { ReactNativeFirebase } from '@react-native-firebase/app'; * * @firebase auth */ -export namespace FirebaseAuthTypes { +/** + * @deprecated Use the exported auth types directly instead. + * FirebaseAuthTypes namespace is kept for backwards compatibility. + */ +/* eslint-disable @typescript-eslint/no-namespace */ +export declare namespace FirebaseAuthTypes { import FirebaseModule = ReactNativeFirebase.FirebaseModule; import NativeFirebaseError = ReactNativeFirebase.NativeFirebaseError; + export interface AuthError extends NativeFirebaseError { + customData?: Record; + } + + export const OperationType: { + LINK: 'link'; + REAUTHENTICATE: 'reauthenticate'; + SIGN_IN: 'signIn'; + }; + + export interface ActionCodeURL { + apiKey: string; + code: string; + continueUrl?: string | null; + languageCode?: string | null; + operation: string; + tenantId?: string | null; + } + + export interface ApplicationVerifier { + readonly type: string; + verify(): Promise; + } + + export interface PasswordPolicy { + readonly enforcementState?: string; + readonly forceUpgradeOnSignin?: boolean; + readonly schemaVersion: number; + } + export interface NativeFirebaseAuthError extends NativeFirebaseError { userInfo: { /** @@ -286,7 +321,7 @@ export namespace FirebaseAuthTypes { * 3- the return value of generateQrCodeUrl is a Promise because react-native bridge is async * @public */ - export declare class TotpSecret { + export class TotpSecret { /** used internally to support non-default auth instances */ private readonly auth; /** @@ -306,7 +341,7 @@ export namespace FirebaseAuthTypes { * @param issuer issuer of the TOTP (likely the app name). * @returns A Promise that resolves to a QR code URL string. */ - async generateQrCodeUrl(accountName?: string, issuer?: string): Promise; + generateQrCodeUrl(accountName?: string, issuer?: string): Promise; /** * Opens the specified QR Code URL in an OTP authenticator app on the device. @@ -332,11 +367,11 @@ export namespace FirebaseAuthTypes { */ generateSecret( session: FirebaseAuthTypes.MultiFactorSession, - auth: FirebaseAuthTypes.Auth, + auth: FirebaseAuthTypes.Module, ): Promise; } - export declare interface MultiFactorError extends AuthError { + export interface MultiFactorError extends AuthError { /** Details about the MultiFactorError. */ readonly customData: AuthError['customData'] & { /** @@ -349,6 +384,9 @@ export namespace FirebaseAuthTypes { /** * firebase.auth.X */ + /** + * @deprecated Use the package default export or modular APIs instead. + */ export interface Statics { /** * Return the #{@link MultiFactorUser} instance for the current user. @@ -1646,6 +1684,9 @@ export namespace FirebaseAuthTypes { * * TODO @salakar missing updateCurrentUser */ + /** + * @deprecated Use the package default export or modular APIs instead. + */ export class Module extends FirebaseModule { /** * The current `FirebaseApp` instance for this Firebase service. @@ -1671,7 +1712,7 @@ export namespace FirebaseAuthTypes { * const language = firebase.auth().languageCode; * ``` */ - languageCode: string; + get languageCode(): string; /** * Returns the current `AuthSettings`. */ @@ -2294,33 +2335,12 @@ export namespace FirebaseAuthTypes { * Gets the config used to initialize this auth instance. This is to match Firebase JS SDK behavior. * It returns an empty map as the config is not available in the native SDK. */ - get config(): Map; + get config(): Record; } } export type CallbackOrObserver any> = T | { next: T }; -type AuthNamespace = ReactNativeFirebase.FirebaseModuleWithStaticsAndApp< - FirebaseAuthTypes.Module, - FirebaseAuthTypes.Statics -> & { - auth: ReactNativeFirebase.FirebaseModuleWithStaticsAndApp< - FirebaseAuthTypes.Module, - FirebaseAuthTypes.Statics - >; - firebase: ReactNativeFirebase.Module; - app(name?: string): ReactNativeFirebase.FirebaseApp; -}; - -declare const defaultExport: AuthNamespace; - -export const firebase: ReactNativeFirebase.Module & { - auth: typeof defaultExport; - app(name?: string): ReactNativeFirebase.FirebaseApp & { auth(): FirebaseAuthTypes.Module }; -}; - -export default defaultExport; - /** * Attach namespace to `firebase.` and `FirebaseApp.`. */ @@ -2335,5 +2355,3 @@ declare module '@react-native-firebase/app' { } } } - -export * from './modular'; diff --git a/packages/auth/lib/web/RNFBAuthModule.android.js b/packages/auth/lib/web/RNFBAuthModule.android.ts similarity index 100% rename from packages/auth/lib/web/RNFBAuthModule.android.js rename to packages/auth/lib/web/RNFBAuthModule.android.ts diff --git a/packages/auth/lib/web/RNFBAuthModule.ios.js b/packages/auth/lib/web/RNFBAuthModule.ios.ts similarity index 100% rename from packages/auth/lib/web/RNFBAuthModule.ios.js rename to packages/auth/lib/web/RNFBAuthModule.ios.ts diff --git a/packages/auth/lib/web/RNFBAuthModule.js b/packages/auth/lib/web/RNFBAuthModule.ts similarity index 79% rename from packages/auth/lib/web/RNFBAuthModule.js rename to packages/auth/lib/web/RNFBAuthModule.ts index f5b02d9c48..fd5609ca85 100644 --- a/packages/auth/lib/web/RNFBAuthModule.js +++ b/packages/auth/lib/web/RNFBAuthModule.ts @@ -1,8 +1,8 @@ import { Platform } from 'react-native'; +import * as firebaseAuthModule from '@react-native-firebase/app/dist/module/internal/web/firebaseAuth'; import { getApp, initializeAuth, - getReactNativePersistence, onAuthStateChanged, onIdTokenChanged, signInAnonymously, @@ -44,6 +44,22 @@ import { PhoneAuthProvider, OAuthProvider, } from '@react-native-firebase/app/dist/module/internal/web/firebaseAuth'; +import type { + ActionCodeSettings, + Auth, + AuthCredential, + MultiFactorInfo, + MultiFactorError, + MultiFactorSession, + MultiFactorResolver, + PhoneAuthCredential, + TotpSecret as WebTotpSecret, + Unsubscribe, + User, + UserCredential, + UserInfo, + UserMetadata, +} from '@react-native-firebase/app/dist/module/internal/web/firebaseAuth'; import { guard, getWebError, @@ -53,13 +69,71 @@ import { getReactNativeAsyncStorageInternal, isMemoryStorage, } from '@react-native-firebase/app/dist/module/internal/asyncStorage'; +type UserInfoObject = { + providerId: string; + uid: string; + displayName: string | null; + email: string | null; + photoURL: string | null; + phoneNumber: string | null; +}; + +type WebErrorLike = { + name?: string; + code?: string; + message: string; + details?: unknown; + customData?: unknown; + userInfo?: Record; +}; + +type MultiFactorInfoObject = { + displayName?: string | null; + enrollmentTime: string; + factorId: string; + uid: string; + phoneNumber?: string | null; +}; + +type UserMetadataObject = { + creationTime: string | null; + lastSignInTime: string | null; +}; + +type UserObject = UserInfoObject & { + emailVerified: boolean; + isAnonymous: boolean; + tenantId: string | null; + providerData: UserInfoObject[]; + metadata: UserMetadataObject; + multiFactor: { + enrolledFactors: MultiFactorInfoObject[]; + }; +}; + +type AuthResultObject = { + user: UserObject; + additionalUserInfo: { + isNewUser: boolean; + profile: Record | null; + providerId: string | null; + username?: string | null; + } | null; +}; + +const getReactNativePersistence = + ( + firebaseAuthModule as unknown as { + getReactNativePersistence?: (storage: unknown) => unknown; + } + ).getReactNativePersistence ?? ((storage: unknown) => storage); /** * Resolves or rejects an auth method promise without a user (user was missing). * @param {boolean} isError whether to reject the promise. * @returns {Promise} - Void promise. */ -function promiseNoUser(isError = false) { +function promiseNoUser(isError = false): Promise { if (isError) { return rejectPromiseWithCodeAndMessage('no-current-user', 'No user currently signed in.'); } @@ -73,13 +147,14 @@ function promiseNoUser(isError = false) { * @param {string} code - The error code. * @param {string} message - The error message. */ -function rejectPromiseWithCodeAndMessage(code, message) { - return rejectPromise(getWebError({ code: `auth/${code}`, message })); +function rejectPromiseWithCodeAndMessage(code: string, message: string): Promise { + return rejectPromise(getWebError({ name: 'FirebaseError', code: `auth/${code}`, message })); } -function rejectWithCodeAndMessage(code, message) { +function rejectWithCodeAndMessage(code: string, message: string): Promise { return Promise.reject( getWebError({ + name: 'FirebaseError', code, message, }), @@ -91,10 +166,10 @@ function rejectWithCodeAndMessage(code, message) { * @param {error} error The error object. * @returns {never} */ -function rejectPromise(error) { +function rejectPromise(error: WebErrorLike): Promise { const { code, message, details } = error; const nativeError = { - code, + code: code ?? 'auth/unknown', message, userInfo: { code: code ? code.replace('auth/', '') : 'unknown', @@ -110,7 +185,7 @@ function rejectPromise(error) { * @param {User} user - The User object to convert. * @returns {object} */ -function userToObject(user) { +function userToObject(user: User): UserObject { return { ...userInfoToObject(user), emailVerified: user.emailVerified, @@ -132,7 +207,12 @@ function userToObject(user) { * @param {string|null} secret - The secret to use for the credential. * @returns {AuthCredential|null} - The AuthCredential object. */ -function getAuthCredential(_auth, provider, token, secret) { +function getAuthCredential( + _auth: Auth, + provider: string, + token: string, + secret?: string | null, +): AuthCredential | null { if (provider.startsWith('oidc.')) { return new OAuthProvider(provider).credential({ idToken: token, @@ -141,29 +221,29 @@ function getAuthCredential(_auth, provider, token, secret) { switch (provider) { case 'facebook.com': - return FacebookAuthProvider().credential(token); + return FacebookAuthProvider.credential(token); case 'google.com': - return GoogleAuthProvider().credential(token, secret); + return GoogleAuthProvider.credential(token, secret ?? undefined); case 'twitter.com': - return TwitterAuthProvider().credential(token, secret); + return TwitterAuthProvider.credential(token, secret ?? ''); case 'github.com': - return GithubAuthProvider().credential(token); + return GithubAuthProvider.credential(token); case 'apple.com': return new OAuthProvider(provider).credential({ idToken: token, - rawNonce: secret, + rawNonce: secret ?? undefined, }); case 'oauth': - return OAuthProvider(provider).credential({ + return new OAuthProvider(provider).credential({ idToken: token, - accessToken: secret, + accessToken: secret ?? undefined, }); case 'phone': - return PhoneAuthProvider.credential(token, secret); + return PhoneAuthProvider.credential(token, secret ?? ''); case 'password': - return EmailAuthProvider.credential(token, secret); + return EmailAuthProvider.credential(token, secret ?? ''); case 'emailLink': - return EmailAuthProvider.credentialWithLink(token, secret); + return EmailAuthProvider.credentialWithLink(token, secret ?? ''); default: return null; } @@ -173,7 +253,7 @@ function getAuthCredential(_auth, provider, token, secret) { * Converts a user info object to a plain object. * @param {UserInfo} userInfo - The UserInfo object to convert. */ -function userInfoToObject(userInfo) { +function userInfoToObject(userInfo: UserInfo): UserInfoObject { return { providerId: userInfo.providerId, uid: userInfo.uid, @@ -190,7 +270,7 @@ function userInfoToObject(userInfo) { * Converts a user metadata object to a plain object. * @param {UserMetadata} metadata - The UserMetadata object to convert. */ -function userMetadataToObject(metadata) { +function userMetadataToObject(metadata: UserMetadata): UserMetadataObject { return { creationTime: metadata.creationTime ? new Date(metadata.creationTime).toISOString() : null, lastSignInTime: metadata.lastSignInTime @@ -203,8 +283,8 @@ function userMetadataToObject(metadata) { * Converts a MultiFactorInfo object to a plain object. * @param {MultiFactorInfo} multiFactorInfo - The MultiFactorInfo object to convert. */ -function multiFactorInfoToObject(multiFactorInfo) { - const obj = { +function multiFactorInfoToObject(multiFactorInfo: MultiFactorInfo): MultiFactorInfoObject { + const obj: MultiFactorInfoObject = { displayName: multiFactorInfo.displayName, enrollmentTime: multiFactorInfo.enrollmentTime, factorId: multiFactorInfo.factorId, @@ -213,7 +293,9 @@ function multiFactorInfoToObject(multiFactorInfo) { // If https://firebase.google.com/docs/reference/js/auth.phonemultifactorinfo if ('phoneNumber' in multiFactorInfo) { - obj.phoneNumber = multiFactorInfo.phoneNumber; + obj.phoneNumber = ( + multiFactorInfo as MultiFactorInfo & { phoneNumber?: string | null } + ).phoneNumber; } return obj; @@ -223,28 +305,30 @@ function multiFactorInfoToObject(multiFactorInfo) { * Converts a user credential object to a plain object. * @param {UserCredential} userCredential - The user credential object to convert. */ -function authResultToObject(userCredential) { +function authResultToObject(userCredential: UserCredential): AuthResultObject { const additional = getAdditionalUserInfo(userCredential); return { user: userToObject(userCredential.user), - additionalUserInfo: { - isNewUser: additional.isNewUser, - profile: additional.profile, - providerId: additional.providerId, - username: additional.username, - }, + additionalUserInfo: additional + ? { + isNewUser: additional.isNewUser, + profile: (additional.profile as Record | null) ?? null, + providerId: additional.providerId, + username: additional.username, + } + : null, }; } -const instances = {}; -const authStateListeners = {}; -const idTokenListeners = {}; -const sessionMap = new Map(); -const totpSecretMap = new Map(); +const instances: Record = {}; +const authStateListeners: Record = {}; +const idTokenListeners: Record = {}; +const sessionMap = new Map(); +const totpSecretMap = new Map(); let sessionId = 0; // Returns a cached Firestore instance. -function getCachedAuthInstance(appName) { +function getCachedAuthInstance(appName: string): Auth { if (!instances[appName]) { if (isMemoryStorage()) { // Warn auth persistence is is disabled unless Async Storage implementation is provided. @@ -265,7 +349,7 @@ function getCachedAuthInstance(appName) { ); } - const authOptions = {}; + const authOptions: { persistence?: unknown } = {}; if (Platform.OS !== 'web') { // Non-web platforms pull the react-native export from package.json and // get a bundle that defines `getReactNativePersistence` etc, but web platforms @@ -273,13 +357,16 @@ function getCachedAuthInstance(appName) { authOptions.persistence = getReactNativePersistence(getReactNativeAsyncStorageInternal()); } - instances[appName] = initializeAuth(getApp(appName), authOptions); + instances[appName] = initializeAuth(getApp(appName), authOptions as never); } return instances[appName]; } // getConstants -const CONSTANTS = { +const CONSTANTS: { + APP_LANGUAGE: Record; + APP_USER: Record; +} = { APP_LANGUAGE: {}, APP_USER: {}, }; @@ -328,7 +415,7 @@ export default { * @param {string} appName - The name of the app to get the auth instance for. * @returns {Promise} - Void promise. */ - addAuthStateListener(appName) { + addAuthStateListener(appName: string) { if (authStateListeners[appName]) { return; } @@ -336,7 +423,7 @@ export default { return guard(async () => { const auth = getCachedAuthInstance(appName); - authStateListeners[appName] = onAuthStateChanged(auth, user => { + authStateListeners[appName] = onAuthStateChanged(auth, (user: User | null) => { emitEvent('auth_state_changed', { appName, user: user ? userToObject(user) : null, @@ -350,7 +437,7 @@ export default { * @param {string} appName - The name of the app to get the auth instance for. * @returns {Promise} - Void promise. */ - removeAuthStateListener(appName) { + removeAuthStateListener(appName: string) { if (authStateListeners[appName]) { authStateListeners[appName](); delete authStateListeners[appName]; @@ -362,7 +449,7 @@ export default { * @param {string} appName - The name of the app to get the auth instance for. * @returns {Promise} - Void promise. */ - addIdTokenListener(appName) { + addIdTokenListener(appName: string) { if (idTokenListeners[appName]) { return; } @@ -370,7 +457,7 @@ export default { return guard(async () => { const auth = getCachedAuthInstance(appName); - idTokenListeners[appName] = onIdTokenChanged(auth, user => { + idTokenListeners[appName] = onIdTokenChanged(auth, (user: User | null) => { emitEvent('auth_id_token_changed', { authenticated: !!user, appName, @@ -385,28 +472,28 @@ export default { * @param {string} appName - The name of the app to get the auth instance for. * @returns {Promise} - Void promise. */ - removeIdTokenListener(appName) { + removeIdTokenListener(appName: string) { if (idTokenListeners[appName]) { idTokenListeners[appName](); delete idTokenListeners[appName]; } }, - async forceRecaptchaFlowForTesting() { + async forceRecaptchaFlowForTesting(): Promise { return rejectPromiseWithCodeAndMessage( 'unsupported', 'This operation is not supported in this environment.', ); }, - async setAutoRetrievedSmsCodeForPhoneNumber() { + async setAutoRetrievedSmsCodeForPhoneNumber(): Promise { return rejectPromiseWithCodeAndMessage( 'unsupported', 'This operation is not supported in this environment.', ); }, - async setAppVerificationDisabledForTesting() { + async setAppVerificationDisabledForTesting(): Promise { return rejectPromiseWithCodeAndMessage( 'unsupported', 'This operation is not supported in this environment.', @@ -418,7 +505,7 @@ export default { * @param {string} appName - The name of the app to get the auth instance for. * @returns {Promise} - Void promise. */ - signOut(appName) { + signOut(appName: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); @@ -436,7 +523,7 @@ export default { * @param {*} appName - The name of the app to get the auth instance for. * @returns */ - signInAnonymously(appName) { + signInAnonymously(appName: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); const credential = await signInAnonymously(auth); @@ -451,7 +538,7 @@ export default { * @param {string} password - The password to sign in with. * @returns {Promise} - The result of the sign in. */ - async createUserWithEmailAndPassword(appName, email, password) { + async createUserWithEmailAndPassword(appName: string, email: string, password: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); const credential = await createUserWithEmailAndPassword(auth, email, password); @@ -466,7 +553,7 @@ export default { * @param {string} password - The password to sign in with. * @returns {Promise} - The result of the sign in. */ - async signInWithEmailAndPassword(appName, email, password) { + async signInWithEmailAndPassword(appName: string, email: string, password: string) { // The default guard / getWebError process doesn't work well here, // since it creates a new error object that is then passed through // a native module proxy and gets processed again. @@ -480,13 +567,14 @@ export default { password, ); return authResultToObject(credential); - } catch (e) { - e.userInfo = { - code: e.code.split('/')[1], - message: e.message, - customData: e.customData, + } catch (e: unknown) { + const error = e as WebErrorLike; + error.userInfo = { + code: error.code?.split('/')[1] ?? 'unknown', + message: error.message, + customData: error.customData, }; - throw e; + throw error; } // }); }, @@ -497,7 +585,7 @@ export default { * @param {string} emailLink - The email link to sign in with. * @returns {Promise} - Whether the link is a valid sign in with email link. */ - async isSignInWithEmailLink(appName, emailLink) { + async isSignInWithEmailLink(appName: string, emailLink: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); return await isSignInWithEmailLink(auth, emailLink); @@ -511,7 +599,7 @@ export default { * @param {string} emailLink - The email link to sign in with. * @returns {Promise} - The result of the sign in. */ - async signInWithEmailLink(appName, email, emailLink) { + async signInWithEmailLink(appName: string, email: string, emailLink: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); const credential = await signInWithEmailLink(auth, email, emailLink); @@ -525,7 +613,7 @@ export default { * @param {string} token - The token to sign in with. * @returns {Promise} - The result of the sign in. */ - async signInWithCustomToken(appName, token) { + async signInWithCustomToken(appName: string, token: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); const credential = await signInWithCustomToken(auth, token); @@ -547,7 +635,7 @@ export default { * @param {ActionCodeSettings} settings - The settings to use for the password reset email. * @returns {Promise} */ - async sendPasswordResetEmail(appName, email, settings) { + async sendPasswordResetEmail(appName: string, email: string, settings?: ActionCodeSettings) { return guard(async () => { const auth = getCachedAuthInstance(appName); await sendPasswordResetEmail(auth, email, settings); @@ -562,7 +650,7 @@ export default { * @param {ActionCodeSettings} settings - The settings to use for the password reset email. * @returns {Promise} */ - async sendSignInLinkToEmail(appName, email, settings) { + async sendSignInLinkToEmail(appName: string, email: string, settings: ActionCodeSettings) { return guard(async () => { const auth = getCachedAuthInstance(appName); await sendSignInLinkToEmail(auth, email, settings); @@ -579,7 +667,7 @@ export default { * @param {string} appName - The name of the app to get the auth instance for. * @returns {Promise} */ - async delete(appName) { + async delete(appName: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); @@ -597,7 +685,7 @@ export default { * @param {string} appName - The name of the app to get the auth instance for. * @returns {Promise} - The current user object. */ - async reload(appName) { + async reload(appName: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); @@ -616,7 +704,7 @@ export default { * @param {ActionCodeSettings} actionCodeSettings - The settings to use for the email verification. * @returns {Promise} - The current user object. */ - async sendEmailVerification(appName, actionCodeSettings) { + async sendEmailVerification(appName: string, actionCodeSettings?: ActionCodeSettings | null) { return guard(async () => { const auth = getCachedAuthInstance(appName); @@ -636,7 +724,11 @@ export default { * @param {ActionCodeSettings} actionCodeSettings - The settings to use for the email verification. * @returns {Promise} - The current user object. */ - async verifyBeforeUpdateEmail(appName, email, actionCodeSettings) { + async verifyBeforeUpdateEmail( + appName: string, + email: string, + actionCodeSettings?: ActionCodeSettings | null, + ) { return guard(async () => { const auth = getCachedAuthInstance(appName); @@ -655,7 +747,7 @@ export default { * @param {string} email - The email to update. * @returns {Promise} - The current user object. */ - async updateEmail(appName, email) { + async updateEmail(appName: string, email: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); @@ -674,7 +766,7 @@ export default { * @param {string} password - The password to update. * @returns {Promise} - The current user object. */ - async updatePassword(appName, password) { + async updatePassword(appName: string, password: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); @@ -695,7 +787,12 @@ export default { * @param {string} authSecret - The auth secret to update the phone number with. * @returns {Promise} - The current user object. */ - async updatePhoneNumber(appName, provider, authToken, authSecret) { + async updatePhoneNumber( + appName: string, + provider: string, + authToken: string, + authSecret?: string | null, + ) { return guard(async () => { const auth = getCachedAuthInstance(appName); @@ -719,7 +816,7 @@ export default { ); } - await updatePhoneNumber(auth.currentUser, credential); + await updatePhoneNumber(auth.currentUser, credential as PhoneAuthCredential); return userToObject(auth.currentUser); }); @@ -731,7 +828,10 @@ export default { * @param {object} props - The properties to update. * @returns {Promise} - The current user object. */ - async updateProfile(appName, props) { + async updateProfile( + appName: string, + props: { displayName?: string | null; photoURL?: string | null }, + ) { return guard(async () => { const auth = getCachedAuthInstance(appName); @@ -756,7 +856,12 @@ export default { * @param {string} authSecret - The auth secret to sign in with. * @returns {Promise} - The result of the sign in. */ - async signInWithCredential(appName, provider, authToken, authSecret) { + async signInWithCredential( + appName: string, + provider: string, + authToken: string, + authSecret?: string | null, + ) { return guard(async () => { const auth = getCachedAuthInstance(appName); const credential = getAuthCredential(auth, provider, authToken, authSecret); @@ -792,7 +897,7 @@ export default { * @param {string} appName - The name of the app to get the auth instance for. * @returns {Promise} - The session ID. */ - async getSession(appName) { + async getSession(appName: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); @@ -853,7 +958,7 @@ export default { * @param {string} newPassword - The new password to set. * @returns {Promise} */ - async confirmPasswordReset(appName, code, newPassword) { + async confirmPasswordReset(appName: string, code: string, newPassword: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); await confirmPasswordReset(auth, code, newPassword); @@ -867,7 +972,7 @@ export default { * @param {string} code - The code to apply. * @returns {Promise} - Void promise. */ - async applyActionCode(appName, code) { + async applyActionCode(appName: string, code: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); await applyActionCode(auth, code); @@ -880,7 +985,7 @@ export default { * @param {string} code - The code to check. * @returns {Promise} - The result of the check. */ - async checkActionCode(appName, code) { + async checkActionCode(appName: string, code: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); const result = await checkActionCode(auth, code); @@ -904,7 +1009,12 @@ export default { * @param {string} authSecret - The auth secret to link. * @returns {Promise} - The current user object. */ - async linkWithCredential(appName, provider, authToken, authSecret) { + async linkWithCredential( + appName: string, + provider: string, + authToken: string, + authSecret?: string | null, + ) { return guard(async () => { const auth = getCachedAuthInstance(appName); const credential = getAuthCredential(auth, provider, authToken, authSecret); @@ -938,7 +1048,7 @@ export default { * @param {string} providerId - The provider ID to unlink. * @returns {Promise} - The current user object. */ - async unlink(appName, providerId) { + async unlink(appName: string, providerId: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); @@ -959,7 +1069,12 @@ export default { * @param {string} authSecret - The auth secret to reauthenticate with. * @returns {Promise} - The current user object. */ - async reauthenticateWithCredential(appName, provider, authToken, authSecret) { + async reauthenticateWithCredential( + appName: string, + provider: string, + authToken: string, + authSecret?: string | null, + ) { return guard(async () => { const auth = getCachedAuthInstance(appName); const credential = getAuthCredential(auth, provider, authToken, authSecret); @@ -993,7 +1108,7 @@ export default { * @param {boolean} forceRefresh - Whether to force a token refresh. * @returns {Promise} - The ID token. */ - async getIdToken(appName, forceRefresh) { + async getIdToken(appName: string, forceRefresh: boolean) { return guard(async () => { const auth = getCachedAuthInstance(appName); @@ -1012,7 +1127,7 @@ export default { * @param {boolean} forceRefresh - Whether to force a token refresh. * @returns {Promise} - The ID token result. */ - async getIdTokenResult(appName, forceRefresh) { + async getIdTokenResult(appName: string, forceRefresh: boolean) { return guard(async () => { const auth = getCachedAuthInstance(appName); @@ -1041,7 +1156,7 @@ export default { * @param {*} code the code from the user TOTP app * @return TotpMultiFactorAssertion to use for resolving */ - assertionForSignIn(_appName, uid, code) { + assertionForSignIn(_appName: string, uid: string, code: string) { return TotpMultiFactorGenerator.assertionForSignIn(uid, code); }, @@ -1051,7 +1166,7 @@ export default { * @param {*} error the MFA error returned from initial factor login attempt * @return MultiFactorResolver to use for verifying the second factor */ - getMultiFactorResolver(appName, error) { + getMultiFactorResolver(appName: string, error: MultiFactorError): MultiFactorResolver | null { return getMultiFactorResolver(getCachedAuthInstance(appName), error); }, @@ -1061,9 +1176,16 @@ export default { * @param {*} session - The MultiFactorSession to associate with the secret * @returns object with secretKey to associate with TotpSecret */ - async generateTotpSecret(_appName, session) { + async generateTotpSecret(_appName: string, session: string) { return guard(async () => { - const totpSecret = await TotpMultiFactorGenerator.generateSecret(sessionMap.get(session)); + const mappedSession = sessionMap.get(session); + if (!mappedSession) { + return rejectPromiseWithCodeAndMessage( + 'invalid-multi-factor-session', + 'The supplied multi-factor session is invalid or has expired.', + ); + } + const totpSecret = await TotpMultiFactorGenerator.generateSecret(mappedSession); totpSecretMap.set(totpSecret.secretKey, totpSecret); return { secretKey: totpSecret.secretKey }; }); @@ -1075,13 +1197,14 @@ export default { * @param {*} enrollmentId - The ID to associate with the enrollment * @returns */ - async unenrollMultiFactor(appName, enrollmentId) { + async unenrollMultiFactor(appName: string, enrollmentId: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); if (auth.currentUser === null) { return promiseNoUser(true); } await multiFactor(auth.currentUser).unenroll(enrollmentId); + return promiseNoUser(); }); }, @@ -1093,17 +1216,30 @@ export default { * @param {*} displayName - The name to associate as a hint * @returns */ - async finalizeTotpEnrollment(appName, secretKey, verificationCode, displayName) { + async finalizeTotpEnrollment( + appName: string, + secretKey: string, + verificationCode: string, + displayName?: string | null, + ) { return guard(async () => { const auth = getCachedAuthInstance(appName); if (auth.currentUser === null) { return promiseNoUser(true); } + const totpSecret = totpSecretMap.get(secretKey); + if (!totpSecret) { + return rejectPromiseWithCodeAndMessage( + 'invalid-multi-factor-secret', + "can't find secret for provided key", + ); + } const multiFactorAssertion = TotpMultiFactorGenerator.assertionForEnrollment( - totpSecretMap.get(secretKey), + totpSecret, verificationCode, ); await multiFactor(auth.currentUser).enroll(multiFactorAssertion, displayName); + return promiseNoUser(); }); }, @@ -1115,8 +1251,15 @@ export default { * @param {*} issuer - The issuer to use in auth app * @returns QR Code URL */ - generateQrCodeUrl(_appName, secretKey, accountName, issuer) { - return totpSecretMap.get(secretKey).generateQrCodeUrl(accountName, issuer); + generateQrCodeUrl(_appName: string, secretKey: string, accountName?: string, issuer?: string) { + const totpSecret = totpSecretMap.get(secretKey); + if (!totpSecret) { + return rejectPromiseWithCodeAndMessage( + 'invalid-multi-factor-secret', + "can't find secret for provided key", + ); + } + return totpSecret.generateQrCodeUrl(accountName, issuer); }, /** @@ -1142,7 +1285,7 @@ export default { * @param {string} email - The email to fetch the sign in methods for. * @returns {Promise} - The sign in methods for the email. */ - async fetchSignInMethodsForEmail(appName, email) { + async fetchSignInMethodsForEmail(appName: string, email: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); const methods = await fetchSignInMethodsForEmail(auth, email); @@ -1156,7 +1299,7 @@ export default { * @param {string} code - The language code to set. * @returns {void} */ - setLanguageCode(appName, code) { + setLanguageCode(appName: string, code: string | null) { return guard(async () => { const auth = getCachedAuthInstance(appName); auth.languageCode = code; @@ -1169,7 +1312,7 @@ export default { * @param {string} tenantId - The tenant ID to set. * @returns {void} */ - setTenantId(appName, tenantId) { + setTenantId(appName: string, tenantId: string | null) { return guard(async () => { const auth = getCachedAuthInstance(appName); auth.tenantId = tenantId; @@ -1181,7 +1324,7 @@ export default { * @param {string} appName - The name of the app to get the auth instance for. * @returns void */ - useDeviceLanguage(appName) { + useDeviceLanguage(appName: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); useDeviceLanguage(auth); @@ -1192,7 +1335,7 @@ export default { * Verify the provided password reset code. * @returns {string} - The users email address if valid. */ - verifyPasswordResetCode(appName, code) { + verifyPasswordResetCode(appName: string, code: string) { return guard(async () => { const auth = getCachedAuthInstance(appName); const email = await verifyPasswordResetCode(auth, code); @@ -1207,7 +1350,7 @@ export default { * @param {number} port - The port to use for the auth emulator. * @returns {void} */ - useEmulator(appName, host, port) { + useEmulator(appName: string, host: string, port: number) { return guard(async () => { const auth = getCachedAuthInstance(appName); connectAuthEmulator(auth, `http://${host}:${port}`); diff --git a/packages/auth/package.json b/packages/auth/package.json index bbbdd7883d..8cd03a7c8a 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -3,14 +3,15 @@ "version": "24.0.0", "author": "Invertase (http://invertase.io)", "description": "React Native Firebase - The authentication module provides an easy-to-use API to integrate an authentication workflow into new and existing applications. React Native Firebase provides access to all Firebase authentication methods and identity providers.", - "main": "lib/index.js", - "types": "lib/index.d.ts", + "main": "./dist/module/index.js", + "types": "./dist/typescript/lib/index.d.ts", "scripts": { - "build": "genversion --semi lib/version.js", + "build": "genversion --esm --semi lib/version.ts", "build:clean": "rimraf android/build && rimraf ios/build", "build:plugin": "rimraf plugin/build && tsc --build plugin", + "compile": "bob build", "lint:plugin": "eslint plugin/src/*", - "prepare": "yarn run build && yarn run build:plugin" + "prepare": "yarn run build && yarn compile && yarn run build:plugin" }, "repository": { "type": "git", @@ -32,7 +33,8 @@ }, "devDependencies": { "@types/plist": "^3.0.5", - "expo": "^55.0.5" + "expo": "^55.0.5", + "react-native-builder-bob": "^0.40.13" }, "peerDependenciesMeta": { "expo": { @@ -42,5 +44,35 @@ "publishConfig": { "access": "public", "provenance": true - } + }, + "exports": { + ".": { + "source": "./lib/index.ts", + "types": "./dist/typescript/lib/index.d.ts", + "default": "./dist/module/index.js" + }, + "./package.json": "./package.json" + }, + "react-native-builder-bob": { + "source": "lib", + "output": "dist", + "targets": [ + [ + "module", + { + "esm": true + } + ], + [ + "typescript", + { + "tsc": "../../node_modules/.bin/tsc" + } + ] + ] + }, + "eslintIgnore": [ + "node_modules/", + "dist/" + ] } diff --git a/packages/auth/tsconfig.json b/packages/auth/tsconfig.json new file mode 100644 index 0000000000..0e79144fc2 --- /dev/null +++ b/packages/auth/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "../../tsconfig.packages.base.json", + "compilerOptions": { + "baseUrl": ".", + "rootDir": ".", + "paths": { + "@react-native-firebase/app/dist/module/common/*": ["../app/dist/typescript/lib/common/*"], + "@react-native-firebase/app/dist/module/common": ["../app/dist/typescript/lib/common"], + "@react-native-firebase/app/dist/module/internal/NativeModules": [ + "../app/dist/typescript/lib/internal/NativeModules" + ], + "@react-native-firebase/app/dist/module/internal/*": [ + "../app/dist/typescript/lib/internal/*" + ], + "@react-native-firebase/app/dist/module/internal": ["../app/dist/typescript/lib/internal"], + "@react-native-firebase/app": ["../app/dist/typescript/lib"], + "@react-native-firebase/app/dist/module/types/common": [ + "../app/dist/typescript/lib/types/common" + ], + "@react-native-firebase/app/dist/module/types/internal": [ + "../app/dist/typescript/lib/types/internal" + ] + } + }, + "include": ["lib/**/*"], + "exclude": ["node_modules", "dist", "__tests__", "**/*.test.ts"] +} diff --git a/packages/auth/type-test.ts b/packages/auth/type-test.ts index ab298449fa..1cad47cb8a 100644 --- a/packages/auth/type-test.ts +++ b/packages/auth/type-test.ts @@ -1,311 +1,270 @@ +/* + * Consumer-facing API type tests for @react-native-firebase/auth. + * Part 1: namespaced API (firebase.auth(), default auth()). + * Part 2: modular API (getAuth, signInWithEmailAndPassword, etc. from lib/modular.ts). + */ + import auth, { firebase, - FirebaseAuthTypes, + applyActionCode, + ActionCodeOperation, + beforeAuthStateChanged, + checkActionCode, + confirmPasswordReset, + connectAuthEmulator, + createUserWithEmailAndPassword, + EmailAuthProvider, + fetchSignInMethodsForEmail, + FactorId, + getAdditionalUserInfo, getAuth, + getCustomAuthDomain, + getMultiFactorResolver, + getRedirectResult, initializeAuth, + isSignInWithEmailLink, + multiFactor, onAuthStateChanged, onIdTokenChanged, + OperationType, + parseActionCodeURL, + PhoneAuthProvider, + sendPasswordResetEmail, + sendSignInLinkToEmail, + setLanguageCode, signInAnonymously, - signInWithEmailAndPassword, signInWithCredential, signInWithCustomToken, + signInWithEmailAndPassword, signInWithEmailLink, signInWithPhoneNumber, + signInWithPopup, + signInWithRedirect, signOut, - createUserWithEmailAndPassword, - sendPasswordResetEmail, - confirmPasswordReset, - verifyPasswordResetCode, - sendSignInLinkToEmail, - isSignInWithEmailLink, - fetchSignInMethodsForEmail, + SignInMethod, + updateCurrentUser, useDeviceLanguage, - setLanguageCode, - connectAuthEmulator, - getMultiFactorResolver, - multiFactor, - EmailAuthProvider, + useUserAccessGroup, + validatePassword, + verifyPasswordResetCode, + type ActionCodeInfo, + type ActionCodeSettings, + type ApplicationVerifier, + type Auth, + type AuthProvider, + type AuthSettings, + type Config, + type ConfirmationResult, + type Dependencies, + type IdTokenResult, + type MultiFactorError, + type PasswordPolicy, + type PasswordValidationStatus, + type Persistence, + type PopupRedirectResolver, + type Unsubscribe, + type User, + type UserCredential, } from '.'; -console.log(auth().app); - -// checks module exists at root -console.log(firebase.auth().app.name); -console.log(firebase.auth().currentUser); +const authModule = auth(); +const namespacedAuth = firebase.auth(); +const authFromApp = firebase.app().auth(); +const authFromAppArg = auth(firebase.app()); -// checks module exists at app level -console.log(firebase.app().auth().app.name); -console.log(firebase.app().auth().currentUser); - -// checks statics exist +console.log(authModule.app.name); +console.log(namespacedAuth.currentUser); +console.log(authFromApp.app.name); +console.log(authFromAppArg.app.name); console.log(firebase.auth.SDK_VERSION); - -// checks statics exist on defaultExport console.log(auth.firebase.SDK_VERSION); - -// checks root exists console.log(firebase.SDK_VERSION); -// checks multi-app support exists -console.log(firebase.auth(firebase.app()).app.name); - -// checks default export supports app arg -console.log(auth(firebase.app()).app.name); - -// checks statics - providers -console.log(firebase.auth.EmailAuthProvider.PROVIDER_ID); -console.log(firebase.auth.PhoneAuthProvider.PROVIDER_ID); -console.log(firebase.auth.GoogleAuthProvider.PROVIDER_ID); -console.log(firebase.auth.GithubAuthProvider.PROVIDER_ID); -console.log(firebase.auth.TwitterAuthProvider.PROVIDER_ID); -console.log(firebase.auth.FacebookAuthProvider.PROVIDER_ID); -console.log(firebase.auth.AppleAuthProvider.PROVIDER_ID); -console.log(firebase.auth.OAuthProvider); -console.log(firebase.auth.OIDCAuthProvider); -console.log(firebase.auth.PhoneAuthState.CODE_SENT); -console.log(firebase.auth.PhoneMultiFactorGenerator); -console.log(firebase.auth.getMultiFactorResolver); -console.log(firebase.auth.multiFactor); - -// checks Module instance APIs -const authInstance = firebase.auth(); -console.log(authInstance.currentUser); -console.log(authInstance.tenantId); -console.log(authInstance.languageCode); -console.log(authInstance.settings); - -authInstance.setTenantId('tenant-123').then(() => { - console.log('Tenant set'); -}); - -authInstance.setLanguageCode('fr').then(() => { - console.log('Language set'); -}); - -authInstance.useEmulator('http://localhost:9099'); - -const unsubscribe1 = authInstance.onAuthStateChanged((user: FirebaseAuthTypes.User | null) => { - if (user) { - console.log(user.email); - console.log(user.displayName); - console.log(user.uid); - } -}); - -const unsubscribe2 = authInstance.onIdTokenChanged((user: FirebaseAuthTypes.User | null) => { - if (user) { - console.log(user.email); - } -}); +const actionCodeSettings: ActionCodeSettings = { + url: 'https://example.com/auth', + handleCodeInApp: true, + android: { packageName: 'io.invertase.demo', installApp: true }, + iOS: { bundleId: 'io.invertase.demo' }, +}; + +const appVerifier: ApplicationVerifier = { + type: 'recaptcha', + verify: async () => 'token', +}; + +const popupRedirectResolver: PopupRedirectResolver = {}; +const redirectProvider = { providerId: 'oidc.test' } as AuthProvider; +const authSettings: AuthSettings = { appVerificationDisabledForTesting: true }; +const persistence: Persistence = { type: 'NONE' }; +const authConfig: Config = { + apiKey: 'api-key', + apiHost: 'identitytoolkit.googleapis.com', + apiScheme: 'https', + tokenApiHost: 'securetoken.googleapis.com', + sdkClientVersion: 'web/0.0.0', +}; +const dependencies: Dependencies = { + persistence, + popupRedirectResolver, +}; +const passwordPolicy: PasswordPolicy = { + customStrengthOptions: { minPasswordLength: 6 }, + allowedNonAlphanumericCharacters: '!@#', + enforcementState: 'OFF', + forceUpgradeOnSignin: false, +}; +const passwordValidationStatus: PasswordValidationStatus = { + isValid: true, + passwordPolicy, +}; + +console.log(authSettings.appVerificationDisabledForTesting); +console.log(authConfig.apiHost); +console.log(dependencies.persistence); +console.log(passwordValidationStatus.passwordPolicy.enforcementState); +console.log( + ActionCodeOperation.VERIFY_EMAIL, + FactorId.PHONE, + OperationType.SIGN_IN, + SignInMethod.EMAIL_LINK, +); -unsubscribe1(); -unsubscribe2(); +const modularAuth: Auth = getAuth(); +const modularAuthFromApp: Auth = getAuth(firebase.app()); +const initializedAuth: Auth = initializeAuth(firebase.app(), dependencies); +console.log(modularAuth.app.name, modularAuthFromApp.app.name, initializedAuth.app.name); + +namespacedAuth.setTenantId('tenant-123'); +namespacedAuth.setLanguageCode('en'); +namespacedAuth.useEmulator('http://localhost:9099'); +namespacedAuth.sendPasswordResetEmail('test@example.com'); +namespacedAuth.sendSignInLinkToEmail('test@example.com', actionCodeSettings); +namespacedAuth.verifyPasswordResetCode('oob-code').then((email: string) => console.log(email)); +namespacedAuth + .checkActionCode('oob-code') + .then((info: ActionCodeInfo) => console.log(info.operation)); +namespacedAuth.getCustomAuthDomain().then((domain: string) => console.log(domain)); + +const namespacedUnsubscribe: Unsubscribe = namespacedAuth.onAuthStateChanged( + (user: User | null) => { + console.log(user?.uid); + }, +); +namespacedUnsubscribe(); -authInstance.signInAnonymously().then((credential: FirebaseAuthTypes.UserCredential) => { - console.log(credential.user.uid); +namespacedAuth.onIdTokenChanged((user: User | null) => { + console.log(user?.email); }); -authInstance +namespacedAuth + .signInAnonymously() + .then((credential: UserCredential) => console.log(credential.user.uid)); +namespacedAuth + .createUserWithEmailAndPassword('new@example.com', 'password123') + .then((credential: UserCredential) => console.log(credential.user.email)); +namespacedAuth .signInWithEmailAndPassword('test@example.com', 'password123') - .then((credential: FirebaseAuthTypes.UserCredential) => { - console.log(credential.user.email); - }); - -authInstance + .then((credential: UserCredential) => console.log(credential.user.email)); +namespacedAuth .signInWithCustomToken('custom-token') - .then((credential: FirebaseAuthTypes.UserCredential) => { - console.log(credential.user.uid); - }); - -authInstance + .then((credential: UserCredential) => console.log(credential.user.uid)); +namespacedAuth .signInWithEmailLink('test@example.com', 'email-link') - .then((credential: FirebaseAuthTypes.UserCredential) => { - console.log(credential.user.email); - }); - -authInstance + .then((credential: UserCredential) => console.log(credential.user.email)); +namespacedAuth .signInWithPhoneNumber('+1234567890') - .then((result: FirebaseAuthTypes.ConfirmationResult) => { - console.log(result.verificationId); - }); - -authInstance.signOut().then(() => { - console.log('Signed out'); -}); + .then((result: ConfirmationResult) => console.log(result.verificationId)); +namespacedAuth.signOut(); -authInstance - .createUserWithEmailAndPassword('new@example.com', 'password123') - .then((credential: FirebaseAuthTypes.UserCredential) => { - console.log(credential.user.email); - }); +const emailCredential = EmailAuthProvider.credential('test@example.com', 'password123'); +const phoneCredential = PhoneAuthProvider.credential('verification-id', '123456'); +console.log(emailCredential.providerId, phoneCredential.providerId); -authInstance.sendPasswordResetEmail('test@example.com').then(() => { - console.log('Password reset email sent'); -}); - -authInstance.confirmPasswordReset('code', 'newPassword').then(() => { - console.log('Password reset confirmed'); -}); - -authInstance.verifyPasswordResetCode('code').then((email: string) => { - console.log(email); -}); - -authInstance.sendSignInLinkToEmail('test@example.com').then(() => { - console.log('Sign in link sent'); -}); - -authInstance.isSignInWithEmailLink('email-link').then((isLink: boolean) => { - console.log(isLink); -}); - -authInstance.fetchSignInMethodsForEmail('test@example.com').then((methods: string[]) => { - console.log(methods); -}); - -const resolver = authInstance.getMultiFactorResolver({} as FirebaseAuthTypes.MultiFactorError); -console.log(resolver); - -authInstance.getCustomAuthDomain().then((domain: string) => { - console.log(domain); -}); - -// checks modular API functions -const authModular1 = getAuth(); -console.log(authModular1.app.name); - -const authModular2 = getAuth(firebase.app()); -console.log(authModular2.app.name); - -const authModular3 = initializeAuth(firebase.app()); -console.log(authModular3.app.name); - -onAuthStateChanged(authInstance, (user: FirebaseAuthTypes.User | null) => { - console.log(user?.email); -}); - -onIdTokenChanged(authInstance, (user: FirebaseAuthTypes.User | null) => { - console.log(user?.email); -}); - -signInAnonymously(authInstance).then((credential: FirebaseAuthTypes.UserCredential) => { - console.log(credential.user.uid); -}); - -signInWithEmailAndPassword(authInstance, 'test@example.com', 'password123').then( - (credential: FirebaseAuthTypes.UserCredential) => { - console.log(credential.user.email); - }, +applyActionCode(modularAuth, 'oob-code'); +checkActionCode(modularAuth, 'oob-code').then((info: ActionCodeInfo) => + console.log(info.data.email), ); - -const emailCredential = EmailAuthProvider.credential('test@example.com', 'password'); -signInWithCredential(authInstance, emailCredential).then( - (credential: FirebaseAuthTypes.UserCredential) => { - console.log(credential.user.email); - }, +confirmPasswordReset(modularAuth, 'oob-code', 'new-password'); +connectAuthEmulator(modularAuth, 'http://localhost:9099', { disableWarnings: false }); +createUserWithEmailAndPassword(modularAuth, 'new@example.com', 'password123').then( + (credential: UserCredential) => console.log(credential.user.uid), ); - -signInWithCustomToken(authInstance, 'custom-token').then( - (credential: FirebaseAuthTypes.UserCredential) => { - console.log(credential.user.uid); - }, +fetchSignInMethodsForEmail(modularAuth, 'test@example.com').then((methods: string[]) => + console.log(methods), ); +getMultiFactorResolver(modularAuth, {} as MultiFactorError); +getRedirectResult(modularAuth, popupRedirectResolver).then(result => console.log(result?.user.uid)); +isSignInWithEmailLink(modularAuth, 'email-link').then((valid: boolean) => console.log(valid)); -signInWithEmailLink(authInstance, 'test@example.com', 'email-link').then( - (credential: FirebaseAuthTypes.UserCredential) => { - console.log(credential.user.email); - }, +const modularUnsubscribe: Unsubscribe = onAuthStateChanged(modularAuth, (user: User | null) => + console.log(user?.email), ); +modularUnsubscribe(); -signInWithPhoneNumber(authInstance, '+1234567890').then( - (result: FirebaseAuthTypes.ConfirmationResult) => { - console.log(result.verificationId); - }, +onIdTokenChanged(modularAuth, (user: User | null) => console.log(user?.uid)); +signInAnonymously(modularAuth).then((credential: UserCredential) => + console.log(credential.user.uid), ); - -signOut(authInstance).then(() => { - console.log('Signed out'); -}); - -createUserWithEmailAndPassword(authInstance, 'new@example.com', 'password123').then( - (credential: FirebaseAuthTypes.UserCredential) => { - console.log(credential.user.email); - }, +signInWithCredential(modularAuth, emailCredential).then((credential: UserCredential) => + console.log(credential.user.email), +); +signInWithCustomToken(modularAuth, 'custom-token').then((credential: UserCredential) => + console.log(credential.user.uid), +); +signInWithEmailAndPassword(modularAuth, 'test@example.com', 'password123').then( + (credential: UserCredential) => console.log(credential.user.email), +); +signInWithEmailLink(modularAuth, 'test@example.com', 'email-link').then( + (credential: UserCredential) => console.log(credential.user.email), +); +signInWithPhoneNumber(modularAuth, '+1234567890', appVerifier).then((result: ConfirmationResult) => + console.log(result.verificationId), +); +signInWithPopup(modularAuth, redirectProvider, popupRedirectResolver).then(result => + console.log(result.user.uid), +); +signInWithRedirect(modularAuth, redirectProvider, popupRedirectResolver).then(result => + console.log(result.user.uid), +); +signOut(modularAuth); +sendPasswordResetEmail(modularAuth, 'test@example.com', actionCodeSettings); +sendSignInLinkToEmail(modularAuth, 'test@example.com', actionCodeSettings); +setLanguageCode(modularAuth, 'fr'); +useUserAccessGroup(modularAuth, 'group.example'); +verifyPasswordResetCode(modularAuth, 'oob-code').then((email: string) => console.log(email)); +getCustomAuthDomain(modularAuth).then((domain: string) => console.log(domain)); +validatePassword(modularAuth, 'password123').then((status: PasswordValidationStatus) => + console.log(status.isValid), ); -sendPasswordResetEmail(authInstance, 'test@example.com').then(() => { - console.log('Password reset email sent'); -}); - -confirmPasswordReset(authInstance, 'code', 'newPassword').then(() => { - console.log('Password reset confirmed'); -}); - -verifyPasswordResetCode(authInstance, 'code').then((email: string) => { - console.log(email); -}); - -sendSignInLinkToEmail(authInstance, 'test@example.com').then(() => { - console.log('Sign in link sent'); -}); - -isSignInWithEmailLink(authInstance, 'email-link').then((isLink: boolean) => { - console.log(isLink); -}); - -fetchSignInMethodsForEmail(authInstance, 'test@example.com').then((methods: string[]) => { - console.log(methods); -}); - -useDeviceLanguage(authInstance); - -setLanguageCode(authInstance, 'fr').then(() => { - console.log('Language set'); +beforeAuthStateChanged(modularAuth, async (user: User | null) => { + console.log(user?.uid); }); +updateCurrentUser(modularAuth, null); +useDeviceLanguage(modularAuth); -connectAuthEmulator(authInstance, 'http://localhost:9099', { disableWarnings: false }); - -const modularResolver = getMultiFactorResolver( - authInstance, - {} as FirebaseAuthTypes.MultiFactorError, +const parsedActionCode = parseActionCodeURL( + 'https://example.com/auth?mode=verifyEmail&oobCode=abc', ); -console.log(modularResolver); - -// Test User methods if currentUser exists -const currentUser = authInstance.currentUser; -if (currentUser) { - currentUser.reload().then(() => { - console.log('User reloaded'); - }); - - currentUser.getIdToken().then((token: string) => { - console.log(token); - }); - - currentUser.getIdToken(true).then((token: string) => { - console.log(token); - }); - - currentUser.getIdTokenResult().then((result: FirebaseAuthTypes.IdTokenResult) => { - console.log(result.token); - }); - - currentUser.sendEmailVerification().then(() => { - console.log('Verification email sent'); - }); - - currentUser.updateEmail('new@example.com').then(() => { - console.log('Email updated'); - }); - - currentUser.updatePassword('newPassword').then(() => { - console.log('Password updated'); - }); - - currentUser.updateProfile({ displayName: 'New Name' }).then(() => { - console.log('Profile updated'); - }); - - const multiFactorUser = multiFactor(currentUser); - console.log(multiFactorUser); +console.log(parsedActionCode?.code); + +const additionalUserInfo = getAdditionalUserInfo({ + additionalUserInfo: null, + user: {} as User, +} as unknown as UserCredential); +console.log(additionalUserInfo); + +const maybeUser = namespacedAuth.currentUser; +if (maybeUser) { + maybeUser.reload(); + maybeUser.getIdToken().then((token: string) => console.log(token)); + maybeUser.getIdTokenResult().then((result: IdTokenResult) => console.log(result.claims)); + maybeUser.sendEmailVerification(actionCodeSettings); + maybeUser.updateEmail('new@example.com'); + maybeUser.updatePassword('new-password'); + maybeUser.updatePhoneNumber(phoneCredential); + maybeUser.updateProfile({ displayName: 'New Name', photoURL: 'https://example.com/photo.png' }); + + const mfaUser = multiFactor(maybeUser); + mfaUser.getSession(); } diff --git a/packages/vertexai/tsconfig.json b/packages/vertexai/tsconfig.json index 837c336e3e..f57afbea70 100644 --- a/packages/vertexai/tsconfig.json +++ b/packages/vertexai/tsconfig.json @@ -32,7 +32,7 @@ ], "@react-native-firebase/app/lib/internal": ["../app/dist/typescript/lib/internal"], "@react-native-firebase/app": ["../app/dist/typescript/lib"], - "@react-native-firebase/auth": ["../auth/lib"], + "@react-native-firebase/auth": ["../auth/dist/typescript/lib"], "@react-native-firebase/app-check": ["../app-check/dist/typescript/lib"] } }, diff --git a/tsconfig.json b/tsconfig.json index 0e75593f74..c96f4815fd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,8 +3,6 @@ "packages/ai/lib/types/polyfills.d.ts", "packages/app/lib/internal/global.d.ts", "packages/app/lib/internal/web/memidb/index.d.ts", - "packages/auth/lib/index.d.ts", - "packages/auth/lib/modular/index.d.ts", "packages/database/lib/index.d.ts", "packages/database/lib/modular/index.d.ts", "packages/database/lib/modular/query.d.ts", @@ -74,7 +72,7 @@ ], "@react-native-firebase/firestore/pipelines": [ "./packages/firestore/dist/typescript/lib/pipelines/index.d.ts" - ], + ] } }, "exclude": ["node_modules", "**/*.spec.ts", "packages/**/dist"] diff --git a/yarn.lock b/yarn.lock index 1721ecdee5..d53ee2264f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6302,6 +6302,7 @@ __metadata: "@types/plist": "npm:^3.0.5" expo: "npm:^55.0.5" plist: "npm:^3.1.0" + react-native-builder-bob: "npm:^0.40.13" peerDependencies: "@react-native-firebase/app": 24.0.0 expo: ">=47.0.0"