当用户注册时devise“确认令牌无效”

在我的web应用程序中使用Rails 4和Devise 3.1.0。 我写了一个黄瓜testing来testing用户注册; 当从电子邮件中点击“确认我的帐户”链接时,它将失败。

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$ 

当我通过networking服务器手动注册时,这个错误是可重现的,所以它似乎不是一个黄瓜问题。

我想要:

  • 用户能够通过这个电子邮件的链接一键确认他们的帐户
  • 让用户在确认帐户后保持login状态

我已经设置:

  • 最新的Devise代码,来自GitHub(3.1.0,ref 041fcf90807df5efded5fdcd53ced80544e7430f)
  • 实现可confirmable User
  • 使用“默认”确认控制器(我还没有定义自己的自定义控件)

我读过这些post:

  • deviseconfirmation_token无效
  • devise3.1:现在使用更安全的默认设置
  • GitHub问题 – 设置confirmation_token无效

并试过:

  • 在我的Devise初始值设定项中设置config.allow_insecure_tokens_lookup = true ,启动时会抛出一个“未知方法”错误。 另外,这听起来像只是一个暂时的解决办法,所以我想避免使用它。
  • 清理我的数据库,并从头开始(所以没有旧的令牌存在)

更新:

在注册后检查存储在User上的确认令牌。 电子邮件令牌与DBs令牌相匹配。 根据上面的post,新的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 

所以升级到Devise 3.1.0就留下了一些“cru </s>”,以为我一时没有碰过。

根据这个博客文章 ,你需要改变你的devise邮件使用@token而不是旧的@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> 

这应该修复您正在使用的任何基于令牌的确认问题。 这可能会解决任何解锁或重置密码令牌问题。

我的一个朋友刚刚发现这个问题,并通过电子邮件发问我是否知道了这一点,这提醒我,我从来没有提交自己的答案,所以在这里:)

我结束了重置令牌&使用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