From 5ba31ff0d2263fd61c0ec38e4b65cba00a6ec829 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 5 Dec 2025 16:01:50 +0000 Subject: [PATCH] refactor(appcheck): move to Typescript implementation --- packages/app-check/index.d.ts | 1 + .../ReactNativeFirebaseAppCheckProvider.js | 9 - .../ReactNativeFirebaseAppCheckProvider.ts | 78 ++++ packages/app-check/lib/index.d.ts | 361 ------------------ packages/app-check/lib/index.ts | 23 ++ packages/app-check/lib/modular.ts | 158 ++++++++ packages/app-check/lib/modular/index.d.ts | 152 -------- packages/app-check/lib/modular/index.js | 102 ----- .../app-check/lib/{index.js => namespaced.ts} | 110 ++++-- packages/app-check/lib/types.d.ts | 57 +++ packages/app-check/lib/types/appcheck.ts | 105 +++++ .../lib/web/RNFBAppCheckModule.android.js | 2 - .../lib/web/RNFBAppCheckModule.android.ts | 19 + .../lib/web/RNFBAppCheckModule.ios.js | 2 - .../lib/web/RNFBAppCheckModule.ios.ts | 19 + ...ppCheckModule.js => RNFBAppCheckModule.ts} | 56 ++- packages/app-check/package.json | 65 +++- packages/app-check/tsconfig.json | 32 ++ 18 files changed, 664 insertions(+), 687 deletions(-) create mode 100644 packages/app-check/index.d.ts delete mode 100644 packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.js create mode 100644 packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.ts delete mode 100644 packages/app-check/lib/index.d.ts create mode 100644 packages/app-check/lib/index.ts create mode 100644 packages/app-check/lib/modular.ts delete mode 100644 packages/app-check/lib/modular/index.d.ts delete mode 100644 packages/app-check/lib/modular/index.js rename packages/app-check/lib/{index.js => namespaced.ts} (66%) create mode 100644 packages/app-check/lib/types.d.ts create mode 100644 packages/app-check/lib/types/appcheck.ts delete mode 100644 packages/app-check/lib/web/RNFBAppCheckModule.android.js create mode 100644 packages/app-check/lib/web/RNFBAppCheckModule.android.ts delete mode 100644 packages/app-check/lib/web/RNFBAppCheckModule.ios.js create mode 100644 packages/app-check/lib/web/RNFBAppCheckModule.ios.ts rename packages/app-check/lib/web/{RNFBAppCheckModule.js => RNFBAppCheckModule.ts} (56%) create mode 100644 packages/app-check/tsconfig.json diff --git a/packages/app-check/index.d.ts b/packages/app-check/index.d.ts new file mode 100644 index 0000000000..11aece60c4 --- /dev/null +++ b/packages/app-check/index.d.ts @@ -0,0 +1 @@ +export * from './lib/index'; 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/ReactNativeFirebaseAppCheckProvider.ts b/packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.ts new file mode 100644 index 0000000000..fbd7c66d6d --- /dev/null +++ b/packages/app-check/lib/ReactNativeFirebaseAppCheckProvider.ts @@ -0,0 +1,78 @@ +/* + * 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 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 ReactNativeFirebaseAppCheckProviderConfigOptions { + web?: ReactNativeFirebaseAppCheckProviderWebOptions; + android?: ReactNativeFirebaseAppCheckProviderAndroidOptions; + apple?: ReactNativeFirebaseAppCheckProviderAppleOptions; + isTokenAutoRefreshEnabled?: boolean; +} + +export default class ReactNativeFirebaseAppCheckProvider { + providerOptions?: ReactNativeFirebaseAppCheckProviderConfigOptions; + + constructor() {} + + configure(options: ReactNativeFirebaseAppCheckProviderConfigOptions): void { + this.providerOptions = options; + } + + // Required by AppCheckProvider interface, but implementation is delegated to native modules + async getToken(): Promise { + throw new Error( + 'getToken should not be called directly on ReactNativeFirebaseAppCheckProvider', + ); + } +} diff --git a/packages/app-check/lib/index.d.ts b/packages/app-check/lib/index.d.ts deleted file mode 100644 index 508f41a7fa..0000000000 --- a/packages/app-check/lib/index.d.ts +++ /dev/null @@ -1,361 +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 { ReactNativeFirebase } from '@react-native-firebase/app'; - -/** - * 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 - */ -export namespace FirebaseAppCheckTypes { - import FirebaseModule = ReactNativeFirebase.FirebaseModule; - - /** - * 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 interface AppCheckProvider { - /** - * Returns an AppCheck token. - */ - getToken(): Promise; - } - - /** - * Custom provider class. - * @public - */ - export class CustomProvider implements AppCheckProvider { - constructor(customProviderOptions: CustomProviderOptions); - } - - 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; - - /** - * 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 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 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; - } - - /** - * Result returned by `getToken()`. - */ - 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; - } - - /** - * 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 { - /** - * 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, - onCompletion?: () => void, - ): () => void; - } -} - -declare const defaultExport: ReactNativeFirebase.FirebaseModuleWithStaticsAndApp< - FirebaseAppCheckTypes.Module, - FirebaseAppCheckTypes.Statics ->; - -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.`. - */ -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; - } - } -} diff --git a/packages/app-check/lib/index.ts b/packages/app-check/lib/index.ts new file mode 100644 index 0000000000..435c6e18ed --- /dev/null +++ b/packages/app-check/lib/index.ts @@ -0,0 +1,23 @@ +/* + * 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 defaultExport from './namespaced'; + +export * from './modular'; +export * from './namespaced'; + +export default defaultExport; diff --git a/packages/app-check/lib/modular.ts b/packages/app-check/lib/modular.ts new file mode 100644 index 0000000000..c007ccb943 --- /dev/null +++ b/packages/app-check/lib/modular.ts @@ -0,0 +1,158 @@ +/* + * 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 type { ReactNativeFirebase } from '@react-native-firebase/app'; +import ReactNativeFirebaseAppCheckProvider, { + type ReactNativeFirebaseAppCheckProviderOptions, + type ReactNativeFirebaseAppCheckProviderWebOptions, + type ReactNativeFirebaseAppCheckProviderAppleOptions, + type ReactNativeFirebaseAppCheckProviderAndroidOptions, +} from './ReactNativeFirebaseAppCheckProvider'; +import type { + AppCheckProvider, + CustomProviderOptions, + AppCheckOptions, + AppCheckTokenResult, + AppCheckListenerResult, + PartialObserver, +} from './types/appcheck'; +import type { FirebaseAppCheckModule } from './namespaced'; + +declare module '@react-native-firebase/app' { + function getApp( + name?: string, + ): ReactNativeFirebase.FirebaseApp & { appCheck(): FirebaseAppCheckModule }; +} + +export { CustomProvider } from './namespaced'; +export { + ReactNativeFirebaseAppCheckProvider, + type ReactNativeFirebaseAppCheckProviderOptions, + type ReactNativeFirebaseAppCheckProviderWebOptions, + type ReactNativeFirebaseAppCheckProviderAppleOptions, + type ReactNativeFirebaseAppCheckProviderAndroidOptions, +}; + +export type { + AppCheckProvider, + CustomProviderOptions, + AppCheckOptions, + AppCheckTokenResult, + AppCheckListenerResult, + PartialObserver, +}; + +/** + * 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?: ReactNativeFirebase.FirebaseApp, + options?: AppCheckOptions, +): Promise { + if (app) { + const appCheck = getApp(app.name).appCheck(); + if (options) { + await appCheck.initializeAppCheck.call(appCheck, options, MODULAR_DEPRECATION_ARG); + } + return appCheck; + } + const appCheck = getApp().appCheck(); + if (options) { + 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: FirebaseAppCheckModule, + forceRefresh?: boolean, +): Promise { + // @ts-ignore - MODULAR_DEPRECATION_ARG is filtered out internally + 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 appCheckInstance - The App Check instance. + * @returns {Promise} + */ +export function getLimitedUseToken( + appCheckInstance: FirebaseAppCheckModule, +): Promise { + // @ts-ignore - MODULAR_DEPRECATION_ARG is filtered out internally + return appCheckInstance.getLimitedUseToken.call(appCheckInstance, MODULAR_DEPRECATION_ARG); +} + +/** + * 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: FirebaseAppCheckModule, + isAutoRefreshEnabled: boolean, +): void { + // @ts-ignore - MODULAR_DEPRECATION_ARG is filtered out internally + 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 appCheckInstance - The App Check instance. + * @param onNextOrObserver - The listener to register. + * @param onError - Optional error callback. + * @param onCompletion - Optional completion callback. + * @returns {() => void} + */ +export function onTokenChanged( + appCheckInstance: FirebaseAppCheckModule, + onNextOrObserver: + | PartialObserver + | ((tokenResult: AppCheckListenerResult) => void), + onError?: (error: Error) => void, + onCompletion?: () => void, +): () => void { + // Cast to any to handle overload resolution - runtime behavior is correct + return (appCheckInstance.onTokenChanged as any).call( + appCheckInstance, + onNextOrObserver, + onError, + onCompletion, + MODULAR_DEPRECATION_ARG, + ); +} 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 a80fbe4442..0000000000 --- a/packages/app-check/lib/modular/index.d.ts +++ /dev/null @@ -1,152 +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 }; diff --git a/packages/app-check/lib/index.js b/packages/app-check/lib/namespaced.ts similarity index 66% rename from packages/app-check/lib/index.js rename to packages/app-check/lib/namespaced.ts index c596fedc76..44b3e18502 100644 --- a/packages/app-check/lib/index.js +++ b/packages/app-check/lib/namespaced.ts @@ -34,15 +34,26 @@ 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 type { + AppCheckProvider, + CustomProviderOptions, + AppCheckOptions, + AppCheckTokenResult, + AppCheckListenerResult, + PartialObserver, + Statics, +} from './types/appcheck'; -import version from './version'; +import { version } from './version'; const namespace = 'appCheck'; const nativeModuleName = 'RNFBAppCheckModule'; -export class CustomProvider { - constructor(_customProviderOptions) { +export class CustomProvider implements AppCheckProvider { + _customProviderOptions: CustomProviderOptions; + + constructor(_customProviderOptions: CustomProviderOptions) { if (!isObject(_customProviderOptions)) { throw new Error('Invalid configuration: no provider options defined.'); } @@ -57,33 +68,53 @@ export class CustomProvider { } } -const statics = { - CustomProvider, -}; +const statics: Statics = {}; + +export interface FirebaseAppCheckModule extends FirebaseModule { + getIsTokenRefreshEnabledDefault(): boolean | undefined; + newReactNativeFirebaseAppCheckProvider(): ReactNativeFirebaseAppCheckProvider; + initializeAppCheck(options: AppCheckOptions, ...args: any[]): Promise; + activate( + siteKeyOrProvider: string | AppCheckProvider, + isTokenAutoRefreshEnabled?: boolean, + ): Promise; + setTokenAutoRefreshEnabled(isTokenAutoRefreshEnabled: boolean, ...args: any[]): void; + getToken(forceRefresh?: boolean, ...args: any[]): Promise; + getLimitedUseToken(...args: any[]): Promise; + onTokenChanged(observer: PartialObserver, ...args: any[]): () => void; + onTokenChanged( + onNext: (tokenResult: AppCheckListenerResult) => void, + onError?: (error: Error) => void, + onCompletion?: () => void, + ...args: any[] + ): () => void; +} + +class FirebaseAppCheckModuleImpl extends FirebaseModule implements FirebaseAppCheckModule { + _listenerCount: number; -class FirebaseAppCheckModule extends FirebaseModule { - constructor(...args) { + constructor(...args: any[]) { super(...args); - this.emitter.addListener(this.eventNameForApp('appCheck_token_changed'), event => { + this.emitter.addListener(this.eventNameForApp('appCheck_token_changed'), (event: any) => { this.emitter.emit(this.eventNameForApp('onAppCheckTokenChanged'), event); }); this._listenerCount = 0; } - getIsTokenRefreshEnabledDefault() { + getIsTokenRefreshEnabledDefault(): boolean | undefined { // no default to start - isTokenAutoRefreshEnabled = undefined; + let isTokenAutoRefreshEnabled: boolean | undefined = undefined; return isTokenAutoRefreshEnabled; } - newReactNativeFirebaseAppCheckProvider() { + newReactNativeFirebaseAppCheckProvider(): ReactNativeFirebaseAppCheckProvider { return new ReactNativeFirebaseAppCheckProvider(); } - initializeAppCheck(options) { + initializeAppCheck(options: AppCheckOptions): Promise { if (isOther) { if (!isObject(options)) { throw new Error('Invalid configuration: no options defined.'); @@ -109,44 +140,49 @@ class FirebaseAppCheckModule extends FirebaseModule { } this.native.setTokenAutoRefreshEnabled(options.isTokenAutoRefreshEnabled); - if (options.provider === undefined || options.provider.providerOptions === undefined) { + if (options.provider === undefined || (options.provider as any).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)) { + const providerOptions = (options.provider as any).providerOptions; + if (!isString(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, + providerOptions.android.provider, + providerOptions.android.debugToken, ); } if (Platform.OS === 'ios' || Platform.OS === 'macos') { - if (!isString(options.provider.providerOptions.apple.provider)) { + const providerOptions = (options.provider as any).providerOptions; + if (!isString(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, + providerOptions.apple.provider, + providerOptions.apple.debugToken, ); } throw new Error('Unsupported platform: ' + Platform.OS); } - activate(siteKeyOrProvider, isTokenAutoRefreshEnabled) { + activate( + _siteKeyOrProvider: string | AppCheckProvider, + isTokenAutoRefreshEnabled?: boolean, + ): Promise { if (isOther) { throw new Error('firebase.appCheck().activate(*) is not supported on other platforms'); } - if (!isString(siteKeyOrProvider)) { + 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(); + const rnfbProvider = new ReactNativeFirebaseAppCheckProvider(); rnfbProvider.configure({ android: { provider: 'playIntegrity', @@ -163,12 +199,11 @@ class FirebaseAppCheckModule extends FirebaseModule { return this.initializeAppCheck({ provider: rnfbProvider, isTokenAutoRefreshEnabled }); } - // TODO this is an async call - setTokenAutoRefreshEnabled(isTokenAutoRefreshEnabled) { + setTokenAutoRefreshEnabled(isTokenAutoRefreshEnabled: boolean): void { this.native.setTokenAutoRefreshEnabled(isTokenAutoRefreshEnabled); } - getToken(forceRefresh) { + getToken(forceRefresh?: boolean): Promise { if (!forceRefresh) { return this.native.getToken(false); } else { @@ -176,12 +211,17 @@ class FirebaseAppCheckModule extends FirebaseModule { } } - getLimitedUseToken() { + getLimitedUseToken(): Promise { return this.native.getLimitedUseToken(); } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - onTokenChanged(onNextOrObserver, onError, onCompletion) { + 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 @@ -189,13 +229,7 @@ class FirebaseAppCheckModule extends FirebaseModule { 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, @@ -224,11 +258,9 @@ export default createModuleNamespace({ nativeEvents: ['appCheck_token_changed'], hasMultiAppSupport: true, hasCustomUrlOrRegionSupport: false, - ModuleClass: FirebaseAppCheckModule, + ModuleClass: FirebaseAppCheckModuleImpl, }); -export * from './modular'; - // import appCheck, { firebase } from '@react-native-firebase/app-check'; // appCheck().X(...); // firebase.appCheck().X(...); diff --git a/packages/app-check/lib/types.d.ts b/packages/app-check/lib/types.d.ts new file mode 100644 index 0000000000..17c69ba310 --- /dev/null +++ b/packages/app-check/lib/types.d.ts @@ -0,0 +1,57 @@ +declare module '@react-native-firebase/app/lib/common' { + export const MODULAR_DEPRECATION_ARG: string; + export const isBoolean: (value: any) => value is boolean; + export const isIOS: boolean; + export const isString: (value: any) => value is string; + export const isObject: (value: any) => value is object; + export const isFunction: (value: any) => boolean; + export const isUndefined: (value: any) => value is undefined; + export const isOther: boolean; + export const parseListenerOrObserver: (listener: any) => (value: any) => void; +} + +declare module '@react-native-firebase/app/lib/internal' { + export function createModuleNamespace(config: any): any; + export class FirebaseModule { + constructor(...args: any[]); + native: any; + app: any; + emitter: any; + firebaseJson: any; + eventNameForApp(eventName: string): string; + _customUrlOrRegion: string | null; + } + export function getFirebaseRoot(): any; +} + +declare module '@react-native-firebase/app/lib/internal/nativeModule' { + export function setReactNativeModule(moduleName: string, module: any): void; +} + +declare module '@react-native-firebase/app/lib/internal/web/firebaseAppCheck' { + export function getApp(appName?: string): any; + export function initializeAppCheck(app: any, options: any): any; + export function getToken(appCheck: any, forceRefresh?: boolean): Promise; + export function getLimitedUseToken(appCheck: any): Promise; + export function setTokenAutoRefreshEnabled(appCheck: any, enabled: boolean): void; + export class CustomProvider { + constructor(options: any); + getToken(): Promise; + } + export function onTokenChanged(appCheck: any, callback: (tokenResult: any) => void): () => void; + export function makeIDBAvailable(): void; +} + +declare module '@react-native-firebase/app/lib/internal/web/utils' { + export function guard(fn: () => T): T; + export function emitEvent(eventName: string, event: any): void; +} + +declare module './version' { + export const version: string; +} + +declare module './web/RNFBAppCheckModule' { + const fallBackModule: any; + export default fallBackModule; +} diff --git a/packages/app-check/lib/types/appcheck.ts b/packages/app-check/lib/types/appcheck.ts new file mode 100644 index 0000000000..8d4dacfd8b --- /dev/null +++ b/packages/app-check/lib/types/appcheck.ts @@ -0,0 +1,105 @@ +/* + * 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. + * + */ + +/** + * 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 interface AppCheckProvider { + /** + * Returns an AppCheck token. + */ + getToken(): Promise; +} + +/** + * Custom provider options. + */ +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: AppCheckProvider; + + /** + * 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 }; + +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; + +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +export interface Statics { + // firebase.appCheck.* static props go here +} diff --git a/packages/app-check/lib/web/RNFBAppCheckModule.android.js b/packages/app-check/lib/web/RNFBAppCheckModule.android.js deleted file mode 100644 index af77c859b1..0000000000 --- a/packages/app-check/lib/web/RNFBAppCheckModule.android.js +++ /dev/null @@ -1,2 +0,0 @@ -// No-op for android. -export default {}; 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..1674e6a705 --- /dev/null +++ b/packages/app-check/lib/web/RNFBAppCheckModule.android.ts @@ -0,0 +1,19 @@ +/* + * 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. + * + */ + +// No-op for android. +export default {}; diff --git a/packages/app-check/lib/web/RNFBAppCheckModule.ios.js b/packages/app-check/lib/web/RNFBAppCheckModule.ios.js deleted file mode 100644 index a3429ada0e..0000000000 --- a/packages/app-check/lib/web/RNFBAppCheckModule.ios.js +++ /dev/null @@ -1,2 +0,0 @@ -// No-op for ios. -export default {}; 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..0a8978d0ca --- /dev/null +++ b/packages/app-check/lib/web/RNFBAppCheckModule.ios.ts @@ -0,0 +1,19 @@ +/* + * 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. + * + */ + +// No-op for ios. +export default {}; diff --git a/packages/app-check/lib/web/RNFBAppCheckModule.js b/packages/app-check/lib/web/RNFBAppCheckModule.ts similarity index 56% rename from packages/app-check/lib/web/RNFBAppCheckModule.js rename to packages/app-check/lib/web/RNFBAppCheckModule.ts index 634cf28ee2..4b75f7b0e9 100644 --- a/packages/app-check/lib/web/RNFBAppCheckModule.js +++ b/packages/app-check/lib/web/RNFBAppCheckModule.ts @@ -1,3 +1,21 @@ +/* + * 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. + * + */ + +// @ts-ignore - web platform imports available at runtime import { getApp, initializeAppCheck, @@ -8,12 +26,22 @@ import { onTokenChanged, makeIDBAvailable, } from '@react-native-firebase/app/lib/internal/web/firebaseAppCheck'; +// @ts-ignore - web platform imports available at runtime import { guard, emitEvent } from '@react-native-firebase/app/lib/internal/web/utils'; +import type { AppCheckOptions } from '../types/appcheck'; + +interface AppCheckInstances { + [appName: string]: any; +} + +interface ListenersForApp { + [appName: string]: (() => void) | undefined; +} -let appCheckInstances = {}; -let listenersForApp = {}; +let appCheckInstances: AppCheckInstances = {}; +let listenersForApp: ListenersForApp = {}; -function getAppCheckInstanceForApp(appName) { +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.`, @@ -29,11 +57,11 @@ function getAppCheckInstanceForApp(appName) { * java methods on Android. */ export default { - initializeAppCheck(appName, options) { + initializeAppCheck(appName: string, options: AppCheckOptions): Promise { makeIDBAvailable(); return guard(async () => { if (appCheckInstances[appName]) { - return; + return null; } const { provider, isTokenAutoRefreshEnabled } = options; const _provider = new CustomProvider({ @@ -48,32 +76,32 @@ export default { return null; }); }, - setTokenAutoRefreshEnabled(appName, isTokenAutoRefreshEnabled) { + setTokenAutoRefreshEnabled(appName: string, isTokenAutoRefreshEnabled: boolean): Promise { return guard(async () => { const instance = getAppCheckInstanceForApp(appName); setTokenAutoRefreshEnabled(instance, isTokenAutoRefreshEnabled); return null; }); }, - getLimitedUseToken(appName) { + getLimitedUseToken(appName: string): Promise { return guard(async () => { const instance = getAppCheckInstanceForApp(appName); return getLimitedUseToken(instance); }); }, - getToken(appName, forceRefresh) { + getToken(appName: string, forceRefresh: boolean): Promise { return guard(async () => { const instance = getAppCheckInstanceForApp(appName); return getToken(instance, forceRefresh); }); }, - addAppCheckListener(appName) { + addAppCheckListener(appName: string): Promise { return guard(async () => { if (listenersForApp[appName]) { - return; + return null; } const instance = getAppCheckInstanceForApp(appName); - listenersForApp[appName] = onTokenChanged(instance, tokenResult => { + listenersForApp[appName] = onTokenChanged(instance, (tokenResult: any) => { emitEvent('appCheck_token_changed', { appName, ...tokenResult, @@ -82,12 +110,12 @@ export default { return null; }); }, - removeAppCheckListener(appName) { + removeAppCheckListener(appName: string): Promise { return guard(async () => { if (!listenersForApp[appName]) { - return; + return null; } - listenersForApp[appName](); + listenersForApp[appName]?.(); delete listenersForApp[appName]; return null; }); diff --git a/packages/app-check/package.json b/packages/app-check/package.json index 65f8932e6c..d9b727d86a 100644 --- a/packages/app-check/package.json +++ b/packages/app-check/package.json @@ -3,14 +3,17 @@ "version": "23.5.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", + "compile": "bob build", "lint:plugin": "eslint plugin/src/*", - "prepare": "yarn run build && yarn run build:plugin" + "prepare": "yarn run build && yarn compile && yarn run build:plugin", + "tsc": "../../node_modules/.bin/tsc" }, "repository": { "type": "git", @@ -29,7 +32,8 @@ "expo": ">=47.0.0" }, "devDependencies": { - "expo": "^54.0.25" + "expo": "^54.0.25", + "react-native-builder-bob": "^0.40.13" }, "peerDependenciesMeta": { "expo": { @@ -39,5 +43,54 @@ "publishConfig": { "access": "public", "provenance": true - } + }, + "exports": { + ".": { + "source": "./lib/index.js", + "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", + "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..6bf8f06222 --- /dev/null +++ b/packages/app-check/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react-jsx", + "lib": ["ESNext", "DOM"], + "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/common/*": ["../app/dist/typescript/commonjs/lib/common/*"], + "@react-native-firebase/app": ["../app/dist/typescript/commonjs/lib"] + } + }, + "include": ["lib/**/*"], + "exclude": ["node_modules", "dist", "__tests__", "**/*.test.ts"] +}