firebase / firebase-js-sdk

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

[FirebaseServerApp] Failed to initialize in Next.JS Edge Runtime #8299

Closed JoseVSeb closed 4 months ago

JoseVSeb commented 5 months ago

Operating System

N/A

Browser Version

N/A

Firebase SDK Version

10.12.2

Firebase SDK Product:

Auth

Describe your project's tooling

Next.JS

Describe the problem

I cannot use initializeServerApp in Next.JS middleware (Edge runtime) to secure the server at middleware as intended.

However, just commenting out isBrowser() guard clause makes it run as intended.

I understand why it fails (Edge runtime is essentially the same as Browser runtime) and why the guard clause is there, but implementing security in Next.JS middleware is a life-saver in code complexity.

Third-party libraries like next-firebase-auth-edge require the use of service account credentials which is not ideal especially since FirebaseServerApp does what is required without the same.

Steps and code to reproduce issue

  1. Initialize Next.JS project.
  2. Set up Firebase Auth on the client side.
  3. Set up service worker to inject the Authorization header as in https://firebase.google.com/codelabs/firebase-nextjs
  4. Set up Next.JS middleware to secure requests using initializeServerApp and Auth
// middleware.ts
const getIdTokenInServer = () => {
  const prefix = "Bearer ";
  const authorizationHeader = headers().get("Authorization");
  if (!authorizationHeader?.startsWith(prefix)) return null;
  return authorizationHeader.split(prefix)[1] ?? null;
};
const getFirebaseServerApp = () => {
  const idToken = getIdTokenInServer();
  // fails with guard clause but works fine if it's removed
  return initializeServerApp(
    firebaseConfig,
    idToken ? { authIdToken: idToken } : {},
  );
};
const getAuthInServer = () => getAuth(getFirebaseServerApp());
export async function middleware(req: NextRequest) {
  const auth = getAuthInServer();
  await auth.authStateReady()

  if (!auth.currentUser) return NextResponse.redirect(/** redirect url */);

  /** other authorization logic */

  return NextResponse.next();
}
jdgamble555 commented 5 months ago

@JoseVSeb - This is related to #8284, but I can't get a response on this even though it is possibly a simple fix.

😦

J

codercatdev commented 5 months ago

Same issue, so I followed this https://dev.to/geiel/how-to-use-firebase-authentication-in-nextjs-13-client-and-server-side-1bbn

jdgamble555 commented 5 months ago

Problem is that solution does not solve this issue, nor allow you to use initializeServerApp.

J

JoseVSeb commented 5 months ago

Same issue, so I followed this https://dev.to/geiel/how-to-use-firebase-authentication-in-nextjs-13-client-and-server-side-1bbn

That solution is from nearly a year before FirebaseServerApp was added and was for firebase-admin. It does not apply here. FirebaseServerApp builds on top of FirebaseApp (which was meant for the browser) without the login flow (replaced by passing idToken in initialization), making it fully compatible with Edge runtime. Only the isBrowser() guard clause prevents it from doing so.

JoseVSeb commented 5 months ago

1773 is the source of the issue. it was initially applied to detect Firebase Lite in browsers, it seems, and was changed to include web workers as well. But that is invalid as it's not explicitly checking if it's a WebWorker.

jdgamble555 commented 5 months ago

Then the question becomes how do you differentiate a Web Worker from a Cloudflare Worker (what Vercel Edge uses too).

Possible fix:

https://community.cloudflare.com/t/how-to-detect-the-cloudflare-worker-runtime/293715/2

Of course you would need to check for other potential environments like Fastify... and maybe Deno and Bun runtimes as well to be complete...

J

FWI there maybe more issues other than isBrowser, but we can't check for them until this is fixed.

JoseVSeb commented 5 months ago

We could use self instanceof WorkerGlobalScope to detect web worker.

JoseVSeb commented 5 months ago

another issue ReferenceError: FinalizationRegistry is not defined. Edge runtime doesn't support FinalizationRegistry but @firebase/app uses it in FirebaseServerAppImpl unconditionally, even if there is a guard clause in initializeServerApp that suggests that it is needed only if releaseOnDeref is provided in config.

isaniovitor commented 5 months ago

Same problem here, any updates?

JoseVSeb commented 5 months ago

Same problem here, any updates?

merging #8315 and #8320 should fix it.

DellaBitta commented 4 months ago

I just merged PR #8315 and #8335 which should fix this issue in our next release. Please open a new issue and add a link to this one for context if you continue to have related problems.

Thanks everyone for the reports and the submissions!

jdgamble555 commented 4 months ago

@DellaBitta - Is there a @next or a way to test this before release?

JoseVSeb commented 4 months ago

@jdgamble555 you can use firebase@canary

jdgamble555 commented 4 months ago

@JoseVSeb - Thanks! This seems to work so far. I have only tested this on a cloudflare worker with firestore/lite and FirebaseServerApp. I'm not sure if other firebase providers have problems, but these two seem to work.

Will keep you posted in a new post if something fails.

J