Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reset Loopback Password with Access Token

I'm working on a project that uses Loopback as a framework, and includes users and authentication. I added a password reset route generated and sent in an email, and everything seemed to be working correctly. Recently, I discovered that the password reset does not appear to be working. The process for resetting the password here is:

  • Call password reset method for user
  • Send email from reset event, including user ID and access token
  • From reset link, set $http.defaults.headers.common.authorization to the passed token
  • Call user.prototype$updateAttributes (generated by lb-ng) to update password attribute based on a form

The expected behavior is that the password would be updated on the password reset form. Instead, I get an authorization error as either a 401 or a 500 (seems to go back and forth). I notice that in the actual headers sent to the API, the authorization token does not match what I'm passing from the route. Trying to set it using LoopBackAUth.setUser doesn't work, and neither doesn't updating the authorization property before actually sending the request.

I definitely spent time testing this when it was first added, and I can't figure out what would have changed to break this. I've been following the example from loopback-faq-user-management, but we have an Angular front-end instead of the server side views in that example.

Edit:

I tried opening up the ACLs completely to see if I could update the password (or any properties) of my user object (which inherits from User, but is its own type). I'm still getting a 401 when trying to do this.

Edit #2:

Here are my ACLs and sample code for how I'm calling this.

ACLs from model definition

...
{
    "accessType": "*",
    "principalType": "ROLE",
    "principalId": "$owner",
    "permission": "ALLOW"
},
{
    "accessType": "EXECUTE",
    "principalType": "ROLE",
    "principalId": "$owner",
    "permission": "ALLOW",
    "property": "updateAttributes"
}
...

auth.js

...
resetPassword: function(user) {
    return MyUser.prototype$updateAttributes(user, user).$promise;
}
...
like image 383
OverlappingElvis Avatar asked Dec 19 '22 23:12

OverlappingElvis


2 Answers

Figured out what the issue was. In our app's server, we were not using Loopback's token middleware. Adding app.use(loopback.token()); before starting the server causes the access token provided in the reset link to work as expected!

like image 133
OverlappingElvis Avatar answered Dec 21 '22 12:12

OverlappingElvis


While all of the above answers will prove to be helpful, be aware that Loopback destroys a token during validation when it proved it to be invalid . The token will be gone. So when you're working your way through a solution for the 401's, make sure you're creating a new password reset token each time you try a new iteration of your code.

Otherwise you might find yourself looking at perfectly healthy code to change a password, but with a token that's already deleted in a previous iteration of your code, leading you to the false conclusion that you need to work on your code when you see another 401.

In my particular case the access tokens are stored in a SQL Server database and the token would always be immediately expired due to a timezone problem that was introduced, because I had options.useUTC set to false. That cause all newly tokens to be 7200 seconds in the past which is more than the 900 seconds than the password reset tokens are valid. I failed to notice that those tokens were immediately destroyed and concluded I had still problems with my code as I saw 401's in return. Where in fact the 401 was caused by using a token that was already gone on the server.

like image 44
Christiaan Westerbeek Avatar answered Dec 21 '22 12:12

Christiaan Westerbeek