使用带有动态/用户相关秘密的“nestjs/jwt”签名

Tom*_*wik 7 javascript node.js jwt typescript nestjs

我正在尝试根据尝试登录的用户的秘密创建用户令牌。但是,我想使用分配给数据库内用户对象的秘密,而不是使用环境中的秘密。

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UserService } from '@src/modules/user/services';

@Injectable()
export class AuthService {
  public constructor(private readonly jwtService: JwtService,
                     private readonly userService: UserService) {}

  public async createToken(email: string): Promise<JwtReply> {
    const expiresIn = 60 * 60 * 24;
    const user = await this.userService.user({ where: { email } });
    const accessToken = await this.jwtService.signAsync({ email: user.email },
                                                        /* user.secret ,*/
                                                        { expiresIn });

    return {
      accessToken,
      expiresIn,
    };
  }
}
Run Code Online (Sandbox Code Playgroud)

我是 Nestjs 的新手,也许我错过了一些东西。 node-jsonwebtoken确实在sign(...)函数中提供了必要的参数。nestjs/jwt缺少这个参数(见代码)。您将如何在不使用node-jsonwebtoken或更抽象的问题的情况下解决它:我处理用户机密的方式在这里有意义吗?谢谢。

Kim*_*ern 7

仅使用 nest 还无法做到这一点,JwtModule但您可以轻松地自己实现缺失的部分。

现场演示

编辑 Nest 动态 JWT 秘密

您可以通过调用以下路由来创建令牌:

user1(秘密:'123'):https:
//yw7wz99zv1.sse.codesandbox.io/login/1 user2(秘密:'456'):https : //yw7wz99zv1.sse.codesandbox.io/login/2

然后'/'使用您的令牌调用受保护的路由并接收您的用户:

curl -X GET https://yw7wz99zv1.sse.codesandbox.io/ \
      -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxIiwiaWF0IjoxNTUzNjQwMjc5fQ.E5o3djesqWVHNGe-Hi3KODp0aTiQU9X_H3Murht1R5U'
Run Code Online (Sandbox Code Playgroud)

它是如何工作的?

AuthService我只是使用标准jsonwebtoken库来创建令牌。然后,您可以createToken从登录路由调用:

import * as jwt from 'jsonwebtoken';

export class AuthService {
  constructor(private readonly userService: UserService) {}

  createToken(userId: string) {
    const user = this.userService.getUser(userId);
    return jwt.sign({ userId: user.userId }, user.secret, { expiresIn: 3600 });
  }

  // ...
}
Run Code Online (Sandbox Code Playgroud)

JwtStrategy您使用secretOrKeyProvider而不是secretOrKey它可以异步访问UserService以动态获取用户机密:

export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(
    private readonly authService: AuthService,
    private readonly userService: UserService,
  ) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKeyProvider: (request, jwtToken, done) => {
        const decodedToken: any = jwt.decode(jwtToken);
        const user = this.userService.getUser(decodedToken.userId);
        done(null, user.secret);
      },
    });
  }

  // ...
}
Run Code Online (Sandbox Code Playgroud)

请注意,您传递给JwtModulelike的选项expiresIn不会被使用,而是直接在AuthService. JwtModule不带任何选项导入:

JwtModule.register({})
Run Code Online (Sandbox Code Playgroud)

一般的

我处理用户机密的方式在这里有意义吗?

如果不知道您的确切要求,就很难回答这个问题。我想 jwt 有一些用例具有动态秘密,但是有了它,您将失去 jwt 的一个重要属性:它们是无状态的。这意味着您AuthService可以发出 jwt 令牌,而一些ProductService需要身份验证的令牌可以只信任 jwt(它知道秘密),而无需调用其他服务(即UserService必须查询数据库)。

如果与用户相关的密钥不是硬性要求,请考虑使用 jwt 的kid属性频繁轮换密钥。