在 Meteor 中正确使用 onResetPasswordLink、onEnrollmentLink 和 onEmailVerificationLink 方法

Using onResetPasswordLink, onEnrollmentLink, and onEmailVerificationLink methods properly in Meteor

我想知道是否有人愿意提供一个 meteorpad 或在 Meteor 中正确使用上面列出的方法之一的代码示例(iron:router)。我很难理解这些方法究竟是如何与我的应用程序交互的,而且这些方法似乎足够新,以至于没有太多关于如何正确使用它们的好文档。谢谢!

http://docs.meteor.com/#/full/Accounts-onResetPasswordLink

根据文档:

You can construct your own user interface using the functions below, or use the accounts-ui package to include a turn-key user interface for password-based sign-in.

因此,这些回调用于滚动您自己的自定义解决方案。但是,我建议使用以下软件包之一,accounts-entry 是我的首选解决方案:

我写了这个方法,希望我能给出一个很好的例子来说明如何使用它。

它意味着与 Accounts.sendResetPasswordEmailAccounts.resetPassword (http://docs.meteor.com/#/full/accounts_sendresetpasswordemail and http://docs.meteor.com/#/full/accounts_resetpassword) 结合使用。

基本上,假设您想实施自己的帐户 UI 系统,而不是使用 accounts-ui 包或类似的包。如果你想拥有一个密码重设系统,你需要三样东西:

  1. 发送重设密码的电子邮件的方法link
  2. 了解用户何时单击重置的方法link
  3. 一种实际重置密码的方法

流程应该是这样的:

  1. 用户在您的页面上单击 link,显示 "Reset password"
  2. 你找出是哪个用户(可能是让他们输入他们的电子邮件地址),然后调用 Accounts.sendResetPasswordEmail
  3. 用户在他们刚刚收到的电子邮件中点击重设密码link
  4. 您的应用程序已加载并使用 Accounts.onResetPasswordLink
  5. 注册回调
  6. 调用回调是因为 URL 中有一个带有密码重置令牌的特殊片段
  7. 此回调可以显示一个特殊的 UI 元素,要求用户输入新密码
  8. 应用程序使用令牌和新密码调用 Accounts.resetPassword
  9. 现在用户已经登录并且有了新密码

这有点复杂,因为它是最先进的自定义流程。如果您不想弄乱所有这些回调和方法,我建议使用现有帐户之一 UI 包,例如 accounts-uihttps://atmospherejs.com/ian/accounts-ui-bootstrap-3

有关示例代码,请查看 accounts-ui 包的代码:https://github.com/meteor/meteor/blob/devel/packages/accounts-ui-unstyled/login_buttons_dialogs.js

好的,所以我要 post 我最终在这里学习和做的事情,以便其他人可以将其用作参考。我也会尽力解释正在发生的事情。

从其他评论中可以看出,传递给 Accounts.on****Link 回调的 'done' 函数是让我感到困惑的主要部分。这个函数只做一件事——重新启用自动登录。值得注意的是 'done' function/autoLogin 是核心 'accounts' 包之一的一部分,无法修改。 'autoLogin' 用于一种特定情况:用户 A 尝试在用户 B 当前登录的计算机上重置他或她的密码。如果用户 A 在提交新密码之前退出重置密码流程,则用户 B 将保持登录状态。如果用户 A 完成重置密码流程,则用户 B 将注销,用户 A 将登录。

用于处理 accounts-ui 包中的 'done' 的模式,以及我最终所做的,将 'done' 分配给一个变量,然后该变量可以传递给您的模板事件处理函数,以及 运行 一旦您的重置密码逻辑完成。此变量赋值需要在 Accounts.on****Link 回调中完成,但回调可以放在任何顶级客户端代码中(只需确保分配变量的范围正确)。我只是把它放在我的 reset_password_template.js 文件的开头(到目前为止我只是为了重置密码而这样做,但模式应该是相似的):

client/reset_password_template.js:

// set done as a variable to pass
var doneCallback;

Accounts.onResetPasswordLink(function(token, done) {
  Session.set('resetPasswordToken', token);  // pull token and place in a session variable, so it can be accessed later 
  doneCallback = done;  // Assigning to variable
});

使用这些 on****Link 回调的另一个挑战是了解您的应用 'knows' 回调是如何触发的,以及应用需要完成什么。由于 iron:router 与 Meteor 紧密集成,很容易忘记它是一个单独的包。重要的是要记住这些回调是为了独立于 iron:router 而编写的。这意味着当单击发送到您的电子邮件的 link 时,您的应用程序将在根级别(“/”)加载。

*** 旁注 - Whosebug 上还有一些其他答案提供了与 iron:router 集成的方法,并为每个 link 加载了特定的路由。这些模式对我来说的问题是它们看起来有点老套,并且不符合 'meteor' 方式。更重要的是,如果核心 Meteor 团队决定改变这些注册 link 的路径,这些路径就会中断。我试着打电话 Router.go('path');在 on****Link 回调中,但由于某些原因,这在 Chrome 和 Safari 中不起作用。我很想有一种方法来处理每个通过电子邮件发送的 link 的特定路由,从而消除了不断设置和清除会话变量的需要,但我想不出一个好的解决方案。

无论如何,正如@stubailo 在他的回答中所描述的那样,您的应用程序已加载(在根级别),并且触发了回调。触发回调后,您就设置了会话变量。您可以使用此会话变量使用以下模式在根级别加载适当的模板:

client/home.html(或您的着陆页模板)

{{#unless resetPasswordToken}}
  {{> home_template}}
{{else}}
  {{> reset_password_template}}
{{/unless}}

有了这个,您需要在 reset_password_template.js 文件中处理一些事情,并且 home.js:

client/home.js

// checks if the 'resetPasswordToken' session variable is set and returns helper to home template
Template.home.helpers({
  resetPasswordToken: function() {
    return Session.get('resetPasswordToken');
  }
});

client/reset_password_template.js

// if you have links in your template that navigate to other parts of your app, you need to reset your session variable before navigating away, you also need to call the doneCallback to re-enable autoLogin
Template.reset_password_template.rendered = function() {
  var sessionReset = function() {
    Session.set('resetPasswordToken', '');
    if (doneCallback) {
      doneCallback();
    }    
  }

  $("#link-1").click(function() {
    sessionReset();
  });

  $('#link2').click(function() {
    sessionReset();
  });
}

Template.reset_password_template.events({
  'submit #reset-password-form': function(e) {
    e.preventDefault();

    var new_password = $(e.target).find('#new-password').val(), confirm_password = $(e.target).find('#confirm-password').val();

    // Validate passwords
    if (isNotEmpty(new_password) && areValidPasswords(new_password, confirm_password)) {
      Accounts.resetPassword(Session.get('resetPasswordToken'), new_password, function(error) {
        if (error) {
          if (error.message === 'Token expired [403]') {
            Session.set('alert', 'Sorry, this link has expired.');
          } else {
            Session.set('alert', 'Sorry, there was a problem resetting your password.');          
          }
        } else {
          Session.set('alert', 'Your password has been changed.');  // This doesn't show. Display on next page
          Session.set('resetPasswordToken', '');
          // Call done before navigating away from here
          if (doneCallback) {
            doneCallback();
          }
          Router.go('web-app');
        }
      });
    }

    return false;
  }
});

希望这对尝试建立自己的自定义身份验证表单的其他人有所帮助。其他答案中提到的包在很多情况下都很好,但有时您需要通过包无法获得的额外自定义。

这个问题已经过去一年了,但我刚刚遇到了同样的问题。 按照您的解决方案,我发现您可以在路由器中使用 Session 变量和 onAfterAction 挂钩来实现相同的目的,但使用 routes:

Router.route('/', {
  name: 'homepage',
  action: function() {
    if (Session.get('resetPasswordToken')) {
      this.redirect('resetPassword', {token: Session.get('resetPasswordToken')});
    } else {
      this.render('home');
    }
  }
});

Router.route('/password/reset/:token', {
  name: 'resetPassword',
  action: function () {
    this.render('resetPassword');
  },
  data: function() {
    return {token: this.params.token};
  },
  onAfterAction: function () {
    Session.set('resetPasswordToken', '');
  }
});

当然,您还需要:

Accounts.onResetPasswordLink(function(token, done){
  Session.set('resetPasswordToken', token);
  doneResetPassword = done;
});