RESTful password reset

2019-01-20 23:21发布

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.

9条回答
做自己的国王
2楼-- · 2019-01-20 23:51

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.

查看更多
手持菜刀,她持情操
3楼-- · 2019-01-20 23:55

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 */
}
查看更多
Bombasti
4楼-- · 2019-01-21 00:00

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.

URLs with ID placeholders are not RESTful

A resource that is specified as /password-reset/{user_id} implies that the actual url pointing to the resource needs to be built with out-of-band information (that is we need to have previous knowledge of user_id), not acquired through discovery. It further presumes that we'll have access to that information at request time, which in many situations won't be the case.

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:

User knows their password:

Method: POST
url: /v1/account/password
Auth (via headers): joe@example.com:my 0ld pa55wOrd
data load: {"password": "This 1s My very New Passw0rd"}

User doesn't know their password (password reset via email):

Method: POST
url: /v1/account/password
Access Token (via headers): pwd_rst_token_b3xSw4hR8nKWE1d4iE2s7JawT8bCMsT1EvUQ94aI
data load: {"password": "This 1s My very New Passw0rd"}
查看更多
一纸荒年 Trace。
5楼-- · 2019-01-21 00:03

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@example.com}

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 body would be a more secure alternative to passing it as a GET param:

PUT : /api/v1/account/password

data : {
    "email": "email@example.com"
}

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@example.com 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).

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

data : {
    "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

data : {
    "old":"password",
    "new":"password"
}
查看更多
狗以群分
6楼-- · 2019-01-21 00:04

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.
查看更多
Viruses.
7楼-- · 2019-01-21 00:05

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.

查看更多
登录 后发表回答