You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express TypeScript中间件报错:jwt.verify() is not a function

问题:TypeScript环境下Express JWT中间件报错TypeError: jwt.verify is not a function

原本基于JavaScript开发的Express JWT认证中间件(通过Access/Refresh Token实现用户认证)运行正常,迁移到TypeScript后,出现TypeError: jwt.verify is not a function错误。

相关代码

认证中间件代码

import { UserJWTPayload } from '../interfaces/UserJWTPayload'
import { NextFunction, Request, Response } from 'express'
import { ResponseObject } from '../api/classes/ResponseObject'
import SequelizeApi from '../sequelize/sequelize-api'
import * as jwt from 'jsonwebtoken'

const authenticateUserTokens = async (req: Request, res: Response, next: NextFunction) => {
    try {
        const accessToken: string = req.cookies.access_token
        const refreshToken: string = req.cookies.refresh_token

        if (!accessToken || !refreshToken) return res.status(401).json(new ResponseObject(false, 'not authenticated', null))

        console.log(jwt)
        /*
        [Module: null prototype] {
            decode: [Function (anonymous)],
            default: {
                decode: [Function (anonymous)],
                verify: [Function (anonymous)],
                sign: [Function (anonymous)],
                JsonWebTokenError: [Function: JsonWebTokenError],
                NotBeforeError: [Function: NotBeforeError],
                TokenExpiredError: [Function: TokenExpiredError]
            }
        }
        */

        console.log(jwt.verify())
        /*
            export function verify(token: string, secretOrPublicKey: Secret, options?: VerifyOptions & { complete?: false }): JwtPayload | string;
            An argument for 'token' was not provided 
        */

        console.log(jwt.verify(accessToken, process.env.JWT_ACCESS_TOKEN_PRIVATE_KEY))
        /*
        TypeError: jwt.verify is not a function
            at file:///C:/Users/Jakub/Desktop/Projekty/StoriesBatchApi/src/middlewares/authenticate-user-tokens.ts:16:25
            at Generator.next (<anonymous>)
            at file:///C:/Users/Jakub/Desktop/Projekty/StoriesBatchApi/src/middlewares/authenticate-user-tokens.ts:7:71
            at new Promise (<anonymous>)
            at __awaiter (file:///C:/Users/Jakub/Desktop/Projekty/StoriesBatchApi/src/middlewares/authenticate-user-tokens.ts:3:12)
            at file:///C:/Users/Jakub/Desktop/Projekty/StoriesBatchApi/src/middlewares/authenticate-user-tokens.ts:7:96
            at Layer.handle [as handle_request] (C:\Users\Jakub\Desktop\Projekty\StoriesBatchApi\node_modules\express\lib\router\layer.js:95:5)
            at next (C:\Users\Jakub\Desktop\Projekty\StoriesBatchApi\node_modules\express\lib\router\route.js:144:13)
            at Route.dispatch (C:\Users\Jakub\Desktop\Projekty\StoriesBatchApi\node_modules\express\lib\router\route.js:114:3)
            at Layer.handle [as handle_request] (C:\Users\Jakub\Desktop\Projekty\StoriesBatchApi\node_modules\express\lib\router\layer.js:95:5) 
        */

        const accessTokenData = jwt.verify(accessToken, process.env.JWT_ACCESS_TOKEN_PRIVATE_KEY) as UserJWTPayload

        const refreshTokenFromDatabase = await SequelizeApi.getModel('user_token').findOne({ where: { refresh_token: refreshToken } })

        if (!refreshTokenFromDatabase) return res.status(401).json(new ResponseObject(false, 'invalid refresh token', null))

        jwt.verify(refreshToken, process.env.JWT_REFRESH_TOKEN_PRIVATE_KEY)

        res.locals.id_user = accessTokenData.id_user

        return next()
    } catch (e) {
        console.error(e)
        return res.status(401).json(new ResponseObject(false, 'server error | not authenticated', e.message))
    }
};

export default authenticateUserTokens

受保护路由代码

userRouter.delete('/:id', authenticateUserTokens(), async (req, res) => {
    try {
        const deletedUserId = parseInt(req.params.id)
        const actionByUserId = parseInt(res.locals.id_user)

        if (actionByUserId !== deletedUserId) 
            return res.status(403).json(new ResponseObject(false, 'not authorized', null))

        const arrayOfAffectedRows = await userModel.update({ status: 'deleted' }, {
            where: {
                id: deletedUserId
            }
        })

        if (arrayOfAffectedRows[0] === 1) 
            return res.status(200).json(new ResponseObject(true, 'user deleted', null)) 
        else 
            return res.status(404).json(new ResponseObject(false, 'user not found', null))
    } catch(e) {
        return res.status(500).json(new ResponseObject(false, 'server error', e))
    }
})

tsconfig.json配置

{
    "compilerOptions": {
        "module": "ES6",
        "removeComments": true,
        "target": "es6",
        "rootDir": "./",
        "esModuleInterop": true,
        "moduleResolution":"node",
    },
    "include": [ "src/**/*" ],
    "exclude": [ "node_modules" ]
}

启动命令

nodemon -r dotenv/config --experimental-specifier-resolution=node --esm src/server.ts

问题原因

从控制台打印的jwt模块结构可以明确看到:verify等核心方法实际挂载在jwt.default属性下。这是因为jsonwebtoken是CommonJS模块,在ES模块环境(你的配置中module: ES6且启用--esm)下导入时,CommonJS的默认导出会被包装到ES模块的default属性中。而TypeScript的类型定义让你误以为可以直接调用jwt.verify,但运行时该方法并不存在于顶级模块对象上。

另外,路由代码中authenticateUserTokens()的写法是错误的——中间件不需要立即执行,应该传递函数引用,否则中间件会在路由注册时就执行,而非请求到达时执行。

解决方案

1. 修正JWT导入方式(推荐)

将导入语句改为直接导入默认导出,这样可以直接调用jwt.verify:

import jwt from 'jsonwebtoken'

2. 保留原导入,修改调用方式

如果坚持使用import * as jwt,则所有调用都要通过default属性:

// 替换所有jwt.verify为jwt.default.verify
const accessTokenData = jwt.default.verify(accessToken, process.env.JWT_ACCESS_TOKEN_PRIVATE_KEY) as UserJWTPayload
jwt.default.verify(refreshToken, process.env.JWT_REFRESH_TOKEN_PRIVATE_KEY)

3. 修正路由中间件引用

将路由中的authenticateUserTokens()改为authenticateUserTokens,传递函数引用而非执行结果:

userRouter.delete('/:id', authenticateUserTokens, async (req, res) => {
    // ... 原路由逻辑
})

内容的提问来源于stack exchange,提问作者Jakub

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 13:45:37