Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[🐛] Other Platforms - [Web] [Auth] Methods are unsupported or not existing #7921

Open
3 tasks done
younes0 opened this issue Jul 19, 2024 · 18 comments
Open
3 tasks done
Labels
help: needs-triage Issue needs additional investigation/triaging. Needs Attention platform: macOS (Other) plugin: authentication Firebase Authentication

Comments

@younes0
Copy link
Contributor

younes0 commented Jul 19, 2024

Is there an existing issue for this?

  • I have searched the existing issues.

Please confirm you are aware of the 'Other' platform limitations.

  • I confirm that issue is not relating to a known platform limitation.

Please confirm, this issue is NOT for Android or iOS?

  • I confirm that this issue is not for Android and not for iOS.

Please describe your issue here.

When using auth module on web, usage of these methods
Located in auth\lib\index.js throw exceptions :

NativeFirebaseError: [auth/unsupported] This operation is not supported in this environment for

this.native.configureAuthDomain();

NativeFirebaseError: [auth/unknown] "" is not a function for

this.native.addAuthStateListener();
this.native.addIdTokenListener();
this.native
      .signInWithCustomToken(customToken)
      .then(userCredential => this._setUserCredential(userCredential));

Firebase app, firestore & database are initiated without any issues.

Additional context and comments

No response

@younes0 younes0 changed the title [🐛] Other Platforms - [Web] [Auth] Methods are said to be unsupported [🐛] Other Platforms - [Web] [Auth] Methods are unsupported or not existing Jul 19, 2024
@younes0
Copy link
Contributor Author

younes0 commented Jul 19, 2024

Another issue (would create later): the need of polyfill setimmediate otherwise error is thrown:

ReferenceError: setImmediate is not defined
    at Object.eventsNotifyReady (RNFBAppModule.js:209:7)
    at RNFBNativeEventEmitter.addListener (RNFBNativeEventEmitter.js:30:21)
    at subscribeToNativeModuleEvent (nativeModule.js:155:28)
    at initialiseNativeModule (nativeModule.js:133:7)
    at getNativeModule (nativeModule.js:207:10)
    at get native (FirebaseModule.js:56:41)
    at new FirebaseAuthModule (index.js:150:31)
    at firebaseModuleWithApp (namespace.js:171:57)
    at runEffect (BootingApp.tsx:52:31)

@shilo-klydo
Copy link

Another issue (would create later): the need of polyfill setimmediate otherwise error is thrown:

ReferenceError: setImmediate is not defined
    at Object.eventsNotifyReady (RNFBAppModule.js:209:7)
    at RNFBNativeEventEmitter.addListener (RNFBNativeEventEmitter.js:30:21)
    at subscribeToNativeModuleEvent (nativeModule.js:155:28)
    at initialiseNativeModule (nativeModule.js:133:7)
    at getNativeModule (nativeModule.js:207:10)
    at get native (FirebaseModule.js:56:41)
    at new FirebaseAuthModule (index.js:150:31)
    at firebaseModuleWithApp (namespace.js:171:57)
    at runEffect (BootingApp.tsx:52:31)

Hey @younes0 , did you find a solution for that?

@younes0
Copy link
Contributor Author

younes0 commented Jul 29, 2024

@shilo-klydo using the polyfill works fine. Which problem are you reffering to ?

@shilo-klydo
Copy link

@shilo-klydo using the polyfill works fine. Which problem are you reffering to ?

The setImmediate. sorry for the ignorance, what do you mean by polyfill?

@younes0
Copy link
Contributor Author

younes0 commented Jul 29, 2024

@shilo-klydo add this polyfill package https://github.com/yuzujs/setImmediate and import it at the top of your App.tsx

@aleksaelezovic
Copy link

I am also having this problem (version: 20.3.0), happens when calling auth().onUserChanged inside of a useEffect()
I followed the documentation and have similar code like here for initializing the firebase app.

Any solutions?

Also for setImmediate, same error happens to me but only when building with npx expo export --platform web
However, this successfully solved it:

@shilo-klydo add this polyfill package https://github.com/yuzujs/setImmediate and import it at the top of your App.tsx

@russellwheatley russellwheatley added plugin: authentication Firebase Authentication help: needs-triage Issue needs additional investigation/triaging. platform: macOS (Other) labels Aug 12, 2024
@younes0
Copy link
Contributor Author

younes0 commented Sep 2, 2024

I managed to make auth working by commenting these lines in @react-native-firebase/auth/lib/web/RNFBAuthModule.js:

// Returns a cached Firestore instance.
function getCachedAuthInstance(appName) {
  if (!instances[appName]) {
    // if (!isMemoryStorage()) {
    //   // Warn auth persistence is is disabled unless Async Storage implementation is provided.
    //   // eslint-disable-next-line no-console
    //   console.warn(
    //     ```
    // Firebase Auth persistence is disabled. To enable persistence, provide an Async Storage implementation.

    // For example, to use React Native Async Storage:

    //   import AsyncStorage from '@react-native-async-storage/async-storage';

    //   // Before initializing Firebase set the Async Storage implementation
    //   // that will be used to persist user sessions.
    //   firebase.setReactNativeAsyncStorage(AsyncStorage);

    //   // Then initialize Firebase as normal.
    //   await firebase.initializeApp({ ... });
    // ```,
    //   );
    // }
    instances[appName] = initializeAuth(getApp(appName), {
      // persistence: getReactNativePersistence(
      //   getReactNativeAsyncStorageInternal(),
      // ),
    });
  }
  return instances[appName];
}

Of course, this is not a even a workaround since you lose auth persistence.

Copy link

github-actions bot commented Oct 1, 2024

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

@github-actions github-actions bot added the Stale label Oct 1, 2024
@younes0
Copy link
Contributor Author

younes0 commented Oct 1, 2024

not stale

@github-actions github-actions bot removed the Stale label Oct 1, 2024
@vipulbhj
Copy link

I am also having the same issue when I try to run on web.

@vipulbhj
Copy link

vipulbhj commented Oct 14, 2024

@younes0 Are you using expo ? Is your app also using Native ? What does your setup look like ?

Does anyone know if calling await firebase.initializeApp({....}) only gets triggered for other platforms or not ? Do we need to manually do a platform check for calling this function ?

@vipulbhj
Copy link

Has anyone been able to use this library on web ? Can someone provide any examples ?

@mikehardy
Copy link
Collaborator

I use it on web, but before the "other" platform support, via shims that alias the packages between react-native-firebase and firebase-js-sdk, similar to what was implemented here but an older style.

There is a demonstration of same here, you can see the shim definitions that do the aliasing for webpack:

https://github.com/invertase/react-native-firebase-authentication-example/blob/68bd37f8c219f892655b40c73e9818370f368b82/template/craco.config.js#L105-L116

On the web, you run initializeApp:

https://github.com/invertase/react-native-firebase-authentication-example/blob/main/template/src/shims/firebase-app-web.ts

On other platforms you do not, because it is done at a very very early stage of app initialization in native code

You can think of it this way: one of the primary reasons to run firebase natively (vs just using firebase-js-sdk) is for crashlytics to be usable, and catch all native crashes including those at startup before the javascript engine is running. Additionally to start logging performance timers and perhaps startup analytics. Those all happen natively, prior to javascript being available, so the app must already be initialized natively correct? It is, from the google services json/plist files whose information is built into the app at boot time and used by the native sdks

@vipulbhj
Copy link

I wonder the support for "Other Platforms" works. I tried this with Platform checks, but it looks ugly.

If someone can point to where I might be able to start looking into to fix this, I would even love to contribute to this. I was browsing package code today, but it being split into individual packages makes it slightly harder.

if someone has some context, I would love if I can be given context and help speed this up.

@mikehardy
Copy link
Collaborator

I tried this with Platform checks, but it looks ugly.

I'm not aware of any other way to do it, given what I wrote above about the timing of initialization. Web must initialize delayed once you have javascript available, native must initialize very very early before javascript is available. Developers want features (boot time crash detection, performance etc), developer must accept realistic constraints 🤷

I haven't used the new "other" platform style myself yet and I'm not sure many have but the e2e test apps here may provide a reasonable baseline for understanding it - specifically the macos one as it is integrated using "other" if I understand correctly

@vipulbhj
Copy link

@Salakar If I remember correctly, it's you who implemented this. Do you have any insights on this.

Sorry for tagging you directly

@younes0
Copy link
Contributor Author

younes0 commented Oct 14, 2024

@vipulbhj

I've created unified interfaces for Firebase in web and React Native:

  • Using firebase-js for web and react-native-firebase for other platforms
  • Adapter files (auth.adapter.ts, database.adapter.ts etc.) provide consistent methods across platforms
  • FirebaseProviders with web-specific version for ReactFire integration
// auth.adapter.ts
// =====================================================
import { Platform } from "react-native";
import auth, { FirebaseAuthTypes } from "@react-native-firebase/auth";
import {
  Auth as WebAuth,
  UserCredential as WebUserCredential,
  getAuth as getWebAuth,
  signInWithCustomToken as webSignInWithCustomToken,
} from "@firebase/auth";

// types
// ---------------------------------------------------------------------------------
type AuthInstance = WebAuth | FirebaseAuthTypes.Module;

type UserCredential = WebUserCredential | FirebaseAuthTypes.UserCredential;

// methods
// ---------------------------------------------------------------------------------
export const getAuth = (): AuthInstance =>
  Platform.OS === "web" ? getWebAuth() : auth();

export const signInWithCustomToken = (token: string): Promise<UserCredential> =>
  Platform.OS === "web"
    ? webSignInWithCustomToken(getWebAuth(), token)
    : auth().signInWithCustomToken(token);

// database.adapter.ts
// =====================================================
import { Platform } from "react-native";
import {
  DataSnapshot as WebDataSnapshot,
  Database as WebDatabase,
  DatabaseReference as WebDatabaseReference,
  getDatabase as webGetDatabase,
  onValue as webOnValue,
  ref as webRef,
  set as webSet,
} from "@firebase/database";
import {
  FirebaseDatabaseTypes as NativeTypes,
  getDatabase as nativeGetDatabase,
  onValue as nativeOnValue,
  ref as nativeRef,
  set as nativeSet,
} from "@react-native-firebase/database";

// types
// ---------------------------------------------------------------------------------
export type DatabaseType = WebDatabase | NativeTypes.Module;

export type DatabaseReferenceType =
  | WebDatabaseReference
  | NativeTypes.Reference;

export type DataSnapshotType = WebDataSnapshot | NativeTypes.DataSnapshot;

export type UnsubscribeType = () => void;

// methods
// ---------------------------------------------------------------------------------
export const getDatabase = (): DatabaseType =>
  Platform.OS === "web" ? webGetDatabase() : nativeGetDatabase();

export const ref = (db: DatabaseType, path: string): DatabaseReferenceType =>
  Platform.OS === "web"
    ? webRef(db as WebDatabase, path)
    : nativeRef(db as NativeTypes.Module, path);

export const onValue = (
  reference: DatabaseReferenceType,
  callback: (snapshot: DataSnapshotType) => void,
): UnsubscribeType =>
  Platform.OS === "web"
    ? webOnValue(
        reference as WebDatabaseReference,
        callback as (snapshot: WebDataSnapshot) => void,
      )
    : nativeOnValue(
        reference as NativeTypes.Reference,
        callback as (snapshot: NativeTypes.DataSnapshot) => void,
      );

export const set = (
  reference: DatabaseReferenceType,
  value: any,
): Promise<void> =>
  Platform.OS === "web"
    ? webSet(reference as WebDatabaseReference, value)
    : nativeSet(reference as NativeTypes.Reference, value);


// FirebaseProviders.tsx
// =====================================================
import { PropsWithChildren } from "react";

const FirebaseProviders = ({ children }: PropsWithChildren) => children;

export default FirebaseProviders;

// FirebaseProviders.web.tsx
// =====================================================
import { PropsWithChildren } from "react";
import { getApp, getApps, initializeApp } from "@firebase/app";
import { initializeAuth } from "@firebase/auth";
import { FirebaseAppProvider, AuthProvider } from "reactfire";

import { getMmkvPersistence } from "@/modules/firebase/utils/auth-mmkv-persistence";

const firebaseConfig = {
  apiKey: process.env.EXPO_PUBLIC_FIREBASE_API_KEY,
  appId: process.env.EXPO_PUBLIC_FIREBASE_APP_ID,
  authDomain: process.env.EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN,
  databaseURL: process.env.EXPO_PUBLIC_FIREBASE_DATABASE_URL,
  messagingSenderId: process.env.EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  projectId: process.env.EXPO_PUBLIC_FIREBASE_PROJECT_ID,
};

export const firebaseApp = getApps().length
  ? getApp()
  : initializeApp(firebaseConfig);

export const fireBaseAuth = initializeAuth(firebaseApp, {
  persistence: getMmkvPersistence(), // required: persist auth token in mmkv
});

const FirebaseProviders = ({ children }: PropsWithChildren) => (
  <FirebaseAppProvider firebaseConfig={firebaseConfig}>
    <AuthProvider sdk={fireBaseAuth}>{children}</AuthProvider>
  </FirebaseAppProvider>
);

export default FirebaseProviders;

Code shared under MIT license.

@vipulbhj
Copy link

Thank you so much @younes0, I did something very similar too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help: needs-triage Issue needs additional investigation/triaging. Needs Attention platform: macOS (Other) plugin: authentication Firebase Authentication
Projects
None yet
Development

No branches or pull requests

6 participants