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

[BUG] Authjs middleware casues self._ENTRIES[b.name].default is not a function #252

Closed
mackenly opened this issue Jan 14, 2025 · 5 comments · Fixed by #268
Closed

[BUG] Authjs middleware casues self._ENTRIES[b.name].default is not a function #252

mackenly opened this issue Jan 14, 2025 · 5 comments · Fixed by #268
Assignees
Labels
bug Something isn't working triage

Comments

@mackenly
Copy link

Describe the bug

When Authjs' middleware is used (see below), the following error is thrown:

[wrangler:err] TypeError: self._ENTRIES[b.name].default is not a function
    at Object.edgeFunctionHandler (file:///mnt/c/MyRepos/temp-auth-js-demo/auth-js-d1-example-test/.open-next/middleware/handler.mjs:7841:79)
    at handleMiddleware (file:///mnt/c/MyRepos/temp-auth-js-demo/auth-js-d1-example-test/.open-next/middleware/handler.mjs:9058:35)
    at async routingHandler (file:///mnt/c/MyRepos/temp-auth-js-demo/auth-js-d1-example-test/.open-next/middleware/handler.mjs:9239:25)
    at null.<anonymous> (async file:///mnt/c/MyRepos/temp-auth-js-demo/auth-js-d1-example-test/.wrangler/tmp/dev-jKZoIg/worker.js:1294:100909)
    at null.<anonymous> (async file:///mnt/c/MyRepos/temp-auth-js-demo/auth-js-d1-example-test/.wrangler/tmp/dev-jKZoIg/worker.js:1294:72131)
    at null.<anonymous> (async file:///mnt/c/MyRepos/temp-auth-js-demo/auth-js-d1-example-test/.wrangler/tmp/dev-jKZoIg/worker.js:859:15291)
    at null.<anonymous> (async file:///mnt/c/MyRepos/temp-auth-js-demo/auth-js-d1-example-test/.wrangler/tmp/dev-jKZoIg/worker.js:1366:52043)
    at async jsonError (file:///mnt/c/MyRepos/temp-auth-js-demo/auth-js-d1-example-test/node_modules/wrangler/templates/middleware/middleware-miniflare3-json-error.ts:22:10)
    at async drainBody (file:///mnt/c/MyRepos/temp-auth-js-demo/auth-js-d1-example-test/node_modules/wrangler/templates/middleware/middleware-ensure-req-body-drained.ts:5:10)
[wrangler:inf] GET / 500 Internal Server Error (1596ms)

Authjs middleware.ts (from here):

export { auth as middleware } from "./app/auth";

If I use an example middleware like this one, everything works as expected. It's only with Authjs that it seems to break.

Steps to reproduce

Here's a repo with the code: https://github.com/mackenly/temp-auth-js-demo
Minimal example with Authjs installed.

Expected behavior

No exceptions to be thrown.

@opennextjs/cloudflare version

0.3.8

Wrangler version

3.101.0

next info output

Operating System:
  Platform: linux
  Arch: x64
  Version: #1 SMP Wed Mar 2 00:30:59 UTC 2022
  Available memory (MB): 15900
  Available CPU cores: 16
Binaries:
  Node: 20.13.1
  npm: 10.5.2
  Yarn: 1.22.19
  pnpm: 9.15.4
Relevant Packages:
  next: 14.2.5 // An outdated version detected (latest is 15.1.4), upgrade is highly recommended!
  eslint-config-next: 14.2.5
  react: 18.3.1
  react-dom: 18.3.1
  typescript: 5.7.3
Next.js Config:
  output: N/A
 ⚠ An outdated version detected (latest is 15.1.4), upgrade is highly recommended!

Additional context

No response

@willenigma
Copy link

Encountered the same issue. I conducted several experiments and finally found a solution. The root cause might not be related to Auth.js, but rather to the use of top-level await in middleware.ts. In your reproduction repo, there is a top-level await in auth.ts. When auth.ts is imported into middleware.ts, it causes the problem.

I conducted several experiments. As long as you import code that uses top-level await into middleware.ts, you'll encounter the same error—even if the await code is as simple as this:

export async function test1() {
    return new Promise(resolve => resolve("Hello"));
}

export const testAsync = await test1();

Solution

To resolve the issue, we should avoid importing any code that uses top-level await into middleware.ts. Additionally, we may need to change how we integrate Auth.js in middleware.ts, as we cannot import any code that uses top-level await.

Here is a working demo:

// middleware.ts
import { NextResponse } from "next/server";
import { getDB } from "@/lib/db";
import NextAuth from "next-auth";
import { DrizzleAdapter } from "@auth/drizzle-adapter";
import { accounts, sessions, users, verificationTokens } from "@/db/schema";
import Google from "next-auth/providers/google";
import { env } from "@/env.mjs";

export default async function middleware(request) {
  console.log("middleware is running!");
  const db = await getDB();
  const { auth } = initAuthJs(db);

  const response = auth(request);
  if (response) {
    return response;
  }

  return NextResponse.next();
}

export function initAuthJs(drizzleDBInstance) {
    return NextAuth({
        trustHost: true,
        adapter: DrizzleAdapter(drizzleDBInstance, {
            usersTable: users,
            accountsTable: accounts,
            sessionsTable: sessions,
            verificationTokensTable: verificationTokens,
        }),
        providers: [
            Google({
                clientId: env.GOOGLE_CLIENT_ID,
                clientSecret: env.GOOGLE_CLIENT_SECRET,
            })
        ]
    });
}

Some explanations:

  1. The usage of the auth function in middleware.ts is based on this example. It may not appear in the official Auth.js documentation, as I couldn't find it there.
  2. Note that if you do not import auth.ts into middleware.ts, you can continue using top-level await in auth.ts. The key is to ensure that middleware.ts does not import any code that uses top-level await.

@vicb
Copy link
Contributor

vicb commented Jan 17, 2025

Thanks for figuring out the root cause @willenigma, great investigation!

We will look into that.

Note that anyway, the following code from @mackenly :

const authResult = async (): Promise<NextAuthResult> => {
    return NextAuth({
        providers: [
            Resend({
                apiKey: (await getCloudflareContext()).env.AUTH_RESEND_KEY,
                from: (await getCloudflareContext()).env.AUTH_EMAIL_FROM,
            }),
        ],
        adapter: D1Adapter((await getCloudflareContext()).env.DB),
    });
};

export const { handlers, signIn, signOut, auth } = await authResult();

has problem because getCloudflareContext is only available when the worker is handling a request (in a "request context") so (await getCloudflareContext()).env.AUTH_RESEND_KEY will not work.

So lazy init is the right thing to do here.

(We plan to improve the DX here: #229)

@dario-piotrowicz
Copy link
Contributor

Hi there 🙂 , thanks for the issue @mackenly and thanks a lot for the exploration @willenigma! That's been super helpful! 🫶

#268 should address the issue, please try the PR's prerelease and let me know if it works for you 😄🙏

@dario-piotrowicz
Copy link
Contributor

We merged the PR that closed this issue, @mackenly, @willenigma if you still experience the issue please let me know and we can reopen this 🙂

@mackenly
Copy link
Author

Working great for me. Thanks! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working triage
4 participants