vercel / next.js

The React Framework
https://nextjs.org
MIT License
125.41k stars 26.78k forks source link

Route Handlers Crash with bcrypt (see: #46493) #66396

Open jrilez opened 4 months ago

jrilez commented 4 months ago

Verify canary release

Provide environment information

Operating System:
  Platform: linux
  Arch: x64
  Version: #1 SMP PREEMPT_DYNAMIC PMX 6.5.11-8 (2024-01-30T12:27Z)
  Available memory (MB): 2048
  Available CPU cores: 2
Binaries:
  Node: 18.19.0
  npm: 9.2.0
  Yarn: N/A
  pnpm: N/A
Relevant Packages:
  next: 14.2.3 // Latest available version is detected (14.2.3).
  eslint-config-next: 14.0.0
  react: 18.3.1
  react-dom: 18.3.1
  typescript: 5.2.2
Next.js Config:
  output: N/A

Which example does this report relate to?

next-learn/dashboard/starter-example/

What browser are you using? (if relevant)

Firefox 126.0.1

How are you deploying your application? (if relevant)

npm run dev

Describe the Bug

I have been following this guide, and in chapter 15, when setting login-form.tsx as a client component with "use client";, I get the following error:

npm run dev

> dev
> next dev

  ▲ Next.js 14.2.3
  - Local:        http://localhost:3000
  - Environments: .env

 ✓ Starting...
 ✓ Ready in 2.7s
 ✓ Compiled /middleware in 224ms
 ✓ Compiled (224 modules)
 ○ Compiling /lobby ...
 ⨯ ./node_modules/@mapbox/node-pre-gyp/lib/util/nw-pre-gyp/index.html
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> <!doctype html>
| <html>
| <head>

Import trace for requested module:
./node_modules/@mapbox/node-pre-gyp/lib/util/nw-pre-gyp/index.html
./node_modules/@mapbox/node-pre-gyp/lib/ sync ^\.\/.*$
./node_modules/@mapbox/node-pre-gyp/lib/node-pre-gyp.js
./node_modules/bcrypt/bcrypt.js
./auth.ts
./app/lib/actions.ts
./app/ui/login-form.tsx
 ⨯ ./node_modules/@mapbox/node-pre-gyp/lib/util/nw-pre-gyp/index.html
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> <!doctype html>
| <html>
| <head>

Import trace for requested module:
./node_modules/@mapbox/node-pre-gyp/lib/util/nw-pre-gyp/index.html
./node_modules/@mapbox/node-pre-gyp/lib/ sync ^\.\/.*$
./node_modules/@mapbox/node-pre-gyp/lib/node-pre-gyp.js
./node_modules/bcrypt/bcrypt.js
./auth.ts
./app/lib/actions.ts
./app/ui/login-form.tsx
 GET /lobby 500 in 5046ms
 ⨯ ./node_modules/@mapbox/node-pre-gyp/lib/util/nw-pre-gyp/index.html
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> <!doctype html>
| <html>
| <head>

Import trace for requested module:
./node_modules/@mapbox/node-pre-gyp/lib/util/nw-pre-gyp/index.html
./node_modules/@mapbox/node-pre-gyp/lib/ sync ^\.\/.*$
./node_modules/@mapbox/node-pre-gyp/lib/node-pre-gyp.js
./node_modules/bcrypt/bcrypt.js
./auth.ts
./app/lib/actions.ts
./app/ui/login-form.tsx
 GET /lobby 500 in 747ms
 GET /lobby?callbackUrl=http%3A%2F%2Flocalhost%3A3000%2Ffavicon.ico 500 in 27ms

screenshot

I read of the same issue in Route Handlers Crash with bcrypt #46493 , so I even tried to add serverComponentsExternalPackages to next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    serverComponentsExternalPackages: ['bcrypt'],
  },
}

module.exports = nextConfig;

auth.ts

import { z } from 'zod';
import bcrypt from 'bcrypt';

import NextAuth from 'next-auth';
import Credentials from 'next-auth/providers/credentials';
import { sql } from '@vercel/postgres';

import { authConfig } from './auth.config';
import type { User } from '@/app/lib/definitions';

async function getUser(email: string): Promise<User | undefined> {
  try {
    const user = await sql<User>`SELECT * FROM users WHERE email=${email}`;
    return user.rows[0];
  } catch (error) {
    console.error('Failed to fetch user:', error);
    throw new Error('Failed to fetch user.');
  }
}

export const { auth, signIn, signOut } = NextAuth({
  ...authConfig,
  providers: [
    Credentials({
      async authorize(credentials) {
        const parsedCredentials = z
          .object({ email: z.string().email(), password: z.string().min(6) })
          .safeParse(credentials);

        if (parsedCredentials.success) {
          const { email, password } = parsedCredentials.data;
          const user = await getUser(email);
          if (!user) return null;
          const passwordsMatch = await bcrypt.compare(password, user.password);

          if (passwordsMatch) return user;
        }

        console.log('Invalid credentials');
        return null;
      },
    }),
  ],
});

Expected Behavior

no response

To Reproduce

no response

hero100era commented 3 months ago

I got the same error when use bcrypt in a middleware. (Next 14.2.3)

k9982874 commented 3 months ago

The same error occurs. To be honest solving all sorts of weird problems with the framework itself has made me exhausted before I even started actually coding work.

k9982874 commented 3 months ago

Here is a "solution". I found that the promise function is allowed in the middleware. Apparently, we can call an authentication endpoint via the fetch method. This "auth" endpoint will verify the credentials with the bcrypt module.

This clumsy solution fulfills the requirement and while not breaking the beautiful vase!

skyddyyu commented 3 months ago

I got the same error when use bcrypt on async method.

k9982874 commented 3 months ago

My final solution

Basically, you need to do the authorization in your own backend, and ensure the credentials are valid before passing them to the authorize callback.

image