如何在 Node.js 中正确实现 "forgot/reset password" 功能? (使用一次性令牌)

How to implement "forgot/reset password" feature properly in Node.js? (Using one time token)

我正在使用 NestJs 在 Node.js 应用程序中实现 forgot/reset 密码功能。

这就是大体流程:

  1. 用户在“忘记密码”表单中输入他的电子邮件并提交请求
  2. 服务器生成一个以用户ID为负载的jwt token,然后发送一个以token为link的邮件来重置密码(例如:GET: example.com/reset/generated_jwt_token
  3. 用户从他的电子邮件中单击 link,呈现重置密码页面,他用新密码填写表单并提交以密码作为正文的表单(示例:POST : example.com/reset/generated_jwt_token)
  4. 服务器验证令牌(未过期 + 有效负载中的用户 ID 存在于数据库中)并更新密码。

这种方法的主要问题是可以无限次使用 jwt 令牌来重置密码(直到它在 X 分钟后过期)。

有办法解决吗?有人说将当前密码的哈希值作为有效负载,因为它无论如何都会被更改并保证使用 1 次,但我不喜欢这种方法。

编辑:我遇到的另一种方法是在 jwt 令牌的数据库中创建一个不能多次使用的黑名单集合。或者在redis中同样使用缓存,但是好像扩展性不是很好。

生成令牌后,您可以将其(或嵌入其中的独特内容)保存到该用户下的数据库中。然后,服务器验证令牌:

(1) 当点击重置邮件中的 link 时

(2) 当用户提交重设密码页面时

检查令牌是否与数据库中该用户的令牌相同。

此外,当用户成功更改密码后,从数据库中清除令牌,使其无法再次使用。

我同意@CertainPerformance 的(接受的)回答。

我想补充 - 考虑使用 authentication-flows-js您完全不必担心实施!

它是一个可以回答大多数流程的模块 - 身份验证、注册、忘记密码、更改密码等,它足够安全,因此应用程序可以使用它而不必担心它会被轻易破解。

来自我写的一篇文章:

Forgot Password

The user enters their email in the forgot password form, and clicks submit. The server (AFM) verifies the account exists and is not locked. If it is locked, AFM throws an error. Otherwise, an email is sent to the user with a token. That token is stored in the DB in the same row/document/record of the user.

Read the full article with more explanations here.