有效的 JWT 对 NestJS 守卫无效

Posted

技术标签:

【中文标题】有效的 JWT 对 NestJS 守卫无效【英文标题】:Valid JWT is invalid for nestJS guard 【发布时间】:2021-04-08 01:54:45 【问题描述】:

我通过凭据标头从我的客户端应用程序(带有nextAuth 的nextJS)向我的后端nestJS 应用程序(使用graphQL)传递一个JWT。在我的 nestJS 后端应用程序中,我正在尝试实现身份验证保护,因此我在 jwt.strategy.ts

中使用自定义函数提取 JWT

但是 JwtStrategy 不接受我的有效签名令牌。为了证明 JWT 是有效的,我为令牌放置了一些控制台输出。但是永远不会调用 validate() 函数。我不明白为什么,因为可以使用jwt.verify 验证令牌:

这是我的输出 - 它被jwt.verify()解码:

JWT: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7InVzZXJJZCI6MTIzLCJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaXNBZG1pbiI6dHJ1ZX0sImlhdCI6MTYwOTY3NTc4Nn0.LQy4QSesxJR91PyGGb_0mGZjpw9hlC4q7elIDs2CkLo
Secret: uGEFpuMDDdDQA3vCtZXPKgBYAriWWGrk
Decoded: 
  user:  userId: 123, username: 'username', isAdmin: true ,
  iat: 1609675786

我没有看到,我缺少什么,我什至看不到如何调试它,因为我的 jwt.strategy.ts 中没有输出,并且根本没有调用 validate-function。

jwt.strategy.ts

import jwt from 'jsonwebtoken'
// import  JwtService  from '@nestjs/jwt'
import  Strategy  from 'passport-jwt'
import  PassportStrategy  from '@nestjs/passport'
import  Injectable  from '@nestjs/common'
import cookie from 'cookie'

import  getConfig  from '@myapp/config'
const  secret  = getConfig()

const parseCookie = (cookies) => cookie.parse(cookies || '')

const cookieExtractor = async (req) => 
  let token = null
  if (req?.headers?.cookie) 
    token = parseCookie(req.headers.cookie)['next-auth.session-token']
  

  // output as shown above
  console.log('JWT:', token)
  console.log('Secret:', secret)
  const decoded = await jwt.verify(token, secret,  algorithms: ['HS256'] )
  console.log('Decoded: ', decoded)

  return token


@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) 
  constructor() 
    super(
      jwtFromRequest: cookieExtractor,
      ignoreExpiration: true,
      secretOrKey: secret
    )
  

  async validate(payload: any) 
    console.log('payload:', payload) // is never called

    return  userId: payload.sub, username: payload.username 
  

jwt-auth.guard.ts

import  Injectable, ExecutionContext  from '@nestjs/common'
import  AuthGuard  from '@nestjs/passport'
import  GqlExecutionContext  from '@nestjs/graphql'

@Injectable()
export class GqlAuthGuard extends AuthGuard('jwt') 
  getRequest(context: GqlExecutionContext) 
    const ctx = GqlExecutionContext.create(context)
    return ctx.getContext().req
  

在这个解析器中使用了守卫:

editor.resolver.ts

import  Query, Resolver  from '@nestjs/graphql'
import  UseGuards  from '@nestjs/common'
import  GqlAuthGuard  from '../auth/jwt-auth.guard'

@Resolver('Editor')
export class EditorResolvers 
  constructor(private readonly editorService: EditorService) 

  @UseGuards(GqlAuthGuard)
  @Query(() => [File])
  async getFiles() 
    return this.editorService.getFiles()
  

auth.module.ts

import  Module  from '@nestjs/common'
import  AuthController  from './auth.controller'
import  AuthService  from './auth.service'
import  PassportModule  from '@nestjs/passport'
import  LocalStrategy  from './local.strategy'
import  JwtStrategy  from './jwt.strategy'
import  UsersModule  from '../users/users.module'
import  JwtModule  from '@nestjs/jwt'

import  getConfig  from '@myApp/config'
const  secret  = getConfig()

@Module(
  imports: [
    UsersModule,
    PassportModule.register( defaultStrategy: 'jwt' ),
    JwtModule.register(
      secret,
      verifyOptions:  algorithms: ['HS256'] ,
      signOptions:  expiresIn: '1d' 
    )
  ],
  controllers: [AuthController],
  providers: [AuthService, JwtStrategy, LocalStrategy],
  exports: [AuthService]
)
export class AuthModule 

令牌是在服务器端(nextJS api 页面)创建的:

const encode = async ( secret, token ) => jwt.sign(token, secret,  algorithm: 'HS256' )

【问题讨论】:

【参考方案1】:

我从您的 jwt.strategy.ts 文件中看到与 nestJS 文档示例的 2 个不同之处,您可以更改并试一试。

https://docs.nestjs.com/security/authentication#implementing-passport-jwt

    同步提取器而非异步

默认情况下,passport-jwt 提取器我们可以看到这是一个同步而不是异步,因此您可以尝试更改您的提取器并删除异步,或者在调用它时添加等待。

https://github.com/mikenicholson/passport-jwt/blob/master/lib/extract_jwt.js , 寻找 fromAuthHeaderAsBearerToken 函数。

所以或改变你的

const cookieExtractor = async (req) => 

const cookieExtractor = (req) => 

或者 - 当你调用它时添加等待

jwtFromRequest: await cookieExtractor(),
    调用提取器而不只是传递它

通过 JwtStrategy 构造函数中的文档示例,他们调用了提取器,而不是像你那样传递它

jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),

所以尝试在你的 JwtStrategy 构造函数中调用它

jwtFromRequest: cookieExtractor(),    // (again - take care of sync / async)

【讨论】:

哇,谢谢。前几天没看到谢谢你的好收获。只是第一点做到了。传递函数是可以的,你不必调用它。我只需要删除async

以上是关于有效的 JWT 对 NestJS 守卫无效的主要内容,如果未能解决你的问题,请参考以下文章

我需要帮助来验证 jwt 是不是有效或以其他方式创建一个新的,如果发送的令牌不正确,则在 nestjs 中间件中返回错误

在 NestJs 中何时使用守卫以及何时使用中间件

我在我的 nestJS 应用程序(使用 authGuard)中使用了 passport-jwt 身份验证策略,如何访问我的控制器中的令牌有效负载?

Nestjs Roles Decorator:让用户脱离有效负载数据的疯狂行为

JWT 功能在本地有效,但在 heroku 上无效

身份验证(JWT)在本地有效,但在 Heroku 无效?