Recently we updated our online retrospective tool from Rails 3 to Rails 4.
We got some issues related to the Devise gem, so I'm going to share with you what we did to fix those issues with Devise 3.1.
In this current version of Devise more secure defaults have been included, more info about that here:
http://blog.plataformatec.com.br/2013/08/devise-3-1-now-with-more-secure-defaults/
One of the common issues that arise when you update to the latest version is that confirmation emails don't work, to fix this issue you will have to update the Devise mailers using an extra param.
The following includes both versions of the code, what you see before updating and what you get after updating:
<p>Thank you for registering....</p>
<p>
Please click on the link below to confirm that this email account belongs to you.<br>
-(before)<%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>
+(after)<%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @token) %>
</p>
<p>
If the link above doesn't work, you can copy the following URL and paste it in your browser:<br>
-(before)<%= confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>
+(after)<%= confirmation_url(@resource, :confirmation_token => @token) %>
</p>
<p>This email will self-destruct in 1 day!</p>
If you want to sign in automatically after confirming your email you need to paste this in your config/initializers/devise.rb file (this is only recommended as a temporary solution):
config.allow_insecure_sign_in_after_confirmation = true
If you are already using Cucumber for testing then you can use it to test your email confirmation as well.
When /^I confirm my email$/ do
# after updating Devise 3.1 version the token generator is now encrypted
# and we need to generate a new token to be able to confirm the email successfully
token_email, token = Devise.token_generator.generate(User, :confirmation_token)
User.last.update_attributes(confirmation_token: token)
step 'I wait "1" seconds'
visit root_path + "users/confirmation?confirmation_token=#{token_email}"
end
In the following code we are assuming the user is the Devise model.
The content belongs to the app/models/user.rb file
We have to remove
:token_authenticatable
From
devise :omniauthable, :trackable, :token_authenticatable,
Don't forget adding the callback which generates the user's authentication_token:
before_save :ensure_authentication_token
The method's content is:
def ensure_authentication_token
if authentication_token.blank?
self.authentication_token = generate_authentication_token
end
end
private
def generate_authentication_token
loop do
token = Devise.friendly_token
break token unless User.where(authentication_token: token).first
end
end
For auth validation add this content in the app/controllers/application_controller.rb file:
# callback
before_filter :authenticate_user_from_token!
# The method's content:
def authenticate_user_from_token!
user_email = params[:user_email].presence
user = user_email && User.find_by_email(user_email)
if user && Devise.secure_compare(user.authentication_token, params[:user_token])
# if you want to validate in every request add sign_in user, store: false
sign_in user
end
end
Now, you might get the following warning message:
"DEPRECATION WARNING: devise :token_authenticatable is deprecated. Please check Devise 3.1 release notes for more information on how to upgrade"
If you have followed the instructions until this point, then you can get rid of that message by refreshing your browser or running your test suite. Congratulations, you are now using more secure defaults with the Devise gem 🙂
See you around
H.