4

Getting this error in Next.js _middleware file when I try to initialize Firebase admin V9. Anyone know how to solve this issue?

./node_modules/@google-cloud/storage/build/src/bucket.js:22:0
Module not found: Can't resolve 'fs'

../../firebase/auth-admin

import * as admin from "firebase-admin";

if (!admin.apps.length) {
  admin.initializeApp({
    credential: admin.credential.cert({
      projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
      clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
      privateKey: process.env.FIREBASE_ADMIN_PRIVATE_KEY,
    }),
  });
}

const firestore = admin.firestore();

const auth = admin.auth();

export { firestore, auth };

Calling it in my _middleware

import { NextFetchEvent, NextRequest, NextResponse } from "next/server";
import { auth } from "../../firebase/auth-admin";

export default async function authenticate(
  req: NextRequest,
  ev: NextFetchEvent
) {
  const token = req.headers.get("token");
  console.log("auth = ", auth);
  //   const decodeToken = await auth.verifyIdToken(token);
  return NextResponse.next();
}

I saw a solution here by customizing webpack but this does not fix it.

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  webpack: (config, { isServer, node }) => {
    node = {
      ...node,
      fs: "empty",
      child_process: "empty",
      net: "empty",
      tls: "empty",
    };
    return config;
  },
};

module.exports = nextConfig;
juliomalves
  • 42,130
  • 20
  • 150
  • 146
me-me
  • 5,139
  • 13
  • 50
  • 91

4 Answers4

12

The Edge Runtime, which is used by Next.js Middleware, does not support Node.js native APIs.

From the Edge Runtime documentation:

The Edge Runtime has some restrictions including:

  • Native Node.js APIs are not supported. For example, you can't read or write to the filesystem
  • Node Modules can be used, as long as they implement ES Modules and do not use any native Node.js APIs

You can't use Node.js libraries that use fs in Next.js Middleware. Try using a client-side library instead.

juliomalves
  • 42,130
  • 20
  • 150
  • 146
  • Dec 2022: I tried using the client-side library instead (to get a document from firestore), using the v9 tree-shakeable firebase lib. It didn't really work: some requests would succeed, most would fail with "Network connection lost" from the edge function. During build, I see ```./node_modules/@firebase/installations/dist/esm/index.esm2017.js A Node.js API is used (BroadcastChannel at line: 373) which is not supported in the Edge Runtime. Learn more: https://nextjs.org/docs/api-reference/edge-runtime``` so I don't think this approach works. The request was 300 ms which was too long anyway – xaphod Dec 15 '22 at 17:40
  • @xaphod It doesn't look like the Edge Runtime supports `BroadcastChannel` either. Check https://nextjs.org/docs/api-reference/edge-runtime for all the supported Web APIs. – juliomalves Dec 15 '22 at 17:50
  • well yeah, that was the point of my comment -- that there currently doesn't appear to be a firebase client-side library you can safely use on the Edge Runtime, at least to access Firestore. I think it's a bad idea to be making network calls from the edge like this though in any case? Not sure... – xaphod Dec 16 '22 at 18:06
4

I wasted a lot of time tying to get this to work. The weird thing is that this will work in the api itself.

So instead of calling firebase-admin action in the _middleware file. Call it in the api itself like:

import type { NextApiRequest, NextApiResponse } from 'next'
import { auth } from "../../firebase/auth-admin";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const authorization = req.headers.authorization
  console.log(`Handler auth header: ${authorization}`)

  if (!authorization) {
    return res.status(401).json({ message: 'Authorisation header not found.' })
  }

  const token = authorization.split(' ')[1]
  if (!token) {
    return res.status(401).json({ message: 'Bearer token not found.' })
  }

  console.log(`Token: ${token}`)

  try {
    const {uid} = await auth.verifyIdToken("sd" + token)
    console.log(`User uid: ${uid}`)
    res.status(200).json({ userId: uid })
  } catch (error) {
    console.log(`verifyIdToken error: ${error}`)
    res.status(401).json({ message: `Error while verifying token. Error: ${error}` })
  }
}

A workaround to make this reusable is to create a wrapper function.

If anyone knows how to make this work in a _middleware file, I would be really grateful.

Edit: Gist for the wrapper middleware function: https://gist.github.com/jvgrootveld/ed1863f0beddc1cc2bf2d3593dedb6da

Justin
  • 2,960
  • 2
  • 34
  • 48
  • 6
    Half decent solution, still not automatically hooked into the routes, though. Seems the only way to do that at the moment would be a custom server which would prevent hosting on Vercel. Honestly I've found Next middleware to be a nightmare to work with. Not being able to make custom network requests (not using `fetch`) removes so much flexibility. How are people checking API keys against backend stores with middleware? I don't get how Next doesn't support this, very common use case for many APIs. – Damien Roche Nov 24 '22 at 21:18
  • +1 lol. Nextjs middleware is still a pain to work with. Best solution I found was to decode and verify tokens (or API keys) in each route, rather than doing it in middleware. The killer was realizing I couldn't decode tokens at the middleware with jsonwebtoken... So it peppers a fair bit of boilerplate, but does solve the problem. Nextjs middleware has greatly improved over the last few months though. – tudor14 Jan 14 '23 at 22:20
0

make sure you're not calling firebase-admin in the client

import * as admin from "firebase-admin";
Chukwuemeka Maduekwe
  • 6,687
  • 5
  • 44
  • 67
0

I've recently released a library that aims to solve the problem: https://github.com/ensite-in/next-firebase-auth-edge

It allows to create and verify tokens inside Next.js middleware and Next.js 13 server components. Built entirely upon Web Crypto API.

Please note it does rely on Next.js ^13.0.5 experimental "appDir" and "allowMiddlewareResponseBody" features.