firebase / firebase-js-sdk

Firebase Javascript SDK
https://firebase.google.com/docs/web/setup
Other
4.86k stars 891 forks source link

Service auth is not available #8506

Open jdgamble555 opened 1 month ago

jdgamble555 commented 1 month ago

Operating System

Windows 11

Environment (if applicable)

Vercel Edge with Qwik

Firebase SDK Version

10.13.1-canary.16d62d4fa (or lower)

Firebase SDK Product(s)

Auth

Project Tooling

Qwik (1.8.0)

Detailed Problem Description

When I use getAuth() on the server inside a Qwik app, I get the Service Auth is not available error. The local testing environment has no problems and works as expected.

It is literally something that gets errored out in Qwik with getAuth().

Possibly related to #8355

While I'm not sure why the error is thrown, it is coming from here: https://github.com/firebase/firebase-js-sdk/blob/629919ea760e35b7d880a099edf7f42b5bcbae4b/packages/component/src/provider.ts#L130

J

Steps and code to reproduce issue

I made a Repo:

  1. Add your Firebase environment variables to .env and to Vercel Edge environment variables.

    PUBLIC_FIREBASE_CONFIG={"apiKey":....,"authDomain"...}
  2. Deploy to Vercel Edge.

  3. Sign in with Google.

  4. Click the about link in navigation.

  5. See error thrown.

See Deployed Demo with error.

If have isolated the error to getAuth(). This app doesn't use initializeApp or initializeServerApp, but the error is the same in either case.

Here is failing code.

J

google-oss-bot commented 1 month ago

I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.

DellaBitta commented 1 month ago

Hi @jdgamble555,

This reported error is generally realted to an Auth Instance being used without an initialized App Instance. This doesn't seem related to #8355 as that had to do with a lack of support for features in fetch, which would come much later in the usage of Auth.

Are you certain that there's a default instance of App initialized within the context of that Request Handler? It might be worth adding a call to initializeApp before the getAuth call to double check.

jdgamble555 commented 1 month ago

@DellaBitta - I just made the test repo so that you can see clearly what the problem is. This equally happens when the server is initialized. I already have a repo for that.

quik-firebase-todo

J

hsubox76 commented 1 month ago

I'm not familiar with Qwik or Vercel Edge or the architecture of your app but what I can do is tell you what causes that error in the Firebase SDK.

The error is usually caused when the entry point bundle that initializeApp comes from (firebase/app) doesn't match the version of the @firebase/auth bundle that getAuth was imported from. A couple of reasons this happens is (1) mismatched package versions - you have a newer @firebase/app but an older @firebase/auth - sometimes caused by incomplete npm upgrades - cleaning out node_modules and lockfiles and reinstalling usually fixes it, (2) your app does multiple builds - for example, one for server and one for client, with different module resolution configurations - for example, the server build process looks for CJS or the "main" or "exports.node" paths in the package.json, while the client build process looks for "browser" or "module" or "exports.browser/import". This would lead to each process importing a different bundle (CJS vs ESM) which aren't aware of each other, and if you try to share across them, the getAuth() from one won't recognize the app from another.

I took a quick glance and noticed your repo gets the "app" to use for getAuth() in 3 different places, by two different methods, you initializeApp/getApp in 2 places and initializeServerApp in the file that you say you're getting the error in. Do you not get the error from the other two files? Are they all compiled into one app? Or is the initializeServerApp only used in an isolated process on the server? If you use app instead of initializeServerApp there, do you have the same error?

https://github.com/search?q=repo%3Ajdgamble555%2Fqwik-firebase-todo%20getAuth&type=code

Also does this problem only occur when deployed to Vercel Edge? Does it happen locally? Is there any way to deploy it locally so we can reproduce and diagnose it? If not, the best I can suggest is to dig into Qwik or Vercel Edge and try to understand which environments your various bits of code are running in, what the module resolution config might be for each environment if there are multiple environments, and try to give us a breakdown of what's running where, and with what module resolution config. Unfortunately I'm not an expert in these tools and I'm afraid if this isn't reproducible locally, the error is likely to happen somewhere in this tooling pipeline, and can't be figured out without more understanding of the steps of this pipeline. If you can provide insight into it, that would be great.

jdgamble555 commented 1 month ago

Well I know a few things:

1.) I only have one package version, not even sure how you would have two package versions.

2.) Since I am running on the server, it clearly has a build for the server and for the client. I have deleted package-lock.json and node_modules and rebuilt. Same error.

3.) Everything works fine locally, in dev and in production builds.

4.) As far as I can tell, everything compiles with type: "module" everywhere, even in vercel edge plugin. There is unfortunately not a Vercel Serverless plugin, so no way to test this.

5.) The problem only persists when I call, not bundle, but call getAuth() on the server. It does not seem to matter what initialize method I use.

Given this information, any way to isolate the problem (at least get rid of what the causes aren't)?

Here is my test repository with a simpler version - https://github.com/jdgamble555/qwik-firebase-test

Thanks,

J

hsubox76 commented 1 month ago

2.) Since I am running on the server, it clearly has a build for the server and for the client. 5.) The problem only persists when I call, not bundle, but call getAuth() on the server.

It seems like you have two separate builds and two separate copies of the Firebase SDK code - not sure if I'm reading right, but one is in a bundle? for client? And the other is not bundled but called directly out of node_modules by the server?

The way that the Firebase SDK manages different services is a component registry. When you import from firebase/app or firebase/auth, there's some code that runs immediately on load (before you call any methods like initializeApp or getAuth) that registers that service to a component registry in local memory. If you have different runtimes (as you seem to with server and client), then you'll create two different registries in two different namespaces that don't know about each other.

I don't know Qwik so I'm not very sure what's going on, but it seems like the setup when you run it locally must have some kind of shared context vs when you deploy it, where some of the build is shared but some of the code is run in a separate runtime?

My only guess would be to either further separate or further share the code to prevent this from happening. For further separating it, if there's any way to isolate the runtimes of server and client and prevent them from sharing anything after the build? For further sharing it, if there's any way to ensure they are using the same context? Perhaps make firebase external for the client build instead of bundling it, so that both builds are calling code in node_modules?

If none of this helps, we can try to get up to speed on Qwik and Vercel Edge deployment to debug it ourselves but this may take some time, as we have to prioritize this among our tasks.

jdgamble555 commented 1 month ago

The way that the Firebase SDK manages different services is a component registry. When you import from firebase/app or firebase/auth, there's some code that runs immediately on load (before you call any methods like initializeApp or getAuth) that registers that service to a component registry in local memory.

So, even if not one function gets called on the server, just by running "import," there is immediate code that gets ran?

If that is the case, what makes it a new copy of Firebase? Is there a way to delete whatever copy of Firebase is in memory? Why wouldn't it just detect the existing version and not run again? This is confusing to me.

I have modified my code again so that it is impossible to run any firebase imported function on the server:

https://github.com/jdgamble555/qwik-firebase-test

I still get the error. I am technically importing on the server, but not running any functions unless it is in the browser.

A few things:

  1. This problem does not exists locally (dev or prod)
  2. This problem does not exists on other frameworks when I import something on the server
  3. This problem seems to only exist on Vercel Edge or Cloudflare with Firebase

J

jdgamble555 commented 1 month ago

@hsubox76 - This is not an import problem. I just refactored my entire code base to use dynamic imports everywhere, and the problem still exists. It is impossible to run any firebase code on the server, except in the server route. This is the only place there has ever been a problem, and it doesn't seem to be related to any client bundling problems.

https://github.com/jdgamble555/qwik-firebase-test

J

DellaBitta commented 1 week ago

Hi @jdgamble555,

I was able to reproduce the problem. It appears that something in the build pipeline is aggressively tree shaking portions of the Firebase JS SDK when it packages it for the edge runtime. The "service" mentioned in the log file is that fact that Auth cannot properly register itself with the instance of FirebaseApp (or FirebaseServerApp). This is due to the hooks it would use to register itself with the instance of App are inexplicitly missing from the SDK implementation.

I'm not that familiar with Qwik and I'm not sure how to tune the build pipeline, or even if there are ways to tune it. Do you have any thoughts on how that might be done?

In the meantime it might be worth using the Firebase REST API to get around this problem.

jdgamble555 commented 3 days ago

https://github.com/QwikDev/qwik/issues/7052#issuecomment-2472666689

To solve the problem, the firebase import needs to happen in a different file. This file should export helpers that expose the desired firebase functionality. Then when you use these helpers in your Qwik code, they won't be tree shaken and the firebase side effects can run on first import.

@DellaBitta - Is this something that is possible?

Thanks!

J