ruby-on-railsdevisedevise-confirmable

Devise "Confirmation token is invalid" when user signs up


Using Rails 4 and Devise 3.1.0 on my web app. I wrote a Cucumber test to test user sign up; it fails when the "confirm my account" link is clicked from the e-mail.

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$

This error is reproducible when I sign up manually through the web server as well, so it doesn't appear to be a Cucumber issue.

I would like:

I have setup:

I have read these posts:

And have tried:

Update:

Checking the confirmation token stored on the User after registering. The emails token matches the DBs token. According to the posts above, the new Devise behavior says not supposed to, and that instead it is should generate a second token based on the e-mail's token. This is suspicious. Running User.confirm_by_token('[EMAIL_CONFIRMATION_TOKEN]') returns a User who has errors set "@messages={:confirmation_token=>["is invalid"]}", which appears to be the source of the issue.

Mismatching tokens seems to be the heart of the issue; running the following code in console to manually change the User's confirmation_token causes confirmation to succeed:

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

So why is it saving the wrong confirmation token to the DB in the first place? I am using a custom registration controller... maybe there's something in it that causes it to be set incorrectly?

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

Solution

  • So upgrading to Devise 3.1.0 left some 'cruft' in a view that I hadn't touched in a while.

    According to this blog post, you need to change your Devise mailer to use @token instead of the old @resource.confirmation_token.

    Find this in app/views/<user>/mailer/confirmation_instructions.html.erb and change it to something like:

    <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>
    

    This should fix any token-based confirmation problems you're having. This is likely to fix any unlock or reset password token problems as well.