使用访问令牌重设环回密码

我正在使用Loopback作为框架的项目,并包括用户和身份validation。 我添加了一个密码重置路由生成并发送到一个电子邮件,一切似乎工作正常。 最近,我发现密码重置似乎没有工作。 这里重置密码的过程是:

  • 为用户调用密码重置方法
  • 从重置事件发送电子邮件,包括用户ID和访问令牌
  • 从重置链接,将$ http.defaults.headers.common.authorization设置为传递的标记
  • 调用user.prototype $ updateAttributes(由lb-ng生成)以基于表单更新密码属性

预期的行为是在密码重置表单上更新密码。 相反,我得到一个授权错误401或500(似乎来回)。 我注意到,在发送到API的实际标题中,授权令牌与我从路由传递的内容不匹配。 尝试使用LoopBackAUth.setUser设置它不起作用,并且在实际发送请求之前也不更新授权属性。

当我第一次join时,我肯定花了时间testing这个,我不知道会有什么改变来打破这个。 我一直在使用loopback-faq-user-management的例子,但是在这个例子中,我们有一个Angular前端,而不是服务器端的视图。

编辑:

我试图完全打开ACL以查看是否可以更新我的用户对象(从用户inheritance,但是它自己的types)的密码(或任何属性)。 尝试这样做时,我仍然得到一个401。

编辑#2:

这里是我的ACL和示例代码,我如何调用这个。

模型定义的ACL

... { "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; } ... 

找出问题所在。 在我们的应用服务器中,我们没有使用Loopback的令牌中间件。 添加app.use(loopback.token()); 在启动服务器之前,会导致在重置链接中提供的访问令牌按预期工作!

@OverlappingElvis把我放在正确的轨道上。 对于其他人来说,这是一个更完整的答案。 环回文档在这方面相当有限。

确保您的电子邮件中包含用户标识和标记,并将这些标记填写到表单中。

从表单下面的代码完成这个工作:

  function resetPassword(id, token, password) { $http.defaults.headers.common.authorization = token; return User .prototype$updateAttributes({id:id}, { password: password }) .$promise; } 

尽pipe上面的所有答案都将被certificate是有帮助的,但请注意,LoopBack在validation过程中销毁了一个令牌 ,certificate它是无效的。 令牌将会消失。 因此,当您通过401解决scheme工作时,请确保每次尝试新的代码迭代时都要创build一个新的密码重置令牌。

否则,您可能会发现自己正在查看完全健康的代码来更改密码,但是在之前的代码迭代中已经删除了一个代码,从而导致您错误地得出结论:当您看到另一个代码时需要处理代码。

在我的特殊情况下,访问令牌存储在SQL Server数据库中,并且由于引入了时区问题,令牌将始终立即过期,因为我将options.useUTC设置为false。 这导致所有新的令牌在过去90000秒内比密码重置令牌有效的时间是7200秒。 我没有注意到这些令牌立即被销毁,并得出结论,当我看到401代码时,我的代码仍然有问题。 事实上,401是由于使用服务器上已经存在的令牌而导致的。

这是比它应该更复杂的方式。 这是我的完整解决scheme:

1)我在服务器端公开了从令牌更新密码的新方法。

 Member.updatePasswordFromToken = (accessToken, __, newPassword, cb) => { const buildError = (code, error) => { const err = new Error(error); err.statusCode = 400; err.code = code; return err; }; if (!accessToken) { cb(buildError('INVALID_TOKEN', 'token is null')); return; } Member.findById(accessToken.userId, function (err, user) { if (err) { cb(buildError('INVALID_USER', err)); return; }; user.updateAttribute('password', newPassword, function (err, user) { if (err) { cb(buildError('INVALID_OPERATION', err)); return; } // successful, // notify that everything is OK! cb(null, null); }); }); } 

我也定义了可访问性:

 Member.remoteMethod('updatePasswordFromToken', { isStatic: true, accepts: [ { arg: 'accessToken', type: 'object', http: function(ctx) { return ctx.req.accessToken; } }, {arg: 'access_token', type: 'string', required: true, 'http': { source: 'query' }}, {arg: 'newPassword', type: 'string', required: true}, ], http: {path: '/update-password-from-token', verb: 'post'}, returns: {type: 'boolean', arg: 'passwordChanged'} }); 

从客户端,我只是这样称呼它:

 this.memberApi.updatePasswordFromToken(token, newPassword);