RESTful 密码重置

发布于 2024-09-06 18:19:03 字数 345 浏览 6 评论 0原文

构建用于重置密码的 RESTful 资源的正确方法是什么?

此资源旨在为丢失或忘记密码的人提供密码重置器。它会使他们的旧密码失效,并通过电子邮件向他们发送密码。

我有两个选项:

POST /reset_password/{user_name}

或者...

POST /reset_password
   -Username passed through request body

我很确定请求应该是 POST。我对自己选择的名字不太有信心。我不确定 user_name 是否应该通过 URL 或请求正文传递。

What is the proper way to structure a RESTful resource for resetting a password?

This resource is meant to be a password resetter for someone who has lost or forgotten their password. It invalidates their old password and e-mails them a password.

The two options that I have are:

POST /reset_password/{user_name}

or...

POST /reset_password
   -Username passed through request body

I'm pretty sure the request should be a POST. I'm less confident that I have selected an appropriate name. And I'm not sure if the user_name should be passed through the URL or the request body.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

百善笑为先 2024-09-13 18:19:03

未经身份验证的用户

我们在 api/v1/account/password 端点上执行 PUT 请求,并需要一个带有相应帐户电子邮件的参数来标识用户想要访问的帐户重置(更新)密码:

PUT : /api/v1/account/password?email={[email protected]}

注意:正如@DougDomeny在他的评论中提到的那样,将电子邮件作为网址中的查询字符串传递是存在安全风险的。使用 https 时不会公开 GET 参数(对于此类请求,您应该始终使用正确的 https 连接),但还涉及其他安全风险。您可以在 中阅读有关此主题的更多信息这篇博文在这里

在请求正文中传递电子邮件将是比将其作为 GET 参数传递更安全的替代方案:

PUT : /api/v1/account/password

请求正文:

{
    "email": "[email protected]"
}

响应有一个 202 接受 响应含义:

请求已被接受处理,但处理尚未完成。该请求最终可能会或可能不会被执行,因为在实际处理时可能会拒绝该请求。没有从像这样的异步操作重新发送状态代码的工具。

用户将收到一封电子邮件:[电子邮件受保护] 并处理更新请求取决于对电子邮件中的链接采取的操作。

https://example.com/password-reset?token=1234567890

打开此电子邮件中的链接将定向到前端应用程序上的重置密码表单,该表单使用链接中的重置密码令牌作为隐藏输入字段的输入(该令牌是作为查询字符串的链接的一部分)。另一个输入字段允许用户设置新密码。用于确认新密码的第二个输入将用于前端验证(以防止拼写错误)。

注意: 在电子邮件中,我们还可以提到,如果用户没有初始化任何密码重置,他/她可以忽略该电子邮件并继续使用他/她当前的密码正常使用该应用程序

当使用新密码和令牌作为输入提交表单时,将发生重置密码过程。表单数据将再次通过 PUT 请求发送,但这次包括令牌,我们将用新值替换资源密码:

PUT : /api/v1/account/password

请求正文:

{
    "token":"1234567890",
    "new":"password"
}

响应将是 204无内容响应

服务器已满足请求,但不需要返回实体主体,并且可能希望返回更新的元信息。响应可以包含实体标头形式的新的或更新的元信息,如果存在,应该与请求的变体相关联。

经过身份验证的用户

对于想要更改密码的经过身份验证的用户,可以立即执行 PUT 请求,而无需电子邮件(服务器知道我们正在更新密码的帐户)。在这种情况下,表单将提交两个字段:

PUT : /api/v1/account/password

请求正文:

{
    "old":"password",
    "new":"password"
}

Unauthenticated users

We do a PUT request on a api/v1/account/password endpoint and require a parameter with the corresponding account email to identify the account for which the user wants to reset (update) the password:

PUT : /api/v1/account/password?email={[email protected]}

Note: As @DougDomeny mentioned in his comment passing the email as a query string in the url is a security risk. GET parameters are not exposed when using https (and you should always use a proper https connection for such requests) but there are other security risks involved. You can read more on this topic in this blog post here.

Passing the email in the request body would be a more secure alternative to passing it as a GET param:

PUT : /api/v1/account/password

Request body:

{
    "email": "[email protected]"
}

The response has a 202 accepted response meaning:

The request has been accepted for processing, but the processing has not been completed. The request might or might not eventually be acted upon, as it might be disallowed when processing actually takes place. There is no facility for re-sending a status code from an asynchronous operation such as this.

The user will receive an email at [email protected] and processing the update request depends on actions taken with the link from the email.

https://example.com/password-reset?token=1234567890

Opening the link from this email will direct to a reset password form on the front end application that uses the reset password token from the link as input for a hidden input field (the token is part of the link as a query string). Another input field allows the user to set a new password. A second input to confirm the new password will be used for validation on front-end (to prevent typos).

Note: In the email we could also mention that in case the user didn't initialize any password reset he/she can ignore the email and keep using the application normally with his/her current password

When the form is submitted with the new password and the token as inputs the reset password process will take place. The form data will be sent with a PUT request again but this time including the token and we will replace the resource password with a new value:

PUT : /api/v1/account/password

Request body :

{
    "token":"1234567890",
    "new":"password"
}

The response will be a 204 no content response

The server has fulfilled the request but does not need to return an entity-body, and might want to return updated metainformation. The response MAY include new or updated metainformation in the form of entity-headers, which if present SHOULD be associated with the requested variant.

Authenticated users

For authenticated users that want to change their password the PUT request can be performed immediately without the email (the account for which we are updating the password is known to the server). In such case the form will submit two fields:

PUT : /api/v1/account/password

Request body:

{
    "old":"password",
    "new":"password"
}
软的没边 2024-09-13 18:19:03

更新:(下面进一步评论)

我会选择这样的内容:

POST /users/:user_id/reset_password

您有一个用户集合,其中单个用户由 {user_name} 指定。然后,您可以指定要操作的操作,在本例中为 reset_password。这就像说“为 {user_name} 创建 (POST) 一个新的 reset_password 操作”。


以前的答案:

我会采用这样的方法:

PUT /users/:user_id/attributes/password
    -- The "current password" and the "new password" passed through the body

您将有两个集合,一个用户集合和每个用户的属性集合。用户由 :user_id 指定,属性由 password 指定。 PUT 操作更新集合的寻址成员。

UPDATE: (further to comment below)

I would go for something like this:

POST /users/:user_id/reset_password

You have a collection of users, where the single user is specified by the {user_name}. You would then specify the action to operate on, which in this case is reset_password. It is like saying "Create (POST) a new reset_password action for {user_name}".


Previous answer:

I would go for something like this:

PUT /users/:user_id/attributes/password
    -- The "current password" and the "new password" passed through the body

You'd have two collections, a users collection, and an attributes collection for each user. The user is specified by the :user_id and the attribute is specified by password. The PUT operation updates the addressed member of the collection.

泪是无色的血 2024-09-13 18:19:03

让我们先来一下超级 RESTful 吧。为什么不使用密码的 DELETE 操作来触发重置?有道理,不是吗?毕竟,您实际上是在放弃现有密码,转而使用另一个密码。

这意味着你会这样做:

DELETE /users/{user_name}/password

现在,有两个重要的警告:

  1. HTTP DELETE 应该是幂等的(一个奇特的词,表示“多次执行也没什么大不了”)。如果您正在执行标准操作,例如发送“密码重置”电子邮件,那么您将会遇到问题。您可以使用布尔值“Is Reset”标志来解决此问题,标记用户/密码。每次删除时,您都会检查此标志;如果未设置那么您可以重置密码并发送电子邮件。 (请注意,拥有此标志也可能有其他用途。)

  2. 您无法通过表单使用 HTTP DELETE,因此您必须进行 AJAX 调用和/或通过 POST 隧道传输 DELETE。

Let's get uber-RESTful for a second. Why not use the DELETE action for the password to trigger a reset? Makes sense, doesn't it? After all, you're effectively discarding the existing password in favor of another one.

That means you'd do:

DELETE /users/{user_name}/password

Now, two big caveats:

  1. HTTP DELETE is supposed to be idempotent (a fancy word for saying "no big deal if you do it multiple times"). If you're doing the standard stuff like sending out a "Password Reset" email, then you're going to run into problems. You could work around this tagging the user/password with a boolean "Is Reset" flag. On every delete, you check this flag; if it's not set then you can reset the password and send your email. (Note that having this flag might have other uses too.)

  2. You can't use HTTP DELETE through a form, so you'll have to make an AJAX call and/or tunnel the DELETE through the POST.

七色彩虹 2024-09-13 18:19:03

通常,您不想在初始请求时删除或销毁用户的现有密码,因为这可能是由无权访问电子邮件的用户触发的(无意或有意)。相反,请更新用户记录上的重置密码令牌,并将其通过电子邮件中包含的链接发送。单击该链接将确认用户收到令牌并希望更新其密码。理想情况下,这也对时间敏感。

在这种情况下,RESTful 操作将是 POST:触发 PasswordResets 控制器上的创建操作。该操作本身将更新令牌并发送电子邮件。

Often you don't want to delete or destroy the user's existing password on the initial request, as this may have been triggered (unintentionally or intentionally) by a user that does not have access to the email. Instead, update a reset password token on the user record and send that in a link included in an email. Clicking on the link would confirm the user received the token and wished to update their password. Ideally, this would be time sensitive as well.

The RESTful action in this case would be a POST: triggering the create action on the PasswordResets controller. The action itself would update the token and send an email.

森末i 2024-09-13 18:19:03

我实际上是在寻找答案,并不是要提供答案 - 但在 REST 上下文中“reset_password”对我来说听起来是错误的,因为它是一个动词,而不是名词。即使你说你正在做一个“重置动作”名词 - 使用这个理由,所有动词都是名词。

另外,对于搜索相同答案的人来说,可能没有想到您可以通过安全上下文获取用户名,而根本不必通过 url 或正文发送它,这让我感到紧张。

I'm actually looking for an answer, not meaning to provide one - but "reset_password" sounds wrong to me in a REST context because it's a verb, not a noun. Even if you say you're doing a "reset action" noun - using this justification, all verbs are nouns.

Also, it may not have occurred to someone searching for the same answer that you may be able to get the username through the security context, and not have to send it through the url or the body at all, which makes me nervous.

情何以堪。 2024-09-13 18:19:03

需要考虑以下几点:

密码重置不是幂等

的 密码更改会影响用作执行密码的凭据的数据,因此,如果在存储的凭据已更改时简单地逐字重复请求,则可能会使将来的尝试无效。例如,如果使用临时重置令牌来允许更改(在忘记密码的情况下通常如此),则该令牌应在成功更改密码后过期,这再次使复制请求的进一步尝试无效。因此,采用 RESTful 方式更改密码似乎比 PUT 更适合 POST

数据加载中的 ID 或电子邮件可能是多余的

尽管这并不违反 REST 并且可能有一些特殊目的,但通常没有必要为密码重置指定 ID 或电子邮件地址。想一想,为什么要将电子邮件地址作为数据的一部分提供给应该通过某种方式进行身份验证的请求?如果用户只是更改密码,则需要进行身份验证(通过用户名:密码、电子邮件:密码或通过标头提供的访问令牌)。因此,我们可以从该步骤访问他们的帐户。如果他们忘记了密码,他们将获得一个临时重置令牌(通过电子邮件),他们可以专门将其用作执行更改的凭据。在这种情况下,通过令牌进行身份验证应该足以识别他们的帐户。

考虑到上述所有因素,我认为这是 RESTful 密码更改的正确方案:

Method: POST
url: /v1/account/password
Access Token (via headers): pwd_rst_token_b3xSw4hR8nKWE1d4iE2s7JawT8bCMsT1EvUQ94aI
data load: {"password": "This 1s My very New Passw0rd"}

There are a few considerations to take:

Password resets are not idempotent

A password change affects the data used as credentials to perform it, which as a result could invalidate future attempts if the request is simply repeated verbatim while the stored credentials have changed. For instance, if a temporary reset token is used to allow the change, as it is customary in a forgotten password situation, that token should be expired upon successful password change, which again nullifies further attempts at replicating the request. Thus a RESTful approach to a password change seems to be a job better suited for POST than PUT.

ID or e-mail in the data load is probably redundant

Although that's not against REST and may have some special purpose, it is often unnecessary to specify an ID or email address for a password reset. Think about it, why would you provide the email address as part of the data to a request that is supposed to go through authentication one way or another? If the user is simply changing their password they need to authenticate in order to do so (via username:password, email:password, or access token provided via headers). Hence, we have access to their account from that step. If they had forgotten their password, they would've been provided with a temporary reset token (via email) that they can use specifically as credentials to perform the change. And in this case authentication via token should be enough to identify their account.

Taking all of the above into consideration here's what I believe to be the proper scheme to a RESTful password change:

Method: POST
url: /v1/account/password
Access Token (via headers): pwd_rst_token_b3xSw4hR8nKWE1d4iE2s7JawT8bCMsT1EvUQ94aI
data load: {"password": "This 1s My very New Passw0rd"}
冰雪之触 2024-09-13 18:19:03

我认为更好的想法是:

DELETE /api/v1/account/password    - To reset the current password (in case user forget the password)
POST   /api/v1/account/password    - To create new password (if user has reset the password)
PUT    /api/v1/account/{userId}/password    - To update the password (if user knows is old password and new password)

关于提供数据:

  • 重置当前密码

    • 电子邮件应在正文中给出。
  • 创建新密码(重置后)

    • 正文中应提供新密码、激活码和电子邮件 ID。
  • 更新密码(登录用户)

    • 旧密码、新密码应在正文中提及。
    • 参数中的用户 ID。
    • 标头中的身份验证令牌。

I think better idea would be:

DELETE /api/v1/account/password    - To reset the current password (in case user forget the password)
POST   /api/v1/account/password    - To create new password (if user has reset the password)
PUT    /api/v1/account/{userId}/password    - To update the password (if user knows is old password and new password)

Regarding supplying the data:

  • To reset current password

    • email should be given in body.
  • To create new password (after reset)

    • new password, activation code and emailID should be given in body.
  • To update the password (for loggedIn user)

    • old password, new password should be mentioned in body.
    • UserId in Params.
    • Auth Token in the headers.
转身泪倾城 2024-09-13 18:19:03

如果您决定使用 /users/{id}/password 方法,并坚持您的想法,即请求本身就是一种资源,那么我不会有任何更改密码并向他们发送新密码的方法。即 /user-password-request/ 是资源,并且使用 PUT,用户信息应该在正文中。
不过,我不会更改密码,我会向用户发送一封电子邮件,其中包含指向包含 request_guid 的页面的链接,该页面可以与 POST /user/{id}/password/?request_guid= 的请求一起传递xxxxx

这会更改密码,并且不允许有人通过请求更改密码来控制用户。

另外,如果存在未完成的请求,初始 PUT 可能会失败。

I wouldn't have something that changes the password and send them a new one if you decide to use the /users/{id}/password method, and stick to your idea that the request is a resource of its own. ie /user-password-request/ is the resource, and is use PUT, user info should be in the body.
I wouldn't change the password though, Id send an email to the user which contains a link to a page which contains a request_guid, which could be passed along with a request to POST /user/{id}/password/?request_guid=xxxxx

That would change the password, and it doesn't allow someone to hose a user by requesting a password change.

Plus the initial PUT could fail if there is an outstanding request.

绝影如岚 2024-09-13 18:19:03

如果您担心安全性并且不想在 URL 中公开用户的标识符(例如,user_name),您可以考虑另一种方法:

使用唯一的令牌来标识密码重置请求,而不是用户的标识符。当发起密码重置请求时,应生成此令牌并将其安全地存储在服务器上。

向用户发送包含令牌作为 URL 参数的链接的电子邮件,例如:

https://example.com/reset_password?token=abc123

当用户单击链接时,客户端会向服务器发送 GET 请求,其中 URL 参数中包含令牌。

服务器验证令牌并返回一个页面,用户可以在其中输入新密码。

当用户提交新密码时,客户端向服务器发送 POST 请求,并在请求正文中包含新密码和令牌。

服务器验证令牌并更新用户的密码。

此方法的优点是不会在 URL 中暴露用户的标识符,这出于安全原因很有用。但是,它需要额外的步骤来生成安全令牌并将其存储在服务器上,并在用户提交新密码时验证服务器上的令牌。

If you are concerned about security and prefer not to expose the user's identifier (e.g., user_name) in the URL, you could consider an alternative approach:

Use a unique token to identify the password reset request, rather than the user's identifier. This token should be generated and stored securely on the server when the password reset request is initiated.

Send an email to the user with a link that includes the token as a URL parameter, for example:

https://example.com/reset_password?token=abc123

When the user clicks the link, the client sends a GET request to the server with the token in the URL parameter.

The server validates the token and returns a page where the user can enter a new password.

When the user submits the new password, the client sends a POST request to the server with the new password and the token in the request body.

The server validates the token and updates the user's password.

This approach has the advantage of not exposing the user's identifier in the URL, which can be useful for security reasons. However, it requires additional steps to generate and store a secure token on the server, and to validate the token on the server when the user submits the new password.

别忘他 2024-09-13 18:19:03

我们更新登录用户密码
PUT /v1/users/password - 使用 AccessToken 识别用户 ID。

交换用户 ID 并不安全。 Restful API 必须使用 HTTP 标头中收到的 AccessToken 来识别用户。

spring-boot 中的示例

@putMapping(value="/v1/users/password")
public ResponseEntity<String> updatePassword(@RequestHeader(value="Authorization") String token){
/* find User Using token */
/* Update Password*?
/* Return 200 */
}

We Update logged user Password
PUT /v1/users/password - identify the user id using AccessToken.

It is not secure to exchange user id. The Restful API must identify the user using AccessToken received in HTTP header.

Example in spring-boot

@putMapping(value="/v1/users/password")
public ResponseEntity<String> updatePassword(@RequestHeader(value="Authorization") String token){
/* find User Using token */
/* Update Password*?
/* Return 200 */
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文