From e9e0e66211feb810084820f83245e614ed1a1a6a Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 19 Dec 2025 14:51:39 +0000 Subject: [PATCH 01/16] feat: setup package.json and tsconfig.json --- packages/app-check/package.json | 65 +++++++++++++++++++++++++++++--- packages/app-check/tsconfig.json | 40 ++++++++++++++++++++ 2 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 packages/app-check/tsconfig.json diff --git a/packages/app-check/package.json b/packages/app-check/package.json index a17502d2cc..2daef5bccd 100644 --- a/packages/app-check/package.json +++ b/packages/app-check/package.json @@ -3,14 +3,16 @@ "version": "23.7.0", "author": "Invertase (http://invertase.io)", "description": "React Native Firebase - App Check", - "main": "lib/index.js", - "types": "lib/index.d.ts", + "main": "./dist/commonjs/index.js", + "module": "./dist/module/index.js", + "types": "./dist/typescript/commonjs/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", "lint:plugin": "eslint plugin/src/*", - "prepare": "yarn run build && yarn run build:plugin" + "compile": "bob build", + "prepare": "yarn run build && yarn compile && yarn run build:plugin" }, "repository": { "type": "git", @@ -29,7 +31,9 @@ "expo": ">=47.0.0" }, "devDependencies": { - "expo": "^54.0.27" + "expo": "^54.0.27", + "react-native-builder-bob": "^0.40.12", + "typescript": "^5.8.3" }, "peerDependenciesMeta": { "expo": { @@ -39,5 +43,54 @@ "publishConfig": { "access": "public", "provenance": true - } + }, + "exports": { + ".": { + "source": "./lib/index.ts", + "import": { + "types": "./dist/typescript/module/lib/index.d.ts", + "default": "./dist/module/index.js" + }, + "require": { + "types": "./dist/typescript/commonjs/lib/index.d.ts", + "default": "./dist/commonjs/index.js" + } + }, + "./package.json": "./package.json" + }, + "files": [ + "lib/modular", + "dist", + "!**/__tests__", + "!**/__fixtures__", + "!**/__mocks__" + ], + "react-native-builder-bob": { + "source": "lib", + "output": "dist", + "targets": [ + [ + "module", + { + "esm": true + } + ], + [ + "commonjs", + { + "esm": true + } + ], + [ + "typescript", + { + "tsc": "../../node_modules/.bin/tsc" + } + ] + ] + }, + "eslintIgnore": [ + "node_modules/", + "dist/" + ] } diff --git a/packages/app-check/tsconfig.json b/packages/app-check/tsconfig.json new file mode 100644 index 0000000000..ea2ec9d3f5 --- /dev/null +++ b/packages/app-check/tsconfig.json @@ -0,0 +1,40 @@ +{ + "compilerOptions": { + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react-jsx", + "lib": ["ESNext"], + "module": "ESNext", + "moduleResolution": "bundler", + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "noImplicitUseStrict": false, + "noStrictGenericChecks": false, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "ESNext", + "verbatimModuleSyntax": true, + "baseUrl": ".", + "rootDir": ".", + "paths": { + "@react-native-firebase/app/lib/common/*": ["../app/dist/typescript/commonjs/lib/common/*"], + "@react-native-firebase/app/lib/common": ["../app/dist/typescript/commonjs/lib/common"], + "@react-native-firebase/app/lib/internal/web/*": [ + "../app/dist/typescript/commonjs/lib/internal/web/*" + ], + "@react-native-firebase/app/lib/internal/*": [ + "../app/dist/typescript/commonjs/lib/internal/*" + ], + "@react-native-firebase/app/lib/internal": ["../app/dist/typescript/commonjs/lib/internal"], + "@react-native-firebase/app": ["../app/dist/typescript/commonjs/lib"] + } + }, + "include": ["lib/**/*"], + "exclude": ["node_modules", "dist", "__tests__", "**/*.test.ts"] +} From 16067dfba4411a0d3f15080b50d0cc827e8088ae Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 19 Dec 2025 15:38:32 +0000 Subject: [PATCH 02/16] refactor: move to Typescript --- .../ReactNativeFirebaseAppCheckProvider.ts | 18 ++ packages/app-check/lib/index.ts | 26 ++ packages/app-check/lib/modular.ts | 152 +++++++++ packages/app-check/lib/namespaced.ts | 290 ++++++++++++++++++ .../lib/{index.d.ts => types/appcheck.ts} | 27 +- .../lib/web/RNFBAppCheckModule.android.ts | 4 + .../lib/web/RNFBAppCheckModule.ios.ts | 4 + .../app-check/lib/web/RNFBAppCheckModule.ts | 115 +++++++ 8 files changed, 610 insertions(+), 26 deletions(-) create mode 100644 packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.ts create mode 100644 packages/app-check/lib/index.ts create mode 100644 packages/app-check/lib/modular.ts create mode 100644 packages/app-check/lib/namespaced.ts rename packages/app-check/lib/{index.d.ts => types/appcheck.ts} (94%) create mode 100644 packages/app-check/lib/web/RNFBAppCheckModule.android.ts create mode 100644 packages/app-check/lib/web/RNFBAppCheckModule.ios.ts create mode 100644 packages/app-check/lib/web/RNFBAppCheckModule.ts diff --git a/packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.ts b/packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.ts new file mode 100644 index 0000000000..edcdf02e40 --- /dev/null +++ b/packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.ts @@ -0,0 +1,18 @@ +import type { FirebaseAppCheckTypes } from './types/appcheck'; + +interface ProviderOptions { + web?: FirebaseAppCheckTypes.ReactNativeFirebaseAppCheckProviderWebOptions; + android?: FirebaseAppCheckTypes.ReactNativeFirebaseAppCheckProviderAndroidOptions; + apple?: FirebaseAppCheckTypes.ReactNativeFirebaseAppCheckProviderAppleOptions; + isTokenAutoRefreshEnabled?: boolean; +} + +export default class ReactNativeFirebaseAppCheckProvider { + providerOptions?: ProviderOptions; + + constructor() {} + + configure(options: ProviderOptions): void { + this.providerOptions = options; + } +} diff --git a/packages/app-check/lib/index.ts b/packages/app-check/lib/index.ts new file mode 100644 index 0000000000..723319dca4 --- /dev/null +++ b/packages/app-check/lib/index.ts @@ -0,0 +1,26 @@ +/* + * 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 types from types/appcheck +export type { FirebaseAppCheckTypes } from './types/appcheck'; + +// Export modular API functions +export * from './modular'; + +// Export namespaced API +export * from './namespaced'; +export { default } from './namespaced'; diff --git a/packages/app-check/lib/modular.ts b/packages/app-check/lib/modular.ts new file mode 100644 index 0000000000..08e98888fe --- /dev/null +++ b/packages/app-check/lib/modular.ts @@ -0,0 +1,152 @@ +/* + * 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/lib/common'; +import ReactNativeFirebaseAppCheckProvider from './ReactNativeFirebaseAppCheckProvider'; +import type { FirebaseAppCheckTypes } from './types/appcheck'; +import type { ReactNativeFirebase } from '@react-native-firebase/app'; + +type FirebaseApp = ReactNativeFirebase.FirebaseApp; +type AppCheck = FirebaseAppCheckTypes.Module; +type AppCheckOptions = FirebaseAppCheckTypes.AppCheckOptions; +type AppCheckTokenResult = FirebaseAppCheckTypes.AppCheckTokenResult; +type PartialObserver = FirebaseAppCheckTypes.PartialObserver; +type Unsubscribe = FirebaseAppCheckTypes.Unsubscribe; +type AppCheckListenerResult = FirebaseAppCheckTypes.AppCheckListenerResult; + +/** + * Activate App Check for the given app. Can be called only once per app. + * @param app - The app to initialize App Check for. Optional. + * @param options - App Check options. + * @returns Promise + */ +export async function initializeAppCheck( + app?: FirebaseApp, + options?: AppCheckOptions, +): Promise { + if (app) { + const appInstance = getApp(app.name) as any; + const appCheck = appInstance.appCheck(); + await appCheck.initializeAppCheck.call(appCheck, options, MODULAR_DEPRECATION_ARG); + return appCheck; + } + const appInstance = getApp() as any; + const appCheck = appInstance.appCheck(); + await appCheck.initializeAppCheck.call(appCheck, options, MODULAR_DEPRECATION_ARG); + return appCheck; +} + +/** + * Get the current App Check token. Attaches to the most recent in-flight request if one is present. + * Returns null if no token is present and no token requests are in-flight. + * @param appCheckInstance - The App Check instance. + * @param forceRefresh - Whether to force refresh the token. Optional + * @returns Promise + */ +export function getToken( + appCheckInstance: AppCheck, + forceRefresh?: boolean, +): Promise { + return (appCheckInstance.getToken as any).call( + appCheckInstance, + forceRefresh, + MODULAR_DEPRECATION_ARG, + ) as Promise; +} + +/** + * Get a limited-use (consumable) App Check token. + * For use with server calls to firebase functions or custom backends using the firebase admin SDK. + * @param appCheckInstance - The App Check instance. + * @returns Promise + */ +export function getLimitedUseToken(appCheckInstance: AppCheck): Promise { + return (appCheckInstance.getLimitedUseToken as any).call( + appCheckInstance, + MODULAR_DEPRECATION_ARG, + ) as Promise; +} + +/** + * Set whether App Check will automatically refresh tokens as needed. + * @param appCheckInstance - The App Check instance. + * @param isAutoRefreshEnabled - Whether to enable auto-refresh. + */ +export function setTokenAutoRefreshEnabled( + appCheckInstance: AppCheck, + isAutoRefreshEnabled: boolean, +): void { + (appCheckInstance.setTokenAutoRefreshEnabled as any).call( + appCheckInstance, + isAutoRefreshEnabled, + MODULAR_DEPRECATION_ARG, + ); +} + +/** + * Registers a listener to changes in the token state. There can be more + * than one listener registered at the same time for one or more + * App Check instances. The listeners call back on the UI thread whenever + * the current token associated with this App Check instance changes. + * + * @param appCheckInstance - The App Check instance. + * @param listener - The listener to register. + * @returns Unsubscribe + */ +export function onTokenChanged( + appCheckInstance: AppCheck, + listener: PartialObserver, +): Unsubscribe; + +/** + * Registers a listener to changes in the token state. There can be more + * than one listener registered at the same time for one or more + * App Check instances. The listeners call back on the UI thread whenever + * the current token associated with this App Check instance changes. + * + * @param appCheckInstance - The App Check instance. + * @param onNext - The callback function for token changes. + * @param onError - Optional error callback. + * @param onCompletion - Optional completion callback. + * @returns Unsubscribe + */ +export function onTokenChanged( + appCheckInstance: AppCheck, + onNext: (tokenResult: AppCheckListenerResult) => void, + onError?: (error: Error) => void, + onCompletion?: () => void, +): Unsubscribe; + +export function onTokenChanged( + appCheckInstance: AppCheck, + onNextOrObserver: + | PartialObserver + | ((tokenResult: AppCheckListenerResult) => void), + onError?: (error: Error) => void, + onCompletion?: () => void, +): Unsubscribe { + return (appCheckInstance.onTokenChanged as any).call( + appCheckInstance, + onNextOrObserver, + onError, + onCompletion, + MODULAR_DEPRECATION_ARG, + ) as Unsubscribe; +} + +export { ReactNativeFirebaseAppCheckProvider }; diff --git a/packages/app-check/lib/namespaced.ts b/packages/app-check/lib/namespaced.ts new file mode 100644 index 0000000000..e041ec4b6c --- /dev/null +++ b/packages/app-check/lib/namespaced.ts @@ -0,0 +1,290 @@ +/* + * 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 { + isBoolean, + isIOS, + isString, + isObject, + isFunction, + isUndefined, + isOther, + parseListenerOrObserver, +} from '@react-native-firebase/app/lib/common'; +import { + createModuleNamespace, + FirebaseModule, + getFirebaseRoot, +} from '@react-native-firebase/app/lib/internal'; +import { Platform } from 'react-native'; +import ReactNativeFirebaseAppCheckProvider from './ReactNativeFirebaseAppCheckProvider'; +import { setReactNativeModule } from '@react-native-firebase/app/lib/internal/nativeModule'; +import fallBackModule from './web/RNFBAppCheckModule'; +import { version } from './version'; + +const namespace = 'appCheck'; + +const nativeModuleName = 'RNFBAppCheckModule'; + +interface CustomProviderOptions { + getToken: () => Promise<{ token: string; expireTimeMillis: number }>; +} + +interface AppCheckProvider { + getToken(): Promise<{ token: string; expireTimeMillis: number }>; +} + +interface AppCheckTokenResult { + readonly token: string; +} + +interface AppCheckOptions { + provider: AppCheckProvider | ReactNativeFirebaseAppCheckProvider; + isTokenAutoRefreshEnabled?: boolean; +} + +interface AppCheckListenerResult { + readonly token: string; + readonly expireTimeMillis: number; + readonly appName: string; +} + +type PartialObserver = Partial<{ + next: (value: T) => void; + error: (error: Error) => void; + complete: () => void; +}>; + +export class CustomProvider implements AppCheckProvider { + private _customProviderOptions: CustomProviderOptions; + + constructor(_customProviderOptions: CustomProviderOptions) { + if (!isObject(_customProviderOptions)) { + throw new Error('Invalid configuration: no provider options defined.'); + } + if (!isFunction(_customProviderOptions.getToken)) { + throw new Error('Invalid configuration: no getToken function defined.'); + } + this._customProviderOptions = _customProviderOptions; + } + + async getToken(): Promise<{ token: string; expireTimeMillis: number }> { + return this._customProviderOptions.getToken(); + } +} + +const statics = { + CustomProvider, +}; + +class FirebaseAppCheckModule extends FirebaseModule { + _listenerCount: number; + + constructor(app: any, config: any, customUrlOrRegion?: string | null) { + super(app, config, customUrlOrRegion); + + this.emitter.addListener(this.eventNameForApp('appCheck_token_changed'), event => { + this.emitter.emit(this.eventNameForApp('onAppCheckTokenChanged'), event); + }); + + this._listenerCount = 0; + } + + getIsTokenRefreshEnabledDefault(): boolean | undefined { + // no default to start + let isTokenAutoRefreshEnabled: boolean | undefined = undefined; + + return isTokenAutoRefreshEnabled; + } + + newReactNativeFirebaseAppCheckProvider(): ReactNativeFirebaseAppCheckProvider { + return new ReactNativeFirebaseAppCheckProvider(); + } + + initializeAppCheck(options: AppCheckOptions): Promise { + if (isOther) { + if (!isObject(options)) { + throw new Error('Invalid configuration: no options defined.'); + } + if (isUndefined(options.provider)) { + throw new Error('Invalid configuration: no provider defined.'); + } + return this.native.initializeAppCheck(options); + } + // determine token refresh setting, if not specified + if (!isBoolean(options.isTokenAutoRefreshEnabled)) { + const tokenRefresh = this.firebaseJson.app_check_token_auto_refresh; + if (isBoolean(tokenRefresh)) { + options.isTokenAutoRefreshEnabled = tokenRefresh; + } + } + + // If that was not defined, attempt to use app-wide data collection setting per docs: + if (!isBoolean(options.isTokenAutoRefreshEnabled)) { + const dataCollection = this.firebaseJson.app_data_collection_default_enabled; + if (isBoolean(dataCollection)) { + options.isTokenAutoRefreshEnabled = dataCollection; + } + } + + // If that also was not defined, the default is documented as true. + if (!isBoolean(options.isTokenAutoRefreshEnabled)) { + options.isTokenAutoRefreshEnabled = true; + } + this.native.setTokenAutoRefreshEnabled(options.isTokenAutoRefreshEnabled); + + if ( + options.provider === undefined || + (options.provider as ReactNativeFirebaseAppCheckProvider).providerOptions === undefined + ) { + throw new Error('Invalid configuration: no provider or no provider options defined.'); + } + const provider = options.provider as ReactNativeFirebaseAppCheckProvider; + if (Platform.OS === 'android') { + if (!isString(provider.providerOptions?.android?.provider)) { + throw new Error( + 'Invalid configuration: no android provider configured while on android platform.', + ); + } + return this.native.configureProvider( + provider.providerOptions.android.provider, + provider.providerOptions.android.debugToken, + ); + } + if (Platform.OS === 'ios' || Platform.OS === 'macos') { + if (!isString(provider.providerOptions?.apple?.provider)) { + throw new Error( + 'Invalid configuration: no apple provider configured while on apple platform.', + ); + } + return this.native.configureProvider( + provider.providerOptions.apple.provider, + provider.providerOptions.apple.debugToken, + ); + } + throw new Error('Unsupported platform: ' + Platform.OS); + } + + activate( + siteKeyOrProvider: string | AppCheckProvider, + isTokenAutoRefreshEnabled?: boolean, + ): Promise { + if (isOther) { + throw new Error('firebase.appCheck().activate(*) is not supported on other platforms'); + } + if (!isString(siteKeyOrProvider)) { + throw new Error('siteKeyOrProvider must be a string value to match firebase-js-sdk API'); + } + + // We wrap our new flexible interface, with compatible defaults + const rnfbProvider = new ReactNativeFirebaseAppCheckProvider(); + rnfbProvider.configure({ + android: { + provider: 'playIntegrity', + }, + apple: { + provider: 'deviceCheck', + }, + web: { + provider: 'reCaptchaV3', + siteKey: 'none', + }, + }); + + return this.initializeAppCheck({ provider: rnfbProvider, isTokenAutoRefreshEnabled }); + } + + // TODO this is an async call + setTokenAutoRefreshEnabled(isTokenAutoRefreshEnabled: boolean): void { + this.native.setTokenAutoRefreshEnabled(isTokenAutoRefreshEnabled); + } + + getToken(forceRefresh?: boolean): Promise { + if (!forceRefresh) { + return this.native.getToken(false); + } else { + return this.native.getToken(true); + } + } + + getLimitedUseToken(): Promise { + return this.native.getLimitedUseToken(); + } + + onTokenChanged( + onNextOrObserver: + | PartialObserver + | ((tokenResult: AppCheckListenerResult) => void), + _onError?: (error: Error) => void, + _onCompletion?: () => void, + ): () => void { + // iOS does not provide any native listening feature + if (isIOS) { + // eslint-disable-next-line no-console + console.warn('onTokenChanged is not implemented on IOS, only for Android'); + return () => {}; + } + const nextFn = parseListenerOrObserver( + onNextOrObserver as + | ((value: AppCheckListenerResult) => void) + | { next: (value: AppCheckListenerResult) => void }, + ); + // let errorFn = function () { }; + // if (onNextOrObserver.error != null) { + // errorFn = onNextOrObserver.error.bind(onNextOrObserver); + // } + // else if (onError) { + // errorFn = onError; + // } + const subscription = this.emitter.addListener( + this.eventNameForApp('onAppCheckTokenChanged'), + nextFn, + ); + if (this._listenerCount === 0) this.native.addAppCheckListener(); + + this._listenerCount++; + return () => { + subscription.remove(); + this._listenerCount--; + if (this._listenerCount === 0) this.native.removeAppCheckListener(); + }; + } +} + +// import { SDK_VERSION } from '@react-native-firebase/app-check'; +export const SDK_VERSION = version; + +// import appCheck from '@react-native-firebase/app-check'; +// appCheck().X(...); +export default createModuleNamespace({ + statics, + version, + namespace, + nativeModuleName, + nativeEvents: ['appCheck_token_changed'], + hasMultiAppSupport: true, + hasCustomUrlOrRegionSupport: false, + ModuleClass: FirebaseAppCheckModule, +}); + +// import appCheck, { firebase } from '@react-native-firebase/app-check'; +// appCheck().X(...); +// firebase.appCheck().X(...); +export const firebase = getFirebaseRoot(); + +// Register the interop module for non-native platforms. +setReactNativeModule(nativeModuleName, fallBackModule as unknown as Record); diff --git a/packages/app-check/lib/index.d.ts b/packages/app-check/lib/types/appcheck.ts similarity index 94% rename from packages/app-check/lib/index.d.ts rename to packages/app-check/lib/types/appcheck.ts index 6463612a84..fdd91e46c7 100644 --- a/packages/app-check/lib/index.d.ts +++ b/packages/app-check/lib/types/appcheck.ts @@ -15,7 +15,7 @@ * */ -import { ReactNativeFirebase } from '@react-native-firebase/app'; +import type { ReactNativeFirebase } from '@react-native-firebase/app'; /** * Firebase App Check package for React Native. @@ -334,31 +334,6 @@ export namespace FirebaseAppCheckTypes { } } -type AppCheckNamespace = ReactNativeFirebase.FirebaseModuleWithStaticsAndApp< - FirebaseAppCheckTypes.Module, - FirebaseAppCheckTypes.Statics -> & { - appCheck: ReactNativeFirebase.FirebaseModuleWithStaticsAndApp< - FirebaseAppCheckTypes.Module, - FirebaseAppCheckTypes.Statics - >; - firebase: ReactNativeFirebase.Module; - app(name?: string): ReactNativeFirebase.FirebaseApp; -}; - -declare const defaultExport: AppCheckNamespace; - -export const firebase: ReactNativeFirebase.Module & { - appCheck: typeof defaultExport; - app( - name?: string, - ): ReactNativeFirebase.FirebaseApp & { appCheck(): FirebaseAppCheckTypes.Module }; -}; - -export default defaultExport; - -export * from './modular'; - /** * Attach namespace to `firebase.` and `FirebaseApp.`. */ diff --git a/packages/app-check/lib/web/RNFBAppCheckModule.android.ts b/packages/app-check/lib/web/RNFBAppCheckModule.android.ts new file mode 100644 index 0000000000..6945829a16 --- /dev/null +++ b/packages/app-check/lib/web/RNFBAppCheckModule.android.ts @@ -0,0 +1,4 @@ +// No-op for android. +const module: Record = {}; + +export default module; diff --git a/packages/app-check/lib/web/RNFBAppCheckModule.ios.ts b/packages/app-check/lib/web/RNFBAppCheckModule.ios.ts new file mode 100644 index 0000000000..055f3bce5b --- /dev/null +++ b/packages/app-check/lib/web/RNFBAppCheckModule.ios.ts @@ -0,0 +1,4 @@ +// No-op for ios. +const module: Record = {}; + +export default module; diff --git a/packages/app-check/lib/web/RNFBAppCheckModule.ts b/packages/app-check/lib/web/RNFBAppCheckModule.ts new file mode 100644 index 0000000000..ab634e007b --- /dev/null +++ b/packages/app-check/lib/web/RNFBAppCheckModule.ts @@ -0,0 +1,115 @@ +import { + getApp, + initializeAppCheck, + getToken, + getLimitedUseToken, + setTokenAutoRefreshEnabled, + CustomProvider, + onTokenChanged, + makeIDBAvailable, +} from '@react-native-firebase/app/lib/internal/web/firebaseAppCheck'; +import { guard, emitEvent } from '@react-native-firebase/app/lib/internal/web/utils'; +import type { FirebaseAppCheckTypes } from '../types/appcheck'; + +let appCheckInstances: Record = {}; +let listenersForApp: Record void> = {}; + +function getAppCheckInstanceForApp(appName: string): any { + if (!appCheckInstances[appName]) { + throw new Error( + `firebase AppCheck instance for app ${appName} has not been initialized, ensure you have called initializeAppCheck() first.`, + ); + } + return appCheckInstances[appName]; +} + +interface AppCheckModule { + initializeAppCheck( + appName: string, + options: FirebaseAppCheckTypes.AppCheckOptions, + ): Promise; + setTokenAutoRefreshEnabled(appName: string, isTokenAutoRefreshEnabled: boolean): Promise; + getLimitedUseToken(appName: string): Promise; + getToken( + appName: string, + forceRefresh: boolean, + ): Promise; + addAppCheckListener(appName: string): Promise; + removeAppCheckListener(appName: string): Promise; +} + +/** + * This is a 'NativeModule' for the web platform. + * Methods here are identical to the ones found in + * the native android/ios modules e.g. `@ReactMethod` annotated + * java methods on Android. + */ +const module: AppCheckModule = { + initializeAppCheck(appName: string, options: FirebaseAppCheckTypes.AppCheckOptions) { + makeIDBAvailable(); + return guard(async () => { + if (appCheckInstances[appName]) { + return; + } + const { provider, isTokenAutoRefreshEnabled } = options; + const _provider = new CustomProvider({ + getToken() { + if ('getToken' in provider && typeof provider.getToken === 'function') { + return provider.getToken(); + } + throw new Error('Provider does not have a getToken method'); + }, + }); + appCheckInstances[appName] = initializeAppCheck(getApp(appName), { + provider: _provider, + isTokenAutoRefreshEnabled, + }); + }); + }, + setTokenAutoRefreshEnabled(appName: string, isTokenAutoRefreshEnabled: boolean) { + return guard(async () => { + const instance = getAppCheckInstanceForApp(appName); + setTokenAutoRefreshEnabled(instance, isTokenAutoRefreshEnabled); + }); + }, + getLimitedUseToken(appName: string) { + return guard(async () => { + const instance = getAppCheckInstanceForApp(appName); + return getLimitedUseToken(instance); + }); + }, + getToken(appName: string, forceRefresh: boolean) { + return guard(async () => { + const instance = getAppCheckInstanceForApp(appName); + return getToken(instance, forceRefresh); + }); + }, + addAppCheckListener(appName: string) { + return guard(async () => { + if (listenersForApp[appName]) { + return; + } + const instance = getAppCheckInstanceForApp(appName); + listenersForApp[appName] = onTokenChanged( + instance, + (tokenResult: FirebaseAppCheckTypes.AppCheckTokenResult) => { + emitEvent('appCheck_token_changed', { + appName, + ...tokenResult, + }); + }, + ); + }); + }, + removeAppCheckListener(appName: string) { + return guard(async () => { + if (!listenersForApp[appName]) { + return; + } + listenersForApp[appName](); + delete listenersForApp[appName]; + }); + }, +}; + +export default module; From fa3fc64754117629d9e568681d013ed754b79bdf Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 19 Dec 2025 16:04:34 +0000 Subject: [PATCH 03/16] refactor: move to index, namespaced and modular --- .../ReactNativeFirebaseAppCheckProvider.ts | 22 +- packages/app-check/lib/index.ts | 24 +- packages/app-check/lib/namespaced.ts | 39 +- packages/app-check/lib/types/appcheck.ts | 579 ++++++++++-------- 4 files changed, 375 insertions(+), 289 deletions(-) diff --git a/packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.ts b/packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.ts index edcdf02e40..df3b7f2374 100644 --- a/packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.ts +++ b/packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.ts @@ -1,13 +1,19 @@ -import type { FirebaseAppCheckTypes } from './types/appcheck'; +import type { + AppCheckProvider, + AppCheckToken, + ReactNativeFirebaseAppCheckProviderWebOptions, + ReactNativeFirebaseAppCheckProviderAndroidOptions, + ReactNativeFirebaseAppCheckProviderAppleOptions, +} from './types/appcheck'; interface ProviderOptions { - web?: FirebaseAppCheckTypes.ReactNativeFirebaseAppCheckProviderWebOptions; - android?: FirebaseAppCheckTypes.ReactNativeFirebaseAppCheckProviderAndroidOptions; - apple?: FirebaseAppCheckTypes.ReactNativeFirebaseAppCheckProviderAppleOptions; + web?: ReactNativeFirebaseAppCheckProviderWebOptions; + android?: ReactNativeFirebaseAppCheckProviderAndroidOptions; + apple?: ReactNativeFirebaseAppCheckProviderAppleOptions; isTokenAutoRefreshEnabled?: boolean; } -export default class ReactNativeFirebaseAppCheckProvider { +export default class ReactNativeFirebaseAppCheckProvider implements AppCheckProvider { providerOptions?: ProviderOptions; constructor() {} @@ -15,4 +21,10 @@ export default class ReactNativeFirebaseAppCheckProvider { configure(options: ProviderOptions): void { this.providerOptions = options; } + + async getToken(): Promise { + // This is a placeholder - the actual implementation is handled by the native modules + // This method exists to satisfy the AppCheckProvider interface + throw new Error('getToken() must be called after configure() and is handled by native modules'); + } } diff --git a/packages/app-check/lib/index.ts b/packages/app-check/lib/index.ts index 723319dca4..6eec90c687 100644 --- a/packages/app-check/lib/index.ts +++ b/packages/app-check/lib/index.ts @@ -16,7 +16,29 @@ */ // Export types from types/appcheck -export type { FirebaseAppCheckTypes } from './types/appcheck'; +export type { + AppCheckProvider, + CustomProviderOptions, + AppCheckOptions, + AppCheckToken, + AppCheckTokenResult, + AppCheckListenerResult, + PartialObserver, + Observer, + NextFn, + ErrorFn, + CompleteFn, + Unsubscribe, + ReactNativeFirebaseAppCheckProviderOptions, + ReactNativeFirebaseAppCheckProviderWebOptions, + ReactNativeFirebaseAppCheckProviderAppleOptions, + ReactNativeFirebaseAppCheckProviderAndroidOptions, + ReactNativeFirebaseAppCheckProvider, + AppCheckModule, + AppCheckStatics, + AppCheck, + FirebaseAppCheckTypes, +} from './types/appcheck'; // Export modular API functions export * from './modular'; diff --git a/packages/app-check/lib/namespaced.ts b/packages/app-check/lib/namespaced.ts index e041ec4b6c..a68b0bdb5f 100644 --- a/packages/app-check/lib/namespaced.ts +++ b/packages/app-check/lib/namespaced.ts @@ -35,40 +35,19 @@ import ReactNativeFirebaseAppCheckProvider from './ReactNativeFirebaseAppCheckPr import { setReactNativeModule } from '@react-native-firebase/app/lib/internal/nativeModule'; import fallBackModule from './web/RNFBAppCheckModule'; import { version } from './version'; +import type { + CustomProviderOptions, + AppCheckProvider, + AppCheckTokenResult, + AppCheckOptions, + AppCheckListenerResult, + PartialObserver, +} from './types/appcheck'; const namespace = 'appCheck'; const nativeModuleName = 'RNFBAppCheckModule'; -interface CustomProviderOptions { - getToken: () => Promise<{ token: string; expireTimeMillis: number }>; -} - -interface AppCheckProvider { - getToken(): Promise<{ token: string; expireTimeMillis: number }>; -} - -interface AppCheckTokenResult { - readonly token: string; -} - -interface AppCheckOptions { - provider: AppCheckProvider | ReactNativeFirebaseAppCheckProvider; - isTokenAutoRefreshEnabled?: boolean; -} - -interface AppCheckListenerResult { - readonly token: string; - readonly expireTimeMillis: number; - readonly appName: string; -} - -type PartialObserver = Partial<{ - next: (value: T) => void; - error: (error: Error) => void; - complete: () => void; -}>; - export class CustomProvider implements AppCheckProvider { private _customProviderOptions: CustomProviderOptions; @@ -82,7 +61,7 @@ export class CustomProvider implements AppCheckProvider { this._customProviderOptions = _customProviderOptions; } - async getToken(): Promise<{ token: string; expireTimeMillis: number }> { + async getToken() { return this._customProviderOptions.getToken(); } } diff --git a/packages/app-check/lib/types/appcheck.ts b/packages/app-check/lib/types/appcheck.ts index fdd91e46c7..16a0ed6a6a 100644 --- a/packages/app-check/lib/types/appcheck.ts +++ b/packages/app-check/lib/types/appcheck.ts @@ -17,91 +17,349 @@ import type { ReactNativeFirebase } from '@react-native-firebase/app'; +// ============ Provider Types ============ + /** - * Firebase App Check package for React Native. - * - * #### Example 1 - * - * Access the firebase export from the `appCheck` package: - * - * ```js - * import { firebase } from '@react-native-firebase/app-check'; - * - * // firebase.appCheck().X - * ``` - * - * #### Example 2 - * - * Using the default export from the `appCheck` package: - * - * ```js - * import appCheck from '@react-native-firebase/app-check'; - * - * // appCheck().X - * ``` - * - * #### Example 3 - * - * Using the default export from the `app` package: - * - * ```js - * import firebase from '@react-native-firebase/app'; - * import '@react-native-firebase/app-check'; - * - * // firebase.appCheck().X - * ``` - * - * @firebase app-check + * An App Check provider. This can be either the built-in reCAPTCHA provider + * or a custom provider. For more on custom providers, see + * https://firebase.google.com/docs/app-check/web-custom-provider */ -export namespace FirebaseAppCheckTypes { - import FirebaseModule = ReactNativeFirebase.FirebaseModule; +export interface AppCheckProvider { + /** + * Returns an AppCheck token. + */ + getToken(): Promise; +} + +/** + * Function to get an App Check token through a custom provider service. + */ +export interface CustomProviderOptions { + getToken: () => Promise; +} + +// ============ Options & Result Types ============ + +/** + * Custom provider class. + * @public + */ +export declare class CustomProvider implements AppCheckProvider { + constructor(customProviderOptions: CustomProviderOptions); + getToken(): Promise; +} + +/** + * Options for App Check initialization. + */ +export interface AppCheckOptions { + /** + * The App Check provider to use. This can be either the built-in reCAPTCHA provider + * or a custom provider. + */ + provider: CustomProvider | ReactNativeFirebaseAppCheckProvider; + + /** + * If true, enables SDK to automatically + * refresh AppCheck token as needed. If undefined, the value will default + * to the value of `app.automaticDataCollectionEnabled`. That property + * defaults to false and can be set in the app config. + */ + isTokenAutoRefreshEnabled?: boolean; +} + +/** + * The token returned from an `AppCheckProvider`. + */ +export interface AppCheckToken { + /** + * The token string in JWT format. + */ + readonly token: string; + /** + * The local timestamp after which the token will expire. + */ + readonly expireTimeMillis: number; +} + +/** + * Result returned by `getToken()`. + */ +export interface AppCheckTokenResult { + /** + * The token string in JWT format. + */ + readonly token: string; +} + +/** + * The result return from `onTokenChanged` + */ +export type AppCheckListenerResult = AppCheckToken & { readonly appName: string }; + +// ============ Observer Types ============ + +export type NextFn = (value: T) => void; +export type ErrorFn = (error: Error) => void; +export type CompleteFn = () => void; + +export interface Observer { + next: NextFn; + error: ErrorFn; + complete: CompleteFn; +} + +export type PartialObserver = Partial>; + +/** + * A function that unsubscribes from token changes. + */ +export type Unsubscribe = () => void; + +// ============ ReactNativeFirebaseAppCheckProvider Types ============ + +export interface ReactNativeFirebaseAppCheckProviderOptions { + /** + * debug token to use, if any. Defaults to undefined, pre-configure tokens in firebase web console if needed + */ + debugToken?: string; +} + +export interface ReactNativeFirebaseAppCheckProviderWebOptions extends ReactNativeFirebaseAppCheckProviderOptions { + /** + * The web provider to use, either `reCaptchaV3` or `reCaptchaEnterprise`, defaults to `reCaptchaV3` + */ + provider?: 'debug' | 'reCaptchaV3' | 'reCaptchaEnterprise'; + + /** + * siteKey for use in web queries, defaults to `none` + */ + siteKey?: string; +} + +export interface ReactNativeFirebaseAppCheckProviderAppleOptions extends ReactNativeFirebaseAppCheckProviderOptions { + /** + * The apple provider to use, either `deviceCheck` or `appAttest`, or `appAttestWithDeviceCheckFallback`, + * defaults to `DeviceCheck`. `appAttest` requires iOS 14+ or will fail, `appAttestWithDeviceCheckFallback` + * will use `appAttest` for iOS14+ and fallback to `deviceCheck` on devices with ios13 and lower + */ + provider?: 'debug' | 'deviceCheck' | 'appAttest' | 'appAttestWithDeviceCheckFallback'; +} + +export interface ReactNativeFirebaseAppCheckProviderAndroidOptions extends ReactNativeFirebaseAppCheckProviderOptions { + /** + * The android provider to use, either `debug` or `playIntegrity`. default is `playIntegrity`. + */ + provider?: 'debug' | 'playIntegrity'; +} + +export interface ReactNativeFirebaseAppCheckProvider extends AppCheckProvider { + /** + * Specify how the app check provider should be configured. The new configuration is + * in effect when this call returns. You must call `getToken()` + * after this call to get a token using the new configuration. + * This custom provider allows for delayed configuration and re-configuration on all platforms + * so AppCheck has the same experience across all platforms, with the only difference being the native + * providers you choose to use on each platform. + */ + configure(options: { + web?: ReactNativeFirebaseAppCheckProviderWebOptions; + android?: ReactNativeFirebaseAppCheckProviderAndroidOptions; + apple?: ReactNativeFirebaseAppCheckProviderAppleOptions; + isTokenAutoRefreshEnabled?: boolean; + }): void; +} + +// ============ Module Interface ============ + +/** + * App Check module instance - returned from firebase.appCheck() or firebase.app().appCheck() + */ +export interface AppCheckModule extends ReactNativeFirebase.FirebaseModule { + /** The FirebaseApp this module is associated with */ + app: ReactNativeFirebase.FirebaseApp; + + /** + * Create a ReactNativeFirebaseAppCheckProvider option for use in react-native-firebase + */ + newReactNativeFirebaseAppCheckProvider(): ReactNativeFirebaseAppCheckProvider; + + /** + * Initialize the AppCheck module. Note that in react-native-firebase AppCheckOptions must always + * be an object with a `provider` member containing `ReactNativeFirebaseAppCheckProvider` that has returned successfully + * from a call to the `configure` method, with sub-providers for the various platforms configured to meet your project + * requirements. This must be called prior to interacting with any firebase services protected by AppCheck + * + * @param options an AppCheckOptions with a configured ReactNativeFirebaseAppCheckProvider as the provider + */ + initializeAppCheck(options: AppCheckOptions): Promise; + + /** + * Activate App Check + * On iOS App Check is activated with DeviceCheck provider simply by including the module, using the token auto refresh default or + * the specific value (if configured) in firebase.json, but calling this does no harm. + * On Android if you call this it will install the PlayIntegrity provider in release builds, the Debug provider if debuggable. + * On both platforms you may use this method to alter the token refresh setting after startup. + * On iOS if you want to set a specific AppCheckProviderFactory (for instance to FIRAppCheckDebugProviderFactory or + * FIRAppAttestProvider) you must manually do that in your AppDelegate.m prior to calling [FIRApp configure] + * + * @deprecated use initializeAppCheck to gain access to all platform providers and firebase-js-sdk v9 compatibility + * @param siteKeyOrProvider - This is ignored, Android uses DebugProviderFactory if the app is debuggable (https://firebase.google.com/docs/app-check/android/debug-provider) + * Android uses PlayIntegrityProviderFactory for release builds. + * iOS uses DeviceCheckProviderFactory by default unless altered in AppDelegate.m manually + * @param isTokenAutoRefreshEnabled - If true, enables SDK to automatically + * refresh AppCheck token as needed. If undefined, the value will default + * to the value of `app.automaticDataCollectionEnabled`. That property + * defaults to false and can be set in the app config. + */ + activate( + siteKeyOrProvider: string | AppCheckProvider, + isTokenAutoRefreshEnabled?: boolean, + ): Promise; + + /** + * Alter the token auto refresh setting. By default it will take the value of automaticDataCollectionEnabled from Info.plist / AndroidManifest.xml + * @param isTokenAutoRefreshEnabled - If true, the SDK automatically + * refreshes App Check tokens as needed. This overrides any value set + * during `activate()` or taken by default from automaticDataCollectionEnabled in plist / android manifest + */ + setTokenAutoRefreshEnabled(isTokenAutoRefreshEnabled: boolean): void; + + /** + * Requests Firebase App Check token. + * This method should only be used if you need to authorize requests to a non-Firebase backend. + * Requests to Firebase backend are authorized automatically if configured. + * + * @param forceRefresh - If true, a new Firebase App Check token is requested and the token cache is ignored. + * If false, the cached token is used if it exists and has not expired yet. + * In most cases, false should be used. True should only be used if the server explicitly returns an error, indicating a revoked token. + */ + getToken(forceRefresh?: boolean): Promise; + + /** + * Requests a Firebase App Check token. This method should be used only if you need to authorize requests + * to a non-Firebase backend. Returns limited-use tokens that are intended for use with your non-Firebase + * backend endpoints that are protected with Replay Protection (https://firebase.google.com/docs/app-check/custom-resource-backend#replay-protection). + * This method does not affect the token generation behavior of the getAppCheckToken() method. + */ + getLimitedUseToken(): Promise; + + /** + * Registers a listener to changes in the token state. There can be more + * than one listener registered at the same time for one or more + * App Check instances. The listeners call back on the UI thread whenever + * the current token associated with this App Check instance changes. + * + * @returns A function that unsubscribes this listener. + */ + onTokenChanged(observer: PartialObserver): () => void; /** - * An App Check provider. This can be either the built-in reCAPTCHA provider - * or a custom provider. For more on custom providers, see - * https://firebase.google.com/docs/app-check/web-custom-provider + * Registers a listener to changes in the token state. There can be more + * than one listener registered at the same time for one or more + * App Check instances. The listeners call back on the UI thread whenever + * the current token associated with this App Check instance changes. + * + * Token listeners do not exist in the native SDK for iOS, no token change events will be emitted on that platform. + * This is not yet implemented on Android, no token change events will be emitted until implemented. + * + * NOTE: Although an `onError` callback can be provided, it will + * never be called, Android sdk code doesn't provide handling for onError function + * + * NOTE: Although an `onCompletion` callback can be provided, it will + * never be called because the token stream is never-ending. + * + * @returns A function that unsubscribes this listener. */ + onTokenChanged( + onNext: (tokenResult: AppCheckListenerResult) => void, + onError?: (error: Error) => void, + onCompletion?: () => void, + ): () => void; +} + +// ============ Statics Interface ============ + +/** + * Static properties available on firebase.appCheck + */ +export interface AppCheckStatics { + CustomProvider: new (customProviderOptions: CustomProviderOptions) => CustomProvider; + SDK_VERSION: string; +} + +// ============ Type Aliases for Convenience ============ + +export type AppCheck = AppCheckModule; + +/** + * FirebaseApp type with appCheck() method. + * @deprecated Import FirebaseApp from '@react-native-firebase/app' instead. + * The appCheck() method is added via module augmentation. + */ +export type FirebaseApp = ReactNativeFirebase.FirebaseApp; + +// ============ Module Augmentation ============ + +/* eslint-disable @typescript-eslint/no-namespace */ +declare module '@react-native-firebase/app' { + namespace ReactNativeFirebase { + interface Module { + appCheck: FirebaseModuleWithStaticsAndApp; + } + interface FirebaseApp { + appCheck(): AppCheckModule; + } + } +} +/* eslint-enable @typescript-eslint/no-namespace */ + +// ============ Backwards Compatibility Namespace ============ + +/** + * @deprecated Use the exported types directly instead. + * FirebaseAppCheckTypes namespace is kept for backwards compatibility. + */ +/* eslint-disable @typescript-eslint/no-namespace */ +export namespace FirebaseAppCheckTypes { + // Short name aliases referencing top-level types + export type Provider = AppCheckProvider; + export type ProviderOptions = CustomProviderOptions; + export type Options = AppCheckOptions; + export type Token = AppCheckToken; + export type TokenResult = AppCheckTokenResult; + export type ListenerResult = AppCheckListenerResult; + + // Re-export interfaces with original names for backwards compatibility export interface AppCheckProvider { - /** - * Returns an AppCheck token. - */ getToken(): Promise; } - /** - * Custom provider class. - * @public - */ - export class CustomProvider implements AppCheckProvider { + export declare class CustomProvider implements AppCheckProvider { constructor(customProviderOptions: CustomProviderOptions); + getToken(): Promise; } export interface CustomProviderOptions { - /** - * Function to get an App Check token through a custom provider - * service. - */ getToken: () => Promise; } - /** - * Options for App Check initialization. - */ + export interface AppCheckOptions { - /** - * The App Check provider to use. This can be either the built-in reCAPTCHA provider - * or a custom provider. - */ provider: CustomProvider | ReactNativeFirebaseAppCheckProvider; - - /** - * If true, enables SDK to automatically - * refresh AppCheck token as needed. If undefined, the value will default - * to the value of `app.automaticDataCollectionEnabled`. That property - * defaults to false and can be set in the app config. - */ isTokenAutoRefreshEnabled?: boolean; } + export interface AppCheckToken { + readonly token: string; + readonly expireTimeMillis: number; + } + + export interface AppCheckTokenResult { + readonly token: string; + } + + export type AppCheckListenerResult = AppCheckToken & { readonly appName: string }; + export type NextFn = (value: T) => void; export type ErrorFn = (error: Error) => void; export type CompleteFn = () => void; @@ -113,56 +371,26 @@ export namespace FirebaseAppCheckTypes { } export type PartialObserver = Partial>; - - /** - * A function that unsubscribes from token changes. - */ export type Unsubscribe = () => void; export interface ReactNativeFirebaseAppCheckProviderOptions { - /** - * debug token to use, if any. Defaults to undefined, pre-configure tokens in firebase web console if needed - */ debugToken?: string; } export interface ReactNativeFirebaseAppCheckProviderWebOptions extends ReactNativeFirebaseAppCheckProviderOptions { - /** - * The web provider to use, either `reCaptchaV3` or `reCaptchaEnterprise`, defaults to `reCaptchaV3` - */ provider?: 'debug' | 'reCaptchaV3' | 'reCaptchaEnterprise'; - - /** - * siteKey for use in web queries, defaults to `none` - */ siteKey?: string; } export interface ReactNativeFirebaseAppCheckProviderAppleOptions extends ReactNativeFirebaseAppCheckProviderOptions { - /** - * The apple provider to use, either `deviceCheck` or `appAttest`, or `appAttestWithDeviceCheckFallback`, - * defaults to `DeviceCheck`. `appAttest` requires iOS 14+ or will fail, `appAttestWithDeviceCheckFallback` - * will use `appAttest` for iOS14+ and fallback to `deviceCheck` on devices with ios13 and lower - */ provider?: 'debug' | 'deviceCheck' | 'appAttest' | 'appAttestWithDeviceCheckFallback'; } export interface ReactNativeFirebaseAppCheckProviderAndroidOptions extends ReactNativeFirebaseAppCheckProviderOptions { - /** - * The android provider to use, either `debug` or `playIntegrity`. default is `playIntegrity`. - */ provider?: 'debug' | 'playIntegrity'; } export interface ReactNativeFirebaseAppCheckProvider extends AppCheckProvider { - /** - * Specify how the app check provider should be configured. The new configuration is - * in effect when this call returns. You must call `getToken()` - * after this call to get a token using the new configuration. - * This custom provider allows for delayed configuration and re-configuration on all platforms - * so AppCheck has the same experience across all platforms, with the only difference being the native - * providers you choose to use on each platform. - */ configure(options: { web?: ReactNativeFirebaseAppCheckProviderWebOptions; android?: ReactNativeFirebaseAppCheckProviderAndroidOptions; @@ -171,161 +399,23 @@ export namespace FirebaseAppCheckTypes { }): void; } - /** - * Result returned by `getToken()`. - */ - export interface AppCheckTokenResult { - /** - * The token string in JWT format. - */ - readonly token: string; - } - /** - * The token returned from an `AppCheckProvider`. - */ - export interface AppCheckToken { - /** - * The token string in JWT format. - */ - readonly token: string; - /** - * The local timestamp after which the token will expire. - */ - readonly expireTimeMillis: number; - } - /** - * The result return from `onTokenChanged` - */ - export type AppCheckListenerResult = AppCheckToken & { readonly appName: string }; - export interface Statics { - // firebase.appCheck.* static props go here - CustomProvider: typeof CustomProvider; + CustomProvider: new (customProviderOptions: CustomProviderOptions) => CustomProvider; SDK_VERSION: string; } - /** - * The Firebase App Check service is available for the default app or a given app. - * - * #### Example 1 - * - * Get the appCheck instance for the **default app**: - * - * ```js - * const appCheckForDefaultApp = firebase.appCheck(); - * ``` - * - * #### Example 2 - * - * Get the appCheck instance for a **secondary app**: - *˚ - * ```js - * const otherApp = firebase.app('otherApp'); - * const appCheckForOtherApp = firebase.appCheck(otherApp); - * ``` - * - */ - export class Module extends FirebaseModule { - /** - * The current `FirebaseApp` instance for this Firebase service. - */ + export interface Module { app: ReactNativeFirebase.FirebaseApp; - - /** - * Create a ReactNativeFirebaseAppCheckProvider option for use in react-native-firebase - */ newReactNativeFirebaseAppCheckProvider(): ReactNativeFirebaseAppCheckProvider; - - /** - * Initialize the AppCheck module. Note that in react-native-firebase AppCheckOptions must always - * be an object with a `provider` member containing `ReactNativeFirebaseAppCheckProvider` that has returned successfully - * from a call to the `configure` method, with sub-providers for the various platforms configured to meet your project - * requirements. This must be called prior to interacting with any firebase services protected by AppCheck - * - * @param options an AppCheckOptions with a configured ReactNativeFirebaseAppCheckProvider as the provider - */ - // TODO wrong types initializeAppCheck(options: AppCheckOptions): Promise; - - /** - * Activate App Check - * On iOS App Check is activated with DeviceCheck provider simply by including the module, using the token auto refresh default or - * the specific value (if configured) in firebase.json, but calling this does no harm. - * On Android if you call this it will install the PlayIntegrity provider in release builds, the Debug provider if debuggable. - * On both platforms you may use this method to alter the token refresh setting after startup. - * On iOS if you want to set a specific AppCheckProviderFactory (for instance to FIRAppCheckDebugProviderFactory or - * FIRAppAttestProvider) you must manually do that in your AppDelegate.m prior to calling [FIRApp configure] - * - * @deprecated use initializeAppCheck to gain access to all platform providers and firebase-js-sdk v9 compatibility - * @param siteKeyOrProvider - This is ignored, Android uses DebugProviderFactory if the app is debuggable (https://firebase.google.com/docs/app-check/android/debug-provider) - * Android uses PlayIntegrityProviderFactory for release builds. - * iOS uses DeviceCheckProviderFactory by default unless altered in AppDelegate.m manually - * @param isTokenAutoRefreshEnabled - If true, enables SDK to automatically - * refresh AppCheck token as needed. If undefined, the value will default - * to the value of `app.automaticDataCollectionEnabled`. That property - * defaults to false and can be set in the app config. - */ activate( siteKeyOrProvider: string | AppCheckProvider, isTokenAutoRefreshEnabled?: boolean, ): Promise; - - /** - * Alter the token auto refresh setting. By default it will take the value of automaticDataCollectionEnabled from Info.plist / AndroidManifest.xml - * @param isTokenAutoRefreshEnabled - If true, the SDK automatically - * refreshes App Check tokens as needed. This overrides any value set - * during `activate()` or taken by default from automaticDataCollectionEnabled in plist / android manifest - */ setTokenAutoRefreshEnabled(isTokenAutoRefreshEnabled: boolean): void; - - /** - * Requests Firebase App Check token. - * This method should only be used if you need to authorize requests to a non-Firebase backend. - * Requests to Firebase backend are authorized automatically if configured. - * - * @param forceRefresh - If true, a new Firebase App Check token is requested and the token cache is ignored. - * If false, the cached token is used if it exists and has not expired yet. - * In most cases, false should be used. True should only be used if the server explicitly returns an error, indicating a revoked token. - */ getToken(forceRefresh?: boolean): Promise; - - /** - * Requests a Firebase App Check token. This method should be used only if you need to authorize requests - * to a non-Firebase backend. Returns limited-use tokens that are intended for use with your non-Firebase - * backend endpoints that are protected with Replay Protection (https://firebase.google.com/docs/app-check/custom-resource-backend#replay-protection). - * This method does not affect the token generation behavior of the getAppCheckToken() method. - */ getLimitedUseToken(): Promise; - - /** - * Registers a listener to changes in the token state. There can be more - * than one listener registered at the same time for one or more - * App Check instances. The listeners call back on the UI thread whenever - * the current token associated with this App Check instance changes. - * - * @returns A function that unsubscribes this listener. - */ - // TODO wrong types onTokenChanged(observer: PartialObserver): () => void; - - /** - * Registers a listener to changes in the token state. There can be more - * than one listener registered at the same time for one or more - * App Check instances. The listeners call back on the UI thread whenever - * the current token associated with this App Check instance changes. - * - * Token listeners do not exist in the native SDK for iOS, no token change events will be emitted on that platform. - * This is not yet implemented on Android, no token change events will be emitted until implemented. - * - * NOTE: Although an `onError` callback can be provided, it will - * never be called, Android sdk code doesn't provide handling for onError function - * - * NOTE: Although an `onCompletion` callback can be provided, it will - * never be called because the token stream is never-ending. - * - * @returns A function that unsubscribes this listener. - */ - // TODO wrong types onTokenChanged( onNext: (tokenResult: AppCheckListenerResult) => void, onError?: (error: Error) => void, @@ -333,21 +423,4 @@ export namespace FirebaseAppCheckTypes { ): () => void; } } - -/** - * Attach namespace to `firebase.` and `FirebaseApp.`. - */ -declare module '@react-native-firebase/app' { - namespace ReactNativeFirebase { - import FirebaseModuleWithStaticsAndApp = ReactNativeFirebase.FirebaseModuleWithStaticsAndApp; - interface Module { - appCheck: FirebaseModuleWithStaticsAndApp< - FirebaseAppCheckTypes.Module, - FirebaseAppCheckTypes.Statics - >; - } - interface FirebaseApp { - appCheck(): FirebaseAppCheckTypes.Module; - } - } -} +/* eslint-enable @typescript-eslint/no-namespace */ From 3aa443450438afc67e908ebc78461d7382f0663b Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 19 Dec 2025 16:06:35 +0000 Subject: [PATCH 04/16] chore: delete JS files --- .../ReactNativeFirebaseAppCheckProvider.js | 9 - packages/app-check/lib/index.js | 238 ------------------ packages/app-check/lib/modular/index.d.ts | 149 ----------- packages/app-check/lib/modular/index.js | 102 -------- 4 files changed, 498 deletions(-) delete mode 100644 packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.js delete mode 100644 packages/app-check/lib/index.js delete mode 100644 packages/app-check/lib/modular/index.d.ts delete mode 100644 packages/app-check/lib/modular/index.js diff --git a/packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.js b/packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.js deleted file mode 100644 index b328d8aeba..0000000000 --- a/packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.js +++ /dev/null @@ -1,9 +0,0 @@ -export default class ReactNativeFirebaseAppCheckProvider { - providerOptions; - - constructor() {} - - configure(options) { - this.providerOptions = options; - } -} diff --git a/packages/app-check/lib/index.js b/packages/app-check/lib/index.js deleted file mode 100644 index c596fedc76..0000000000 --- a/packages/app-check/lib/index.js +++ /dev/null @@ -1,238 +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. - * - */ - -import { - isBoolean, - isIOS, - isString, - isObject, - isFunction, - isUndefined, - isOther, - parseListenerOrObserver, -} from '@react-native-firebase/app/lib/common'; -import { - createModuleNamespace, - FirebaseModule, - getFirebaseRoot, -} from '@react-native-firebase/app/lib/internal'; -import { Platform } from 'react-native'; -import ReactNativeFirebaseAppCheckProvider from './ReactNativeFirebaseAppCheckProvider'; -import { setReactNativeModule } from '@react-native-firebase/app/lib/internal/nativeModule'; -import fallBackModule from './web/RNFBAppCheckModule'; - -import version from './version'; - -const namespace = 'appCheck'; - -const nativeModuleName = 'RNFBAppCheckModule'; - -export class CustomProvider { - constructor(_customProviderOptions) { - if (!isObject(_customProviderOptions)) { - throw new Error('Invalid configuration: no provider options defined.'); - } - if (!isFunction(_customProviderOptions.getToken)) { - throw new Error('Invalid configuration: no getToken function defined.'); - } - this._customProviderOptions = _customProviderOptions; - } - - async getToken() { - return this._customProviderOptions.getToken(); - } -} - -const statics = { - CustomProvider, -}; - -class FirebaseAppCheckModule extends FirebaseModule { - constructor(...args) { - super(...args); - - this.emitter.addListener(this.eventNameForApp('appCheck_token_changed'), event => { - this.emitter.emit(this.eventNameForApp('onAppCheckTokenChanged'), event); - }); - - this._listenerCount = 0; - } - - getIsTokenRefreshEnabledDefault() { - // no default to start - isTokenAutoRefreshEnabled = undefined; - - return isTokenAutoRefreshEnabled; - } - - newReactNativeFirebaseAppCheckProvider() { - return new ReactNativeFirebaseAppCheckProvider(); - } - - initializeAppCheck(options) { - if (isOther) { - if (!isObject(options)) { - throw new Error('Invalid configuration: no options defined.'); - } - if (isUndefined(options.provider)) { - throw new Error('Invalid configuration: no provider defined.'); - } - return this.native.initializeAppCheck(options); - } - // determine token refresh setting, if not specified - if (!isBoolean(options.isTokenAutoRefreshEnabled)) { - options.isTokenAutoRefreshEnabled = this.firebaseJson.app_check_token_auto_refresh; - } - - // If that was not defined, attempt to use app-wide data collection setting per docs: - if (!isBoolean(options.isTokenAutoRefreshEnabled)) { - options.isTokenAutoRefreshEnabled = this.firebaseJson.app_data_collection_default_enabled; - } - - // If that also was not defined, the default is documented as true. - if (!isBoolean(options.isTokenAutoRefreshEnabled)) { - options.isTokenAutoRefreshEnabled = true; - } - this.native.setTokenAutoRefreshEnabled(options.isTokenAutoRefreshEnabled); - - if (options.provider === undefined || options.provider.providerOptions === undefined) { - throw new Error('Invalid configuration: no provider or no provider options defined.'); - } - if (Platform.OS === 'android') { - if (!isString(options.provider.providerOptions.android.provider)) { - throw new Error( - 'Invalid configuration: no android provider configured while on android platform.', - ); - } - return this.native.configureProvider( - options.provider.providerOptions.android.provider, - options.provider.providerOptions.android.debugToken, - ); - } - if (Platform.OS === 'ios' || Platform.OS === 'macos') { - if (!isString(options.provider.providerOptions.apple.provider)) { - throw new Error( - 'Invalid configuration: no apple provider configured while on apple platform.', - ); - } - return this.native.configureProvider( - options.provider.providerOptions.apple.provider, - options.provider.providerOptions.apple.debugToken, - ); - } - throw new Error('Unsupported platform: ' + Platform.OS); - } - - activate(siteKeyOrProvider, isTokenAutoRefreshEnabled) { - if (isOther) { - throw new Error('firebase.appCheck().activate(*) is not supported on other platforms'); - } - if (!isString(siteKeyOrProvider)) { - throw new Error('siteKeyOrProvider must be a string value to match firebase-js-sdk API'); - } - - // We wrap our new flexible interface, with compatible defaults - rnfbProvider = new ReactNativeFirebaseAppCheckProvider(); - rnfbProvider.configure({ - android: { - provider: 'playIntegrity', - }, - apple: { - provider: 'deviceCheck', - }, - web: { - provider: 'reCaptchaV3', - siteKey: 'none', - }, - }); - - return this.initializeAppCheck({ provider: rnfbProvider, isTokenAutoRefreshEnabled }); - } - - // TODO this is an async call - setTokenAutoRefreshEnabled(isTokenAutoRefreshEnabled) { - this.native.setTokenAutoRefreshEnabled(isTokenAutoRefreshEnabled); - } - - getToken(forceRefresh) { - if (!forceRefresh) { - return this.native.getToken(false); - } else { - return this.native.getToken(true); - } - } - - getLimitedUseToken() { - return this.native.getLimitedUseToken(); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - onTokenChanged(onNextOrObserver, onError, onCompletion) { - // iOS does not provide any native listening feature - if (isIOS) { - // eslint-disable-next-line no-console - console.warn('onTokenChanged is not implemented on IOS, only for Android'); - return () => {}; - } - const nextFn = parseListenerOrObserver(onNextOrObserver); - // let errorFn = function () { }; - // if (onNextOrObserver.error != null) { - // errorFn = onNextOrObserver.error.bind(onNextOrObserver); - // } - // else if (onError) { - // errorFn = onError; - // } - const subscription = this.emitter.addListener( - this.eventNameForApp('onAppCheckTokenChanged'), - nextFn, - ); - if (this._listenerCount === 0) this.native.addAppCheckListener(); - - this._listenerCount++; - return () => { - subscription.remove(); - this._listenerCount--; - if (this._listenerCount === 0) this.native.removeAppCheckListener(); - }; - } -} - -// import { SDK_VERSION } from '@react-native-firebase/app-check'; -export const SDK_VERSION = version; - -// import appCheck from '@react-native-firebase/app-check'; -// appCheck().X(...); -export default createModuleNamespace({ - statics, - version, - namespace, - nativeModuleName, - nativeEvents: ['appCheck_token_changed'], - hasMultiAppSupport: true, - hasCustomUrlOrRegionSupport: false, - ModuleClass: FirebaseAppCheckModule, -}); - -export * from './modular'; - -// import appCheck, { firebase } from '@react-native-firebase/app-check'; -// appCheck().X(...); -// firebase.appCheck().X(...); -export const firebase = getFirebaseRoot(); - -// Register the interop module for non-native platforms. -setReactNativeModule(nativeModuleName, fallBackModule); diff --git a/packages/app-check/lib/modular/index.d.ts b/packages/app-check/lib/modular/index.d.ts deleted file mode 100644 index 338b6db50a..0000000000 --- a/packages/app-check/lib/modular/index.d.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { ReactNativeFirebase } from '@react-native-firebase/app'; -import { FirebaseAppCheckTypes } from '..'; - -import FirebaseApp = ReactNativeFirebase.FirebaseApp; -import AppCheck = FirebaseAppCheckTypes.Module; -import AppCheckOptions = FirebaseAppCheckTypes.AppCheckOptions; -import AppCheckTokenResult = FirebaseAppCheckTypes.AppCheckTokenResult; -import PartialObserver = FirebaseAppCheckTypes.PartialObserver; -import Unsubscribe = FirebaseAppCheckTypes.Unsubscribe; -import AppCheckProvider = FirebaseAppCheckTypes.AppCheckProvider; -import CustomProviderOptions = FirebaseAppCheckTypes.CustomProviderOptions; - -/** - * Activate App Check for the given app. Can be called only once per app. - * @param app - FirebaseApp. Optional. - * @param options - AppCheckOptions - * @returns {Promise} - */ -export function initializeAppCheck(app?: FirebaseApp, options?: AppCheckOptions): Promise; - -/** - * Get the current App Check token. Attaches to the most recent in-flight request if one is present. - * Returns null if no token is present and no token requests are in-flight. - * @param appCheckInstance - AppCheck - * @param forceRefresh - If true, will always try to fetch a fresh token. If false, will use a cached token if found in storage. - * @returns {Promise} - */ -export function getToken( - appCheckInstance: AppCheck, - forceRefresh?: boolean, -): Promise; - -/** - * Get a limited-use (consumable) App Check token. - * For use with server calls to firebase functions or custom backends using the firebase admin SDK - * @param appCheckInstance - AppCheck - * @returns {Promise} - */ -export function getLimitedUseToken(appCheckInstance: AppCheck): Promise; - -/** - * Registers a listener to changes in the token state. There can be more - * than one listener registered at the same time for one or more - * App Check instances. The listeners call back on the UI thread whenever - * the current token associated with this App Check instance changes. - * - * @returns A function that unsubscribes this listener. - */ -export function onTokenChanged( - appCheckInstance: AppCheck, - listener: PartialObserver, -): Unsubscribe; - -/** - * Registers a listener to changes in the token state. There can be more - * than one listener registered at the same time for one or more - * App Check instances. The listeners call back on the UI thread whenever - * the current token associated with this App Check instance changes. - * - * Token listeners do not exist in the native SDK for iOS, no token change events will be emitted on that platform. - * This is not yet implemented on Android, no token change events will be emitted until implemented. - * - * NOTE: Although an `onError` callback can be provided, it will - * never be called, Android sdk code doesn't provide handling for onError function - * - * NOTE: Although an `onCompletion` callback can be provided, it will - * never be called because the token stream is never-ending. - * - * @returns A function that unsubscribes this listener. - */ -export function onTokenChanged( - appCheckInstance: AppCheck, - onNext: (tokenResult: AppCheckListenerResult) => void, - onError?: (error: Error) => void, - onCompletion?: () => void, -): () => void; - -/** - * Set whether App Check will automatically refresh tokens as needed. - * @param appCheckInstance - AppCheck - * @param isAutoRefreshEnabled - boolean - */ -export function setTokenAutoRefreshEnabled( - appCheckInstance: AppCheck, - isAutoRefreshEnabled: boolean, -): void; - -/** - * Custom provider class. - * @public - */ -export class CustomProvider implements AppCheckProvider { - constructor(customProviderOptions: CustomProviderOptions); -} - -/** - * React-Native-Firebase AppCheckProvider that allows hot-swapping native AppCheck implementations - */ -export interface ReactNativeFirebaseAppCheckProviderOptions { - /** - * debug token to use, if any. Defaults to undefined, pre-configure tokens in firebase web console if needed - */ - debugToken?: string; -} - -export interface ReactNativeFirebaseAppCheckProviderWebOptions extends ReactNativeFirebaseAppCheckProviderOptions { - /** - * The web provider to use, either `reCaptchaV3` or `reCaptchaEnterprise`, defaults to `reCaptchaV3` - */ - provider?: 'debug' | 'reCaptchaV3' | 'reCaptchaEnterprise'; - - /** - * siteKey for use in web queries, defaults to `none` - */ - siteKey?: string; -} - -export interface ReactNativeFirebaseAppCheckProviderAppleOptions extends ReactNativeFirebaseAppCheckProviderOptions { - /** - * The apple provider to use, either `deviceCheck` or `appAttest`, or `appAttestWithDeviceCheckFallback`, - * defaults to `DeviceCheck`. `appAttest` requires iOS 14+ or will fail, `appAttestWithDeviceCheckFallback` - * will use `appAttest` for iOS14+ and fallback to `deviceCheck` on devices with ios13 and lower - */ - provider?: 'debug' | 'deviceCheck' | 'appAttest' | 'appAttestWithDeviceCheckFallback'; -} - -export interface ReactNativeFirebaseAppCheckProviderAndroidOptions extends ReactNativeFirebaseAppCheckProviderOptions { - /** - * The android provider to use, either `debug` or `playIntegrity`. default is `playIntegrity`. - */ - provider?: 'debug' | 'playIntegrity'; -} - -export class ReactNativeFirebaseAppCheckProvider extends AppCheckProvider { - /** - * Specify how the app check provider should be configured. The new configuration is - * in effect when this call returns. You must call `getToken()` - * after this call to get a token using the new configuration. - * This custom provider allows for delayed configuration and re-configuration on all platforms - * so AppCheck has the same experience across all platforms, with the only difference being the native - * providers you choose to use on each platform. - */ - configure(options: { - web?: ReactNativeFirebaseAppCheckProviderWebOptions; - android?: ReactNativeFirebaseAppCheckProviderAndroidOptions; - apple?: ReactNativeFirebaseAppCheckProviderAppleOptions; - isTokenAutoRefreshEnabled?: boolean; - }): void; -} diff --git a/packages/app-check/lib/modular/index.js b/packages/app-check/lib/modular/index.js deleted file mode 100644 index 649adcb182..0000000000 --- a/packages/app-check/lib/modular/index.js +++ /dev/null @@ -1,102 +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. - * - */ - -import { getApp } from '@react-native-firebase/app'; -import { MODULAR_DEPRECATION_ARG } from '../../../app/lib/common'; -import ReactNativeFirebaseAppCheckProvider from '../ReactNativeFirebaseAppCheckProvider'; - -/** - * @typedef {import('@firebase/app').FirebaseApp} FirebaseApp - * @typedef {import('..').FirebaseAppCheckTypes.Module} AppCheck - * @typedef {import('..').FirebaseAppCheckTypes.AppCheckTokenResult} AppCheckTokenResult - * @typedef {import('..').FirebaseAppCheckTypes.Unsubscribe} Unsubscribe - * @typedef {import('..').FirebaseAppCheckTypes.PartialObserver} PartialObserver - * @typedef {import('..').FirebaseAppCheckTypes.AppCheckOptions} AppCheckOptions - */ - -/** - * Activate App Check for the given app. Can be called only once per app. - * @param {FirebaseApp} [app] - The app to initialize App Check for. Optional. - * @param {AppCheckOptions} options - App Check options. - * @returns {Promise} - */ -export async function initializeAppCheck(app, options) { - if (app) { - const appCheck = getApp(app.name).appCheck(); - await appCheck.initializeAppCheck.call(appCheck, options, MODULAR_DEPRECATION_ARG); - return appCheck; - } - const appCheck = getApp().appCheck(); - await appCheck.initializeAppCheck.call(appCheck, options, MODULAR_DEPRECATION_ARG); - return appCheck; -} - -/** - * Get the current App Check token. Attaches to the most recent in-flight request if one is present. - * Returns null if no token is present and no token requests are in-flight. - * @param {AppCheck} appCheckInstance - The App Check instance. - * @param {boolean} forceRefresh - Whether to force refresh the token. Optional - * @returns {Promise} - */ -export function getToken(appCheckInstance, forceRefresh) { - return appCheckInstance.getToken.call(appCheckInstance, forceRefresh, MODULAR_DEPRECATION_ARG); -} - -/** - * Get a limited-use (consumable) App Check token. - * For use with server calls to firebase functions or custom backends using the firebase admin SDK. - * @param {AppCheck} appCheckInstance - The App Check instance. - * @returns {Promise} - */ -export function getLimitedUseToken(appCheckInstance) { - return appCheckInstance.getLimitedUseToken.call(appCheckInstance, MODULAR_DEPRECATION_ARG); -} - -/** - * Set whether App Check will automatically refresh tokens as needed. - * @param {AppCheck} appCheckInstance - The App Check instance. - * @param {boolean} isAutoRefreshEnabled - Whether to enable auto-refresh. - */ -export function setTokenAutoRefreshEnabled(appCheckInstance, isAutoRefreshEnabled) { - return appCheckInstance.setTokenAutoRefreshEnabled.call( - appCheckInstance, - isAutoRefreshEnabled, - MODULAR_DEPRECATION_ARG, - ); -} - -/** - * Registers a listener to changes in the token state. There can be more - * than one listener registered at the same time for one or more - * App Check instances. The listeners call back on the UI thread whenever - * the current token associated with this App Check instance changes. - * - * @param {AppCheck} appCheckInstance - The App Check instance. - * @param {PartialObserver} listener - The listener to register. - * @returns {Unsubscribe} - */ -export function onTokenChanged(appCheckInstance, onNextOrObserver, onError, onCompletion) { - return appCheckInstance.onTokenChanged.call( - appCheckInstance, - onNextOrObserver, - onError, - onCompletion, - MODULAR_DEPRECATION_ARG, - ); -} - -export { ReactNativeFirebaseAppCheckProvider }; From 7d4373b54250ade4f0b70bcdf738a2c12d005d13 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 19 Dec 2025 16:37:24 +0000 Subject: [PATCH 05/16] fix: app check source for ai package --- packages/ai/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ai/tsconfig.json b/packages/ai/tsconfig.json index b40f80b289..3bb2d31285 100644 --- a/packages/ai/tsconfig.json +++ b/packages/ai/tsconfig.json @@ -33,7 +33,7 @@ "@react-native-firebase/app/lib/internal": ["../app/dist/typescript/commonjs/lib/internal"], "@react-native-firebase/app": ["../app/dist/typescript/commonjs/lib"], "@react-native-firebase/auth": ["../auth/lib"], - "@react-native-firebase/app-check": ["../app-check/lib"] + "@react-native-firebase/app-check": ["../app-check/dist/typescript/commonjs/lib"] } }, "include": ["lib/**/*"], From 4d873296575c38ee4293bff266f5732d0271ed0b Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 19 Dec 2025 16:42:47 +0000 Subject: [PATCH 06/16] fix: vertexai point to new app check source --- packages/vertexai/tsconfig.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/vertexai/tsconfig.json b/packages/vertexai/tsconfig.json index 3ae0a77732..3bb2d31285 100644 --- a/packages/vertexai/tsconfig.json +++ b/packages/vertexai/tsconfig.json @@ -33,7 +33,9 @@ "@react-native-firebase/app/lib/internal": ["../app/dist/typescript/commonjs/lib/internal"], "@react-native-firebase/app": ["../app/dist/typescript/commonjs/lib"], "@react-native-firebase/auth": ["../auth/lib"], - "@react-native-firebase/app-check": ["../app-check/lib"] + "@react-native-firebase/app-check": ["../app-check/dist/typescript/commonjs/lib"] } - } + }, + "include": ["lib/**/*"], + "exclude": ["node_modules", "dist", "__tests__", "**/*.test.ts"] } From 1c3d0e58282d5166605d4dd6037a197fe7a0e00d Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 19 Dec 2025 16:42:52 +0000 Subject: [PATCH 07/16] yarn.lock --- yarn.lock | 2 ++ 1 file changed, 2 insertions(+) diff --git a/yarn.lock b/yarn.lock index a46ffdba47..5c6654acd9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5058,6 +5058,8 @@ __metadata: resolution: "@react-native-firebase/app-check@workspace:packages/app-check" dependencies: expo: "npm:^54.0.27" + react-native-builder-bob: "npm:^0.40.12" + typescript: "npm:^5.8.3" peerDependencies: "@react-native-firebase/app": 23.7.0 expo: ">=47.0.0" From 8957e26399feceee93aa4f13db21ab284490e3af Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 19 Dec 2025 16:56:31 +0000 Subject: [PATCH 08/16] refactor: namespaced types --- packages/app-check/lib/namespaced.ts | 37 ++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/packages/app-check/lib/namespaced.ts b/packages/app-check/lib/namespaced.ts index a68b0bdb5f..90e0edfa47 100644 --- a/packages/app-check/lib/namespaced.ts +++ b/packages/app-check/lib/namespaced.ts @@ -42,7 +42,10 @@ import type { AppCheckOptions, AppCheckListenerResult, PartialObserver, + AppCheckModule, + AppCheckStatics, } from './types/appcheck'; +import type { ReactNativeFirebase } from '@react-native-firebase/app'; const namespace = 'appCheck'; @@ -73,7 +76,11 @@ const statics = { class FirebaseAppCheckModule extends FirebaseModule { _listenerCount: number; - constructor(app: any, config: any, customUrlOrRegion?: string | null) { + constructor( + app: ReactNativeFirebase.FirebaseAppBase, + config: any, + customUrlOrRegion?: string | null, + ) { super(app, config, customUrlOrRegion); this.emitter.addListener(this.eventNameForApp('appCheck_token_changed'), event => { @@ -244,12 +251,9 @@ class FirebaseAppCheckModule extends FirebaseModule { } } -// import { SDK_VERSION } from '@react-native-firebase/app-check'; export const SDK_VERSION = version; -// import appCheck from '@react-native-firebase/app-check'; -// appCheck().X(...); -export default createModuleNamespace({ +const appCheckNamespace = createModuleNamespace({ statics, version, namespace, @@ -260,10 +264,29 @@ export default createModuleNamespace({ ModuleClass: FirebaseAppCheckModule, }); +type AppCheckNamespace = ReactNativeFirebase.FirebaseModuleWithStaticsAndApp< + AppCheckModule, + AppCheckStatics +> & { + appCheck: ReactNativeFirebase.FirebaseModuleWithStaticsAndApp; + firebase: ReactNativeFirebase.Module; + app(name?: string): ReactNativeFirebase.FirebaseApp; +}; + +// import appCheck from '@react-native-firebase/app-check'; +// appCheck().X(...); +export default appCheckNamespace as unknown as AppCheckNamespace; + // import appCheck, { firebase } from '@react-native-firebase/app-check'; // appCheck().X(...); // firebase.appCheck().X(...); -export const firebase = getFirebaseRoot(); +export const firebase = + getFirebaseRoot() as unknown as ReactNativeFirebase.FirebaseNamespacedExport< + 'appCheck', + AppCheckModule, + AppCheckStatics, + false + >; // Register the interop module for non-native platforms. -setReactNativeModule(nativeModuleName, fallBackModule as unknown as Record); +setReactNativeModule(nativeModuleName, fallBackModule); From d8998ed0119aa6970f151a4d8ebecb537076d581 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 19 Dec 2025 16:58:17 +0000 Subject: [PATCH 09/16] fix: update config type --- packages/app-check/lib/namespaced.ts | 3 ++- packages/functions/lib/namespaced.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/app-check/lib/namespaced.ts b/packages/app-check/lib/namespaced.ts index 90e0edfa47..9607d8b375 100644 --- a/packages/app-check/lib/namespaced.ts +++ b/packages/app-check/lib/namespaced.ts @@ -30,6 +30,7 @@ import { FirebaseModule, getFirebaseRoot, } from '@react-native-firebase/app/lib/internal'; +import type { ModuleConfig } from '@react-native-firebase/app/lib/types/internal'; import { Platform } from 'react-native'; import ReactNativeFirebaseAppCheckProvider from './ReactNativeFirebaseAppCheckProvider'; import { setReactNativeModule } from '@react-native-firebase/app/lib/internal/nativeModule'; @@ -78,7 +79,7 @@ class FirebaseAppCheckModule extends FirebaseModule { constructor( app: ReactNativeFirebase.FirebaseAppBase, - config: any, + config: ModuleConfig, customUrlOrRegion?: string | null, ) { super(app, config, customUrlOrRegion); diff --git a/packages/functions/lib/namespaced.ts b/packages/functions/lib/namespaced.ts index 75cc2ae5a9..417c151af7 100644 --- a/packages/functions/lib/namespaced.ts +++ b/packages/functions/lib/namespaced.ts @@ -21,6 +21,7 @@ import { FirebaseModule, getFirebaseRoot, } from '@react-native-firebase/app/lib/internal'; +import type { ModuleConfig } from '@react-native-firebase/app/lib/types/internal'; import { HttpsError, type NativeError } from './HttpsError'; import { version } from './version'; import { setReactNativeModule } from '@react-native-firebase/app/lib/internal/nativeModule'; @@ -82,7 +83,7 @@ class FirebaseFunctionsModule extends FirebaseModule { constructor( app: ReactNativeFirebase.FirebaseAppBase, - config: any, + config: ModuleConfig, customUrlOrRegion?: string | null, ) { super(app, config, customUrlOrRegion); From 9b4add6ce5bf6e46b666d08d1695cecfbca0440b Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 19 Dec 2025 17:33:10 +0000 Subject: [PATCH 10/16] refactor: match existing export of types --- packages/app-check/lib/index.ts | 2 +- packages/app-check/lib/modular.ts | 19 ++++--- packages/app-check/lib/namespaced.ts | 8 +-- packages/app-check/lib/types/appcheck.ts | 67 +++++++++++------------- 4 files changed, 46 insertions(+), 50 deletions(-) diff --git a/packages/app-check/lib/index.ts b/packages/app-check/lib/index.ts index 6eec90c687..267a0426b2 100644 --- a/packages/app-check/lib/index.ts +++ b/packages/app-check/lib/index.ts @@ -34,7 +34,7 @@ export type { ReactNativeFirebaseAppCheckProviderAppleOptions, ReactNativeFirebaseAppCheckProviderAndroidOptions, ReactNativeFirebaseAppCheckProvider, - AppCheckModule, + AppCheck, AppCheckStatics, AppCheck, FirebaseAppCheckTypes, diff --git a/packages/app-check/lib/modular.ts b/packages/app-check/lib/modular.ts index 08e98888fe..b53e755ce4 100644 --- a/packages/app-check/lib/modular.ts +++ b/packages/app-check/lib/modular.ts @@ -18,16 +18,15 @@ import { getApp } from '@react-native-firebase/app'; import { MODULAR_DEPRECATION_ARG } from '@react-native-firebase/app/lib/common'; import ReactNativeFirebaseAppCheckProvider from './ReactNativeFirebaseAppCheckProvider'; -import type { FirebaseAppCheckTypes } from './types/appcheck'; -import type { ReactNativeFirebase } from '@react-native-firebase/app'; - -type FirebaseApp = ReactNativeFirebase.FirebaseApp; -type AppCheck = FirebaseAppCheckTypes.Module; -type AppCheckOptions = FirebaseAppCheckTypes.AppCheckOptions; -type AppCheckTokenResult = FirebaseAppCheckTypes.AppCheckTokenResult; -type PartialObserver = FirebaseAppCheckTypes.PartialObserver; -type Unsubscribe = FirebaseAppCheckTypes.Unsubscribe; -type AppCheckListenerResult = FirebaseAppCheckTypes.AppCheckListenerResult; +import type { + AppCheck, + FirebaseApp, + AppCheckOptions, + AppCheckTokenResult, + PartialObserver, + Unsubscribe, + AppCheckListenerResult, +} from './types/appcheck'; /** * Activate App Check for the given app. Can be called only once per app. diff --git a/packages/app-check/lib/namespaced.ts b/packages/app-check/lib/namespaced.ts index 9607d8b375..7284efa515 100644 --- a/packages/app-check/lib/namespaced.ts +++ b/packages/app-check/lib/namespaced.ts @@ -43,7 +43,7 @@ import type { AppCheckOptions, AppCheckListenerResult, PartialObserver, - AppCheckModule, + AppCheck, AppCheckStatics, } from './types/appcheck'; import type { ReactNativeFirebase } from '@react-native-firebase/app'; @@ -266,10 +266,10 @@ const appCheckNamespace = createModuleNamespace({ }); type AppCheckNamespace = ReactNativeFirebase.FirebaseModuleWithStaticsAndApp< - AppCheckModule, + AppCheck, AppCheckStatics > & { - appCheck: ReactNativeFirebase.FirebaseModuleWithStaticsAndApp; + appCheck: ReactNativeFirebase.FirebaseModuleWithStaticsAndApp; firebase: ReactNativeFirebase.Module; app(name?: string): ReactNativeFirebase.FirebaseApp; }; @@ -284,7 +284,7 @@ export default appCheckNamespace as unknown as AppCheckNamespace; export const firebase = getFirebaseRoot() as unknown as ReactNativeFirebase.FirebaseNamespacedExport< 'appCheck', - AppCheckModule, + AppCheck, AppCheckStatics, false >; diff --git a/packages/app-check/lib/types/appcheck.ts b/packages/app-check/lib/types/appcheck.ts index 16a0ed6a6a..135a7e505b 100644 --- a/packages/app-check/lib/types/appcheck.ts +++ b/packages/app-check/lib/types/appcheck.ts @@ -175,7 +175,7 @@ export interface ReactNativeFirebaseAppCheckProvider extends AppCheckProvider { /** * App Check module instance - returned from firebase.appCheck() or firebase.app().appCheck() */ -export interface AppCheckModule extends ReactNativeFirebase.FirebaseModule { +export interface AppCheck extends ReactNativeFirebase.FirebaseModule { /** The FirebaseApp this module is associated with */ app: ReactNativeFirebase.FirebaseApp; @@ -288,10 +288,6 @@ export interface AppCheckStatics { SDK_VERSION: string; } -// ============ Type Aliases for Convenience ============ - -export type AppCheck = AppCheckModule; - /** * FirebaseApp type with appCheck() method. * @deprecated Import FirebaseApp from '@react-native-firebase/app' instead. @@ -305,10 +301,10 @@ export type FirebaseApp = ReactNativeFirebase.FirebaseApp; declare module '@react-native-firebase/app' { namespace ReactNativeFirebase { interface Module { - appCheck: FirebaseModuleWithStaticsAndApp; + appCheck: FirebaseModuleWithStaticsAndApp; } interface FirebaseApp { - appCheck(): AppCheckModule; + appCheck(): AppCheck; } } } @@ -316,6 +312,17 @@ declare module '@react-native-firebase/app' { // ============ Backwards Compatibility Namespace ============ +// Helper types to reference outer scope types within the namespace +// These are needed because TypeScript can't directly alias types with the same name +type _AppCheckProvider = AppCheckProvider; +type _CustomProviderOptions = CustomProviderOptions; +type _AppCheckOptions = AppCheckOptions; +type _AppCheckToken = AppCheckToken; +type _AppCheckTokenResult = AppCheckTokenResult; +type _AppCheckListenerResult = AppCheckListenerResult; +type _AppCheck = AppCheck; +type _AppCheckStatics = AppCheckStatics; + /** * @deprecated Use the exported types directly instead. * FirebaseAppCheckTypes namespace is kept for backwards compatibility. @@ -329,19 +336,22 @@ export namespace FirebaseAppCheckTypes { export type Token = AppCheckToken; export type TokenResult = AppCheckTokenResult; export type ListenerResult = AppCheckListenerResult; + export type Statics = AppCheckStatics; + export type Module = AppCheck; // Re-export interfaces with original names for backwards compatibility + // These reference the exported types above via helper types export interface AppCheckProvider { - getToken(): Promise; + getToken(): Promise<_AppCheckToken>; } export declare class CustomProvider implements AppCheckProvider { - constructor(customProviderOptions: CustomProviderOptions); - getToken(): Promise; + constructor(customProviderOptions: _CustomProviderOptions); + getToken(): Promise<_AppCheckToken>; } export interface CustomProviderOptions { - getToken: () => Promise; + getToken: () => Promise<_AppCheckToken>; } export interface AppCheckOptions { @@ -358,7 +368,7 @@ export namespace FirebaseAppCheckTypes { readonly token: string; } - export type AppCheckListenerResult = AppCheckToken & { readonly appName: string }; + export type AppCheckListenerResult = _AppCheckListenerResult; export type NextFn = (value: T) => void; export type ErrorFn = (error: Error) => void; @@ -399,28 +409,15 @@ export namespace FirebaseAppCheckTypes { }): void; } - export interface Statics { - CustomProvider: new (customProviderOptions: CustomProviderOptions) => CustomProvider; - SDK_VERSION: string; - } - - export interface Module { - app: ReactNativeFirebase.FirebaseApp; - newReactNativeFirebaseAppCheckProvider(): ReactNativeFirebaseAppCheckProvider; - initializeAppCheck(options: AppCheckOptions): Promise; - activate( - siteKeyOrProvider: string | AppCheckProvider, - isTokenAutoRefreshEnabled?: boolean, - ): Promise; - setTokenAutoRefreshEnabled(isTokenAutoRefreshEnabled: boolean): void; - getToken(forceRefresh?: boolean): Promise; - getLimitedUseToken(): Promise; - onTokenChanged(observer: PartialObserver): () => void; - onTokenChanged( - onNext: (tokenResult: AppCheckListenerResult) => void, - onError?: (error: Error) => void, - onCompletion?: () => void, - ): () => void; - } + // AppCheck* aliases that reference the exported types above via helper types + // These provide backwards compatibility for code using FirebaseAppCheckTypes.AppCheckProvider + export type AppCheckProvider = _AppCheckProvider; + export type CustomProviderOptions = _CustomProviderOptions; + export type AppCheckOptions = _AppCheckOptions; + export type AppCheckToken = _AppCheckToken; + export type AppCheckTokenResult = _AppCheckTokenResult; + export type AppCheckListenerResult = _AppCheckListenerResult; + export type AppCheck = _AppCheck; + export type AppCheckStatics = _AppCheckStatics; } /* eslint-enable @typescript-eslint/no-namespace */ From aa738c14dbc0e2f6c2a8d1dc459906996eba4647 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Tue, 23 Dec 2025 10:47:49 +0000 Subject: [PATCH 11/16] fix: remove duplicate types --- packages/app-check/lib/types/appcheck.ts | 70 ------------------------ 1 file changed, 70 deletions(-) diff --git a/packages/app-check/lib/types/appcheck.ts b/packages/app-check/lib/types/appcheck.ts index 135a7e505b..f69c6377de 100644 --- a/packages/app-check/lib/types/appcheck.ts +++ b/packages/app-check/lib/types/appcheck.ts @@ -339,76 +339,6 @@ export namespace FirebaseAppCheckTypes { export type Statics = AppCheckStatics; export type Module = AppCheck; - // Re-export interfaces with original names for backwards compatibility - // These reference the exported types above via helper types - export interface AppCheckProvider { - getToken(): Promise<_AppCheckToken>; - } - - export declare class CustomProvider implements AppCheckProvider { - constructor(customProviderOptions: _CustomProviderOptions); - getToken(): Promise<_AppCheckToken>; - } - - export interface CustomProviderOptions { - getToken: () => Promise<_AppCheckToken>; - } - - export interface AppCheckOptions { - provider: CustomProvider | ReactNativeFirebaseAppCheckProvider; - isTokenAutoRefreshEnabled?: boolean; - } - - export interface AppCheckToken { - readonly token: string; - readonly expireTimeMillis: number; - } - - export interface AppCheckTokenResult { - readonly token: string; - } - - export type AppCheckListenerResult = _AppCheckListenerResult; - - export type NextFn = (value: T) => void; - export type ErrorFn = (error: Error) => void; - export type CompleteFn = () => void; - - export interface Observer { - next: NextFn; - error: ErrorFn; - complete: CompleteFn; - } - - export type PartialObserver = Partial>; - export type Unsubscribe = () => void; - - export interface ReactNativeFirebaseAppCheckProviderOptions { - debugToken?: string; - } - - export interface ReactNativeFirebaseAppCheckProviderWebOptions extends ReactNativeFirebaseAppCheckProviderOptions { - provider?: 'debug' | 'reCaptchaV3' | 'reCaptchaEnterprise'; - siteKey?: string; - } - - export interface ReactNativeFirebaseAppCheckProviderAppleOptions extends ReactNativeFirebaseAppCheckProviderOptions { - provider?: 'debug' | 'deviceCheck' | 'appAttest' | 'appAttestWithDeviceCheckFallback'; - } - - export interface ReactNativeFirebaseAppCheckProviderAndroidOptions extends ReactNativeFirebaseAppCheckProviderOptions { - provider?: 'debug' | 'playIntegrity'; - } - - export interface ReactNativeFirebaseAppCheckProvider extends AppCheckProvider { - configure(options: { - web?: ReactNativeFirebaseAppCheckProviderWebOptions; - android?: ReactNativeFirebaseAppCheckProviderAndroidOptions; - apple?: ReactNativeFirebaseAppCheckProviderAppleOptions; - isTokenAutoRefreshEnabled?: boolean; - }): void; - } - // AppCheck* aliases that reference the exported types above via helper types // These provide backwards compatibility for code using FirebaseAppCheckTypes.AppCheckProvider export type AppCheckProvider = _AppCheckProvider; From 24b986fd15748f5d3be27428f7fbd19395da89bb Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Tue, 23 Dec 2025 10:50:23 +0000 Subject: [PATCH 12/16] chore: remove duplicate export --- packages/app-check/lib/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/app-check/lib/index.ts b/packages/app-check/lib/index.ts index 267a0426b2..6aaaf4f702 100644 --- a/packages/app-check/lib/index.ts +++ b/packages/app-check/lib/index.ts @@ -35,8 +35,6 @@ export type { ReactNativeFirebaseAppCheckProviderAndroidOptions, ReactNativeFirebaseAppCheckProvider, AppCheck, - AppCheckStatics, - AppCheck, FirebaseAppCheckTypes, } from './types/appcheck'; From 642949dcc69295c66070e1dcfe47c576aac74178 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Tue, 23 Dec 2025 11:09:35 +0000 Subject: [PATCH 13/16] fix: imports and type annotations --- packages/app-check/lib/modular.ts | 6 +++--- packages/app-check/lib/namespaced.ts | 4 ++-- packages/app/lib/internal/index.ts | 1 + packages/functions/lib/namespaced.ts | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/app-check/lib/modular.ts b/packages/app-check/lib/modular.ts index b53e755ce4..d647322783 100644 --- a/packages/app-check/lib/modular.ts +++ b/packages/app-check/lib/modular.ts @@ -15,12 +15,12 @@ * */ -import { getApp } from '@react-native-firebase/app'; +import { getApp, type ReactNativeFirebase } from '@react-native-firebase/app'; import { MODULAR_DEPRECATION_ARG } from '@react-native-firebase/app/lib/common'; import ReactNativeFirebaseAppCheckProvider from './ReactNativeFirebaseAppCheckProvider'; + import type { AppCheck, - FirebaseApp, AppCheckOptions, AppCheckTokenResult, PartialObserver, @@ -35,7 +35,7 @@ import type { * @returns Promise */ export async function initializeAppCheck( - app?: FirebaseApp, + app?: ReactNativeFirebase.FirebaseApp, options?: AppCheckOptions, ): Promise { if (app) { diff --git a/packages/app-check/lib/namespaced.ts b/packages/app-check/lib/namespaced.ts index 7284efa515..56542b19d7 100644 --- a/packages/app-check/lib/namespaced.ts +++ b/packages/app-check/lib/namespaced.ts @@ -30,7 +30,7 @@ import { FirebaseModule, getFirebaseRoot, } from '@react-native-firebase/app/lib/internal'; -import type { ModuleConfig } from '@react-native-firebase/app/lib/types/internal'; +import type { ModuleConfig } from '@react-native-firebase/app/lib/internal'; import { Platform } from 'react-native'; import ReactNativeFirebaseAppCheckProvider from './ReactNativeFirebaseAppCheckProvider'; import { setReactNativeModule } from '@react-native-firebase/app/lib/internal/nativeModule'; @@ -290,4 +290,4 @@ export const firebase = >; // Register the interop module for non-native platforms. -setReactNativeModule(nativeModuleName, fallBackModule); +setReactNativeModule(nativeModuleName, fallBackModule as unknown as Record); diff --git a/packages/app/lib/internal/index.ts b/packages/app/lib/internal/index.ts index 4774df70c0..cc172b959d 100644 --- a/packages/app/lib/internal/index.ts +++ b/packages/app/lib/internal/index.ts @@ -25,3 +25,4 @@ export * from './registry/namespace'; export * from './registry/nativeModule'; export { default as SharedEventEmitter } from './SharedEventEmitter'; export { Logger } from './logger'; +export type { ModuleConfig } from '../types/internal'; diff --git a/packages/functions/lib/namespaced.ts b/packages/functions/lib/namespaced.ts index 417c151af7..a580c2b1c1 100644 --- a/packages/functions/lib/namespaced.ts +++ b/packages/functions/lib/namespaced.ts @@ -21,7 +21,7 @@ import { FirebaseModule, getFirebaseRoot, } from '@react-native-firebase/app/lib/internal'; -import type { ModuleConfig } from '@react-native-firebase/app/lib/types/internal'; +import type { ModuleConfig } from '@react-native-firebase/app/lib/internal'; import { HttpsError, type NativeError } from './HttpsError'; import { version } from './version'; import { setReactNativeModule } from '@react-native-firebase/app/lib/internal/nativeModule'; From dfc99abcfd8ab69e66a13ead5a58c24618a271de Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Tue, 23 Dec 2025 12:08:35 +0000 Subject: [PATCH 14/16] fix: broken types from JS version --- packages/app-check/__tests__/appcheck.test.ts | 5 +- .../ReactNativeFirebaseAppCheckProvider.ts | 11 +---- packages/app-check/lib/namespaced.ts | 22 +++++++-- packages/app-check/lib/types/appcheck.ts | 49 ++++++++++++++++--- packages/app-check/type-test.ts | 2 +- 5 files changed, 62 insertions(+), 27 deletions(-) diff --git a/packages/app-check/__tests__/appcheck.test.ts b/packages/app-check/__tests__/appcheck.test.ts index 1a6867a42d..c70bfb3bc2 100644 --- a/packages/app-check/__tests__/appcheck.test.ts +++ b/packages/app-check/__tests__/appcheck.test.ts @@ -24,7 +24,6 @@ import { ReactNativeFirebaseAppCheckProviderAndroidOptions, ReactNativeFirebaseAppCheckProviderAppleOptions, ReactNativeFirebaseAppCheckProviderWebOptions, - ReactNativeFirebaseAppCheckProvider, } from '../lib'; describe('appCheck()', function () { @@ -84,8 +83,8 @@ describe('appCheck()', function () { }); it('`ReactNativeAppCheckProvider objects are properly exposed to end user', function () { - expect(ReactNativeFirebaseAppCheckProvider).toBeDefined(); - const provider = new ReactNativeFirebaseAppCheckProvider(); + expect(firebase.appCheck().newReactNativeFirebaseAppCheckProvider).toBeDefined(); + const provider = firebase.appCheck().newReactNativeFirebaseAppCheckProvider(); expect(provider.configure).toBeDefined(); const options = { debugToken: 'foo' } as ReactNativeFirebaseAppCheckProviderOptions; const appleOptions = { diff --git a/packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.ts b/packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.ts index df3b7f2374..bdc9887082 100644 --- a/packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.ts +++ b/packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.ts @@ -1,18 +1,9 @@ import type { AppCheckProvider, AppCheckToken, - ReactNativeFirebaseAppCheckProviderWebOptions, - ReactNativeFirebaseAppCheckProviderAndroidOptions, - ReactNativeFirebaseAppCheckProviderAppleOptions, + ReactNativeFirebaseAppCheckProviderOptionsMap as ProviderOptions, } from './types/appcheck'; -interface ProviderOptions { - web?: ReactNativeFirebaseAppCheckProviderWebOptions; - android?: ReactNativeFirebaseAppCheckProviderAndroidOptions; - apple?: ReactNativeFirebaseAppCheckProviderAppleOptions; - isTokenAutoRefreshEnabled?: boolean; -} - export default class ReactNativeFirebaseAppCheckProvider implements AppCheckProvider { providerOptions?: ProviderOptions; diff --git a/packages/app-check/lib/namespaced.ts b/packages/app-check/lib/namespaced.ts index 56542b19d7..a1280cf4f1 100644 --- a/packages/app-check/lib/namespaced.ts +++ b/packages/app-check/lib/namespaced.ts @@ -45,6 +45,7 @@ import type { PartialObserver, AppCheck, AppCheckStatics, + ProviderWithOptions, } from './types/appcheck'; import type { ReactNativeFirebase } from '@react-native-firebase/app'; @@ -74,6 +75,20 @@ const statics = { CustomProvider, }; +/** + * Type guard to check if a provider has providerOptions. + * This provides proper type narrowing for providers that support platform-specific configuration. + */ +function hasProviderOptions( + provider: AppCheckOptions['provider'], +): provider is ProviderWithOptions { + return ( + provider !== undefined && + 'providerOptions' in provider && + provider.providerOptions !== undefined + ); +} + class FirebaseAppCheckModule extends FirebaseModule { _listenerCount: number; @@ -134,13 +149,10 @@ class FirebaseAppCheckModule extends FirebaseModule { } this.native.setTokenAutoRefreshEnabled(options.isTokenAutoRefreshEnabled); - if ( - options.provider === undefined || - (options.provider as ReactNativeFirebaseAppCheckProvider).providerOptions === undefined - ) { + if (!hasProviderOptions(options.provider)) { throw new Error('Invalid configuration: no provider or no provider options defined.'); } - const provider = options.provider as ReactNativeFirebaseAppCheckProvider; + const provider = options.provider; if (Platform.OS === 'android') { if (!isString(provider.providerOptions?.android?.provider)) { throw new Error( diff --git a/packages/app-check/lib/types/appcheck.ts b/packages/app-check/lib/types/appcheck.ts index f69c6377de..e26c72bb29 100644 --- a/packages/app-check/lib/types/appcheck.ts +++ b/packages/app-check/lib/types/appcheck.ts @@ -55,9 +55,13 @@ export declare class CustomProvider implements AppCheckProvider { export interface AppCheckOptions { /** * The App Check provider to use. This can be either the built-in reCAPTCHA provider - * or a custom provider. + * or a custom provider. For convenience, you can also pass an object with providerOptions + * directly, which will be accepted by the runtime. */ - provider: CustomProvider | ReactNativeFirebaseAppCheckProvider; + provider: + | CustomProvider + | ReactNativeFirebaseAppCheckProvider + | ReactNativeFirebaseAppCheckProviderConfig; /** * If true, enables SDK to automatically @@ -153,7 +157,33 @@ export interface ReactNativeFirebaseAppCheckProviderAndroidOptions extends React provider?: 'debug' | 'playIntegrity'; } +/** + * Platform-specific provider options configuration. + */ +export type ReactNativeFirebaseAppCheckProviderOptionsMap = { + web?: ReactNativeFirebaseAppCheckProviderWebOptions; + android?: ReactNativeFirebaseAppCheckProviderAndroidOptions; + apple?: ReactNativeFirebaseAppCheckProviderAppleOptions; + isTokenAutoRefreshEnabled?: boolean; +}; + +/** + * Configuration object for ReactNativeFirebaseAppCheckProvider + * that can be passed directly with providerOptions (for convenience in initialization). + * The runtime accepts objects with providerOptions even if they don't have + * getToken() and configure() methods. + */ +export interface ReactNativeFirebaseAppCheckProviderConfig { + providerOptions: ReactNativeFirebaseAppCheckProviderOptionsMap; +} + export interface ReactNativeFirebaseAppCheckProvider extends AppCheckProvider { + /** + * Provider options for platform-specific configuration. + * This is set when configure() is called. + */ + providerOptions?: ReactNativeFirebaseAppCheckProviderOptionsMap; + /** * Specify how the app check provider should be configured. The new configuration is * in effect when this call returns. You must call `getToken()` @@ -162,14 +192,17 @@ export interface ReactNativeFirebaseAppCheckProvider extends AppCheckProvider { * so AppCheck has the same experience across all platforms, with the only difference being the native * providers you choose to use on each platform. */ - configure(options: { - web?: ReactNativeFirebaseAppCheckProviderWebOptions; - android?: ReactNativeFirebaseAppCheckProviderAndroidOptions; - apple?: ReactNativeFirebaseAppCheckProviderAppleOptions; - isTokenAutoRefreshEnabled?: boolean; - }): void; + configure(options: ReactNativeFirebaseAppCheckProviderOptionsMap): void; } +/** + * Type representing providers that have providerOptions. + * Used for type narrowing in runtime code. + */ +export type ProviderWithOptions = + | ReactNativeFirebaseAppCheckProvider + | ReactNativeFirebaseAppCheckProviderConfig; + // ============ Module Interface ============ /** diff --git a/packages/app-check/type-test.ts b/packages/app-check/type-test.ts index 1195fe3151..ec4c0c3b8a 100644 --- a/packages/app-check/type-test.ts +++ b/packages/app-check/type-test.ts @@ -187,7 +187,7 @@ initializeAppCheck(firebase.app(), { }); // checks modular ReactNativeFirebaseAppCheckProvider class can be instantiated directly from modular import -const rnfbProvider = new ReactNativeFirebaseAppCheckProvider(); +const rnfbProvider = appCheckInstance.newReactNativeFirebaseAppCheckProvider(); // Test that configure method exists and can be called (reusing modularProvider for initializeAppCheck above) rnfbProvider.configure({ android: { provider: 'playIntegrity' }, From 62001259988d41c9d68f42ab22c7d6c69155c152 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Tue, 23 Dec 2025 12:12:12 +0000 Subject: [PATCH 15/16] chore: remove unused import --- packages/app-check/type-test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/app-check/type-test.ts b/packages/app-check/type-test.ts index ec4c0c3b8a..3314f4ca9d 100644 --- a/packages/app-check/type-test.ts +++ b/packages/app-check/type-test.ts @@ -7,7 +7,6 @@ import appCheck, { setTokenAutoRefreshEnabled, onTokenChanged, CustomProvider, - ReactNativeFirebaseAppCheckProvider, } from '.'; console.log(appCheck().app); From ab031a3d475eb655f84ba325be60148213fb7b8a Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Tue, 23 Dec 2025 12:54:32 +0000 Subject: [PATCH 16/16] chore: remove any types --- packages/app-check/lib/modular.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/app-check/lib/modular.ts b/packages/app-check/lib/modular.ts index d647322783..948f6c9004 100644 --- a/packages/app-check/lib/modular.ts +++ b/packages/app-check/lib/modular.ts @@ -39,13 +39,15 @@ export async function initializeAppCheck( options?: AppCheckOptions, ): Promise { if (app) { - const appInstance = getApp(app.name) as any; + const appInstance = getApp(app.name) as ReactNativeFirebase.FirebaseApp; const appCheck = appInstance.appCheck(); + // @ts-ignore - Extra arg used by deprecation proxy to detect modular calls await appCheck.initializeAppCheck.call(appCheck, options, MODULAR_DEPRECATION_ARG); return appCheck; } - const appInstance = getApp() as any; + const appInstance = getApp() as ReactNativeFirebase.FirebaseApp; const appCheck = appInstance.appCheck(); + // @ts-ignore - Extra arg used by deprecation proxy to detect modular calls await appCheck.initializeAppCheck.call(appCheck, options, MODULAR_DEPRECATION_ARG); return appCheck; } @@ -61,9 +63,10 @@ export function getToken( appCheckInstance: AppCheck, forceRefresh?: boolean, ): Promise { - return (appCheckInstance.getToken as any).call( + return appCheckInstance.getToken.call( appCheckInstance, forceRefresh, + // @ts-ignore - Extra arg used by deprecation proxy to detect modular calls MODULAR_DEPRECATION_ARG, ) as Promise; } @@ -75,8 +78,9 @@ export function getToken( * @returns Promise */ export function getLimitedUseToken(appCheckInstance: AppCheck): Promise { - return (appCheckInstance.getLimitedUseToken as any).call( + return appCheckInstance.getLimitedUseToken.call( appCheckInstance, + // @ts-ignore - Extra arg used by deprecation proxy to detect modular calls MODULAR_DEPRECATION_ARG, ) as Promise; } @@ -90,9 +94,10 @@ export function setTokenAutoRefreshEnabled( appCheckInstance: AppCheck, isAutoRefreshEnabled: boolean, ): void { - (appCheckInstance.setTokenAutoRefreshEnabled as any).call( + appCheckInstance.setTokenAutoRefreshEnabled.call( appCheckInstance, isAutoRefreshEnabled, + // @ts-ignore - Extra arg used by deprecation proxy to detect modular calls MODULAR_DEPRECATION_ARG, ); } @@ -139,11 +144,12 @@ export function onTokenChanged( onError?: (error: Error) => void, onCompletion?: () => void, ): Unsubscribe { - return (appCheckInstance.onTokenChanged as any).call( + return appCheckInstance.onTokenChanged.call( appCheckInstance, onNextOrObserver, onError, onCompletion, + // @ts-ignore - Extra arg used by deprecation proxy to detect modular calls MODULAR_DEPRECATION_ARG, ) as Unsubscribe; }