在用户注册时设计“确认令牌无效”

在我的Web应用程序上使用Rails 4和Devise 3.1.0。 我编写了一个Cucumber测试来测试用户注册。 当从电子邮件中单击“确认我的帐户”链接时,它失败。

Scenario: User signs up with valid data                                                           # features/users/sign_up.feature:9
    When I sign up with valid user data                                                             # features/step_definitions/user_steps.rb:87
    Then I should receive an email                                                                  # features/step_definitions/email_steps.rb:51
    When I open the email                                                                           # features/step_definitions/email_steps.rb:76
    Then I should see the email delivered from "no-reply@mysite.com"                                # features/step_definitions/email_steps.rb:116
    And I should see "You can confirm your account email through the link below:" in the email body # features/step_definitions/email_steps.rb:108
    When I follow "Confirm my account" in the email                                                 # features/step_definitions/email_steps.rb:178
    Then I should be signed in                                                                      # features/step_definitions/user_steps.rb:142
      expected to find text "Logout" in "...Confirmation token is invalid..." (RSpec::Expectations::ExpectationNotMetError)
     ./features/step_definitions/user_steps.rb:143:in `/^I should be signed in$

当我也通过Web服务器手动注册时,此错误是可重现的,因此它似乎不是Cucumber问题。

我想要:

  • 用户可以通过此电子邮件的链接一键确认其帐户
  • 确认帐户后让用户保持登录状态

我已经设置:

  • 来自GitHub的最新Devise代码(3.1.0,ref 041fcf90807df5efded5fdcd53ced80544e7430f)
  • 实现User.confirm_by_token('[EMAIL_CONFIRMATION_TOKEN]')User
  • 使用“默认”确认控制器(我尚未定义自己的自定义控制器。)

我已阅读以下帖子:

  • 设计确认_令牌无效
  • Devise 3.1:现在具有更安全的默认设置
  • GitHub问题-设计确认_令牌无效

并尝试过:

  • 在我的Devise初始化程序中设置User,这会在启动时引发“未知方法”错误。 另外,听起来这只是临时解决方法,所以我想避免使用它。
  • 清除数据库并从头开始(因此不存在旧令牌)

更新:

注册后检查存储在User上的确认令牌。 电子邮件令牌与数据库令牌匹配。 根据上面的帖子,新的Devise行为表示不应该这样做,而是应该基于电子邮件的令牌生成另一个令牌。 这是可疑的。 运行User.confirm_by_token('[EMAIL_CONFIRMATION_TOKEN]')返回的用户错误集为“ @messages = {:confirmation_token => [“ is invalid”]]}“,这似乎是问题的根源。

令牌不匹配似乎是问题的核心。 在控制台中运行以下代码以手动更改用户的Confirmation_token会使确认成功:

new_token = Devise.token_generator.digest(User, :confirmation_token, '[EMAIL_TOKEN]')
u = User.first
u.confirmation_token = new_token
u.save
User.confirm_by_token('[EMAIL_TOKEN]') # Succeeds

那么,为什么首先将错误的确认令牌保存到数据库呢? 我正在使用自定义注册控制器...也许其中有某些东西导致其设置不正确?

routes.rb

  devise_for  :users,
          :path => '',
          :path_names => {
            :sign_in => 'login',
            :sign_out => 'logout',
            :sign_up => 'register'
            },
          :controllers => {
            :registrations => "users/registrations",
            :sessions => "users/sessions"
          }

users / registrations_controller.rb:

class Users::RegistrationsController < Devise::RegistrationsController

  def create
    # Custom code to fix DateTime issue
    Utils::convert_params_date_select params[:user][:profile_attributes], :birthday, nil, true

    super
  end

  def sign_up_params
    # TODO: Still need to fix this. Strong parameters with nested attributes not working.
    #       Permitting all is a security hazard.
    params.require(:user).permit!
    #params.require(:user).permit(:email, :password, :password_confirmation, :profile_attributes)
  end
  private :sign_up_params
end
3个解决方案
97 votes

因此,升级到Devise 3.1.0会导致一些“麻烦”,而我有一段时间没接触过。

根据此博客文章,您需要将Devise邮件更改为使用app/views/<user>/mailer/confirmation_instructions.html.erb,而不是旧的@resource.confirmation_token

app/views/<user>/mailer/confirmation_instructions.html.erb中找到它,并将其更改为:

<p>Welcome <%= @resource.email %>!</p>
<p>You can confirm your account email through the link below:</p>
<p><%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @token) %></p>

这应该可以解决您遇到的所有基于令牌的确认问题。 这也可能会解决所有解锁或重置密码令牌问题。

David Elner answered 2020-07-23T12:22:18Z
0 votes

我的一个朋友刚刚找到了这个问题,并给我发了电子邮件,问我是否已经解决了这个问题,这使我想起我从未提交自己的答案,所以去了:)

我最终重置了令牌并使用send来获取原始令牌。 这很丑陋,但可以为devise (3.5.1)打一拳。

26   it "should auto create org" do
27     email = FG.generate :email
28     visit new_user_registration_path
29     fill_in :user_name, with: 'Ryan Angilly'
30     fill_in :user_user_provided_email, with: email
31     fill_in :user_password, with: '1234567890'
32 
33     expect do
34       click_button 'Continue'
35     end.to change { Organization.count }.by(1)
36 
37     expect(page.current_path).to eq(confirmation_required_path)
38     u = User.where(email: email).first
39     u.send :generate_confirmation_token
40     email_token = u.instance_variable_get(:@raw_confirmation_token)
41     u.save!
42     os = u.organizations
43     expect(os.size).to eq(1)
44     visit user_confirmation_path(confirmation_token: email_token)
45     o = os.first
46 
47     u.reload
48     expect(u.confirmed?)
49     expect(page.current_url).to eq(organization_getting_started_url(o))
50   end
Ryan Angilly answered 2020-07-23T12:22:42Z
0 votes

从设计3.5.2开始,在确认过程中不再消化确认令牌。 这意味着电子邮件中的令牌将与数据库中的令牌匹配。

解决了这个问题后,我仍然无法确认,但是对于我来说,这是我覆盖find_first_by_auth_conditions时引入的错误。通过修复在该方法中引入的错误,我通过确认修复了错误。

Karen answered 2020-07-23T12:23:07Z
translate from https://stackoverflow.com:/questions/18626544/devise-confirmation-token-is-invalid-when-user-signs-up