Nextjs build failing because of jsonwebtoken in _middleware.ts

4.4k views Asked by At

Would love some help on a Vercel deployment. I created a _middleware.ts file that checks a JWT that a user has in their cookie.

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { JwtPayload, verify } from 'jsonwebtoken'

export async function middleware(req: NextRequest) {
  let response = NextResponse.next()
  const url = req.nextUrl.clone()

  const token = req.cookies['allow-list']

  if (!token || token === 'deleted') {
    return response 
  }

  try {
    const decodedToken = verify(
      token,
      process.env.TOKEN_SECRET as string
    ) as JwtPayload
  } catch (e) {}

  return response
}

However, because of it, when I try to build my project, I get the following error: "Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Middleware pages/_middleware". Is there a way around this? It works when I'm running it locally.

[22:59:29.409] Cloning github.com/dimitriborgers/test (Branch: master, Commit: efc1977)
[22:59:30.051] Cloning completed: 642.161ms
[22:59:30.427] Installing build runtime...
[22:59:34.350] Build runtime installed: 3.924s
[22:59:35.048] Looking up build cache...
[22:59:35.297] Build Cache not found
[22:59:35.503] Installing dependencies...
[22:59:35.507] Detected `package-lock.json` generated by npm 7...
[22:59:54.367] 
[22:59:54.367] added 519 packages in 19s
[22:59:54.367] 
[22:59:54.367] 97 packages are looking for funding
[22:59:54.367]   run `npm fund` for details
[22:59:54.386] Detected Next.js version: 12.1.0
[22:59:54.392] Detected `package-lock.json` generated by npm 7...
[22:59:54.392] Running "npm run build"
[22:59:54.673] 
[22:59:54.673] > build
[22:59:54.673] > next build
[22:59:54.673] 
[22:59:55.312] Attention: Next.js now collects completely anonymous telemetry regarding usage.
[22:59:55.312] This information is used to shape Next.js' roadmap and prioritize features.
[22:59:55.312] You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
[22:59:55.312] https://nextjs.org/telemetry
[22:59:55.313] 
[22:59:55.351] info  - Checking validity of types...
[22:59:58.776] warn  - No ESLint configuration detected. Run next lint to begin setup
[22:59:58.781] info  - Creating an optimized production build...
[22:59:58.786] warn  - using beta Middleware (not covered by semver) - https://nextjs.org/docs/messages/beta-middleware
[23:00:22.847] Failed to compile.
[23:00:22.847] 
[23:00:22.847] ./node_modules/function-bind/implementation.js
[23:00:22.847] Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Middleware pages/_middleware
[23:00:22.848] 
[23:00:22.848] Import trace for requested module:
[23:00:22.848] ./node_modules/function-bind/index.js
[23:00:22.848] ./node_modules/call-bind/index.js
[23:00:22.848] ./node_modules/call-bind/callBound.js
[23:00:22.848] ./node_modules/which-typed-array/index.js
[23:00:22.848] ./node_modules/util/support/types.js
[23:00:22.848] ./node_modules/util/util.js
[23:00:22.848] ./node_modules/jws/lib/sign-stream.js
[23:00:22.849] ./node_modules/jws/index.js
[23:00:22.849] ./node_modules/jsonwebtoken/decode.js
[23:00:22.849] ./node_modules/jsonwebtoken/index.js
[23:00:22.849] ./pages/_middleware.ts
[23:00:22.849] 
[23:00:22.849] ./node_modules/get-intrinsic/index.js
[23:00:22.849] Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Middleware pages/_middleware
[23:00:22.849] 
[23:00:22.849] Import trace for requested module:
[23:00:22.849] ./node_modules/call-bind/callBound.js
[23:00:22.849] ./node_modules/which-typed-array/index.js
[23:00:22.849] ./node_modules/util/support/types.js
[23:00:22.849] ./node_modules/util/util.js
[23:00:22.849] ./node_modules/jws/lib/sign-stream.js
[23:00:22.849] ./node_modules/jws/index.js
[23:00:22.850] ./node_modules/jsonwebtoken/decode.js
[23:00:22.850] ./node_modules/jsonwebtoken/index.js
[23:00:22.850] ./pages/_middleware.ts
[23:00:22.850] 
[23:00:22.850] ./node_modules/has/src/index.js
[23:00:22.850] Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Middleware pages/_middleware
[23:00:22.850] 
[23:00:22.850] Import trace for requested module:
[23:00:22.850] ./node_modules/get-intrinsic/index.js
[23:00:22.850] ./node_modules/call-bind/callBound.js
[23:00:22.850] ./node_modules/which-typed-array/index.js
[23:00:22.850] ./node_modules/util/support/types.js
[23:00:22.850] ./node_modules/util/util.js
[23:00:22.850] ./node_modules/jws/lib/sign-stream.js
[23:00:22.850] ./node_modules/jws/index.js
[23:00:22.850] ./node_modules/jsonwebtoken/decode.js
[23:00:22.851] ./node_modules/jsonwebtoken/index.js
[23:00:22.851] ./pages/_middleware.ts
[23:00:22.851] 
[23:00:22.851] ./node_modules/is-generator-function/index.js
[23:00:22.851] Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Middleware pages/_middleware
[23:00:22.851] 
[23:00:22.851] Import trace for requested module:
[23:00:22.851] ./node_modules/util/support/types.js
[23:00:22.852] ./node_modules/util/util.js
[23:00:22.852] ./node_modules/jws/lib/sign-stream.js
[23:00:22.852] ./node_modules/jws/index.js
[23:00:22.852] ./node_modules/jsonwebtoken/decode.js
[23:00:22.852] ./node_modules/jsonwebtoken/index.js
[23:00:22.852] ./pages/_middleware.ts
[23:00:22.853] 
[23:00:22.853] ./node_modules/next/dist/compiled/vm-browserify/index.js
[23:00:22.854] Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Middleware pages/_middleware
[23:00:22.854] 
[23:00:22.854] Import trace for requested module:
[23:00:22.854] ./node_modules/jwa/index.js
[23:00:22.854] ./node_modules/jws/lib/sign-stream.js
[23:00:22.854] ./node_modules/jws/index.js
[23:00:22.854] ./node_modules/jsonwebtoken/decode.js
[23:00:22.855] ./node_modules/jsonwebtoken/index.js
[23:00:22.855] ./pages/_middleware.ts
[23:00:22.855] 
[23:00:22.855] 
[23:00:22.855] > Build failed because of webpack errors
[23:00:22.886] Error: Command "npm run build" exited with 1

4

There are 4 answers

3
Dimitri Borgers On

Using the @tsndr/cloudflare-worker-jwt library instead of jsonwebtoken works.

2
Praveen Thirumurugan On

Since the middleware runtime is based on the browser and not the server, eval is not allowed in that position. Also, the middleware runtime maximum allowed duration is 1.5 seconds. Hence, we can't rely on obtaining the verification from another server from an Edge Function.

I had removed the use of jsonwebtoken library completely and used jose to overcome the issue and keep the codebase simplified. The code works on both browser and server runtimes enabling us to verify the user on the middleware layer itself.

Signing JWT

import * as jose from 'jose';

const jwtToken = await new jose.SignJWT({ userId: `your-data` })
                        .setProtectedHeader({ alg: 'HS256' })
                        .setIssuedAt()
                        .setExpirationTime('30d')
                        .sign(new TextEncoder().encode(`secret-key-phrase`));

Verifying JWT

This works on the middleware without any issues (Edge Function on Vercel).

import * as jose from 'jose';

try {
    const { payload: jwtData } = await jose.jwtVerify(
        jwtToken, new TextEncoder().encode(`secret-key-phrase`)
    );
    // jwtData.uid => `your-data`
} catch (error) {
    console.log(error);
    // JWT validation failed or token is invalid
}
0
Umar On

most native nodejs APIs are not supported

edge runtime

here is offially next js example jwt middleware source this code exapmle use jose jose repo

import { type NextRequest, NextResponse } from 'next/server'
import { verifyAuth } from '@lib/auth'

export const config = {
  matcher: [ '/api/protected', '/protected' ],
}

export async function middleware(req: NextRequest) {
  // validate the user is authenticated
  const verifiedToken = await verifyAuth(req).catch((err) => {
    console.error(err.message)
  })

  if (!verifiedToken) {
    // if this an API request, respond with JSON
    if (req.nextUrl.pathname.startsWith('/api/')) {
      return new NextResponse(
        JSON.stringify({ 'error': { message: 'authentication required' } }),
        { status: 401 });
    }
    // otherwise, redirect to the set token page
    else {
      return NextResponse.redirect(new URL('/', req.url))
    }
  }
}
1
iagowp On

just wanted to add a solution if you don't want to get rid of jsonwebtoken.

You can add this config to your middleware file.

export const config = {
     runtime: "edge",
     unstable_allowDynamic: [
         '**/node_modules/lodash/**/*.js',
    ],
 };

And add whatever other package is calling eval. The current jsonwebtoken version using decode should fix it. As you are using verify, you might need to add other libraries to this unstable_allowDynamic.

Also, the recommended solution, as others mentioned, is probably switching libraries.