...
Currently the reason
field in ErrorResponse
is reserved for a human-readable error message. We should also introduce an additonal additional field errorCode
that defines an Enum
which the clients can use to decide how it would like to handle the error.
For example, in the case of the password change required:
Code Block |
---|
HTTP ERROR 401 { "reason":"You must first change your password!" "errorCode": "PASSWORD_CHANGE_REQUIIREDREQUIRED" } |
- User logs in via the
POST /login
API with a username/password - Check that the user's password is not in our list of known, common passwords.
- If it is, still return with HTTP 401 Unauthourized Unauthorized and respond with an
ErrorResponse
object that contains anerrorCode.
- clientsClients, upon seeing the
errorCode
, should redirect to page for changing password.
Resets should be done via the EmailResetChangePassword
option instead of oldpassword/newpassword so that the old, weak password can't be used to change the account password
API Changes
Currently, our POST /user/password
takes
Code Block |
---|
{
"sessionToken": "<user session token>",
"password": "myNewPassword"
} |
...
EmailResetChangePassword
for changing passwords via email. This would use a signed token to authorize the password change
...
.
OWASP recommends that the token be invalid after immediately after being used.
OWASP cheat sheet also recommended that during the password reset process, we should not indicate for which account the token will perform a password reset. However, we determined that this sacrifices usability (users not knowing for which account they have reset password) while providing minimal security benefits. Since we already have a "People Search" feature, it is implied that usernames and consequently userIds are public information.
If using a database for the token, only the hash of the password reset tokens should be stored. This is important should an attacker gain read-only access to the database via some mechanism such as SQL injection(we should be sufficiently secure against this specific case), they would be able to change any valid existing tokens.
Another option is to use a Password token mechanism similar to Django's PasswordResetTokenGenerator, which avoids storing any database information because the token uses a HMAC(userId, hashed password, last login timestamp, current timestamp). The information sent over email is userId + current timestamp + HMAC. This link provides a pretty good explanation of how it works.
Code Block |
---|
{ "userId": <random int> "expiresOnauthorization": 12345678, "signature": "<HMAC signature>" }<UUID4 TOKEN>, "newPassword": "hunter3" } |
Email will be sent to user on successful password change regardless of which path the password change occurred.