Rails devise sending emails to wrong email address - ruby-on-rails

I successfully installed devise in my rails app and the user registration works perfect. I've also set it up such that users can confirm their accounts by sending an email. This works fine when the user signs up for the first time (They get the confirmation message with a confirmation link).
However, when the user changes his/her email address from exampleuser#gmail.com to exampleuser#hotmail.com, the mail gets delivered to exampleuser#gmail.com and the confirmation link has no confirmation token it looks like
http://{HOST}/users/confirmation
Instead of the normal
http://{HOST}/users/confirmation?confirmation_token=TOKEN_HERE
When I resave the new email exampleuser#hotmail.com it now gets delivered to this address but the confirmation token is invalid as it does not match the one in the db.
I don't know what went wrong.
confirmation_instructions.html.erb
<p>Welcome <%= #resource.unconfirmed_email? ? #resource.unconfirmed_email : #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 => #resource.confirmation_token) %></p>
I also have config.reconfirmable = true in devise initializer
Am also using sideqik for delayed jobs. The emails are all processed by sideqik
Any help?
Thanks

I realise it is sometime since you posted, but I have run into this same issue and resolved it.
In my case I had upgraded devise from v2.0.4 to v2.2.6 - which appears to be the newest version that supports Rails 3.
I'd skimmed the change log which mentions this change in v2.2.0:
All mailer methods now expect a second argument with delivery options.
Unfortunately it doesn't say what that second argument is specifically used for; however it turns out is literally what you'd expect... The options hash that is passed to your Mailers mail method.
I'm guessing if you're like me, your previous Devise::Mailer had a line something like the following:
def confirmation_instructions(record, opts)
mail :to => record.email, :template_name => 'confirmation_instructions'
end
The problem is that email is the previous confirmed email address, not the new unconfirmed one. Hence you probably want to call mail with the options hash you were passed, which will now contain the unconfirmed_email in its :to key, e.g.
{:to => "unconfirmed#email.com"}
So simply change your mail calls to something more like:
def confirmation_instructions(record, opts)
mail opts.merge(:template_name => 'confirmation_instructions')
end

Related

Devise locale customisation for passwords renewal

I am trying to customise my errors messages in Devise for the password renewal when the user has forgotten their password. I did all translations and customisations right for registrations but for passwords I am a bit struggling.
In my passwords/new view I have the usual helper call :
<%= devise_error_messages! %>
But the translation that is fetched from the locale file is this one :
translation missing: fr.errors.messages.not_saved
This is a bit strange as this message is the same used for registrations and is set with "Your account has not been saved" at the moment in my app. But in this case this is not about saving an account but rather confirming a renewal email has been sent.
(of course, on top of this, I have an error message related to the email field which shows correctly)
Is it tweakable ?
EDIT EDIT
Ok it seems it is coming from this bit of code from Devise error helper :
sentence = I18n.t("errors.messages.not_saved",
:count => resource.errors.count,
:resource => resource.class.model_name.human.downcase)
https://github.com/plataformatec/devise/blob/master/app/helpers/devise_helper.rb
Any idea I could tweak this bit of code so that it is different for each of Devise subcontrollers inside the Devise namespace ?

Invitation token not valid

Suddenly the invitation tokens of Devise invitable are not valid anymore.
I am sending emails containing a link link this:
<url>/users/invitation/accept?invitation_token=Yr4PWJQNTTu1xP8sU5q5
The link in the email view is like this:
= link_to "accept invitation", accept_invitation_url(#user, :invitation_token => #user.invitation_token)
But I see this error:
The invitation token provided is not valid!
What is going wrong here? It worked until recently and to my knowledge nothing has changed in this area.
Edit: when using :invitation_token => #token or :invitation_token => #user.raw_invitation_token, it shows an URL without invitation_token in the email:
<url>/users/invitation/accept
I'm using:
devise (3.5.2)
devise_invitable (1.5.5)

Creating confirmation link for Devise

Sometimes our users' confirmation emails get hung up, and I need to generate a confirmation link to send to them manually. I grabbed the code from Devise's mail view, but the link it generates is not the same that gets generated by the auto-generated confirmation email:
Code from Devise's confirmation mailer view:
<p><%= link_to 'Confirm my account', confirmation_url(#resource, :confirmation_token => #token) %></p>
Example confirmation link:
http://myapp.com/confirmation?confirmation_token=dTDYagcDbfJehEJPThRi
Code I'm using in custom confirmation link generator:
<p><%= link_to 'Confirm my account', confirmation_url(#user, :confirmation_token => #user.confirmation_token) %></p>
Example confirmation link (Different from above- doesn't work):
http://myapp.com/confirmation?confirmation_token=162baabc80329f01209297af8c49a42e1fdf9066ffef412322b509bc5967052d
How can I generate a Devise confirmation link?
This is because devise relatively recently (3.1+ I think?) increased security by encrypting tokens (including the confirmation token) before storing them in the database. So the second long token is the encrypted version of the first, shorter token and therefore won't work. The only place the confirmation token exists in unencrypted form is in the original email sent to the user.
This means that a new token needs to be generated each time a confirmation email is sent for a user. Devise can allow users to request another confirmation email (ConfirmationsController) - have you disabled that? It calls the send_confirmation_instructions class method on your user class (which is in devise's Confirmable module and in turn calls ends up calling resend_confirmation_instructions, which calls the send_confirmation_instructions instance method which can generate a new token). You could probably call the send_confirmation_instructions class method on your user class yourself, but it would be easier to allow users to request another confirmation email themselves using the standard devise ConfirmationsController and routes/views.
Alternative if you just want to REDIRECT the user after clicking the confirmation, just
STEP 1
override the after_confirmation_path_for in your confirmations_controller:
Create a new confirmations_controller.rb in app/controllers directory:
class ConfirmationsController < Devise::ConfirmationsController
private
def after_confirmation_path_for(resource_name, resource)
your_new_after_confirmation_path
end
end
STEP 2 In config/routes.rb, add this line so that Devise will use your custom ConfirmationsController. This assumes Devise operates on users table (you may edit to match yours).
devise_for :users, controllers: { confirmations: 'confirmations' }
STEP 3 Restart the web server

Rails 4 + Devise: Password Reset is always giving a "Token is invalid" error on the production server, but works fine locally.

I have a Rails 4 application set up to use Devise, and I'm running a problem with password resets. I have the mailer set up, and the password reset email sends fine. The link provided has the correct reset_password_token assigned to it, which I checked with that database. However, when I submit the form with correctly formatted passwords, it gives an error saying that the reset token is invalid.
However, the exact same code works fine locally through rails s. The email sends, and I can actually reset the password. The code I use is just the standard Devise code, I haven't overridden any of it.
Perhaps it's something with Apache? I'm not too familiar with it. Does anyone have any ideas?
Check the code in app/views/devise/mailer/reset_password_instructions.html.erb
The link should be generated with:
edit_password_url(#resource, :reset_password_token => #token)
If your view still uses this code, that will be the cause of the issue:
edit_password_url(#resource, :reset_password_token => #resource.password_reset_token)
Devise started storing hashes of the token, so the email needs to create the link using the real token (#token) rather than the hashed value stored in the database.
This change occurred in Devise in 143794d701
In addition to doctororange's fix, if you're overwriting resource.find_first_by_auth_conditions, you need to account for the case where warden_conditions contains a reset_password_token instead of an email or username.
EDIT: To elaborate:
Devise adds functionality to your model when you say 'devise :registerable, :trackable, ...'.
In your User model (or Admin, etc), you can overwrite the Devise method named find_first_by_auth_conditions. This special method is used by the Devise logic to locate the record that is attempting to be logged in to. Devise passes in some info in a parameter called warden_conditions. This will contain an email, a user-name, or a reset_password_token, or anything else you add to your devise log-in form (such as an account-id).
For example, you might have something that looks like this:
(app/models/user.rb)
class User
...
def self.find_first_by_auth_conditions warden_conditions
conditions = warden_conditions.dup
if (email = conditions.delete(:email)).present?
where(email: email.downcase).first
end
end
end
However, The above code will break the password-reset functionality, because devise is using a token to locate the record. The user doesn't enter an email, they enter the token via a query-string in the URL, which gets passed to this method to try and find the record.
Therefore, when you overwrite this special method you need to make it more robust to account for the password-reset case:
(app/models/user.rb)
class User
...
def self.find_first_by_auth_conditions warden_conditions
conditions = warden_conditions.dup
if (email = conditions.delete(:email)).present?
where(email: email.downcase).first
elsif conditions.has_key?(:reset_password_token)
where(reset_password_token: conditions[:reset_password_token]).first
end
end
end
If you are taking the URL from a log, it can appear like this:
web_1 | <p><a href=3D"http://localhost:3000/admin/password/edit?reset_password_to=
web_1 | ken=3DJ5Z5g6QNVQb3ZXkiKjTx">Change password</a></p>
In this case, using 3DJ5Z5g6QNVQb3ZXkiKjTx as the token will not work because =3D is really an = character encoded.
In this case, you need to use J5Z5g6QNVQb3ZXkiKjTx (with 3D removed)
Although the accepted answer is correct, wanted to explain why this is happening so you can use it in some other cases as well.
If you take a look at the method which is generating the password reset token:
def set_reset_password_token
raw, enc = Devise.token_generator.generate(self.class, :reset_password_token)
self.reset_password_token = enc
self.reset_password_sent_at = Time.now.utc
self.save(validate: false)
raw
end
You will see that the raw is being returned, and the enc is being saved in the database. If you are using the value from the database - enc to put into a password_reset_token in a hidden field of your form, then it will always say Token invalid as that is encrypted token. The one which you should use is the raw token.
This was done because in case some admin (or a hacker) can access the database, the admin could easily reset anyone's password by just using encrypted token, which is tried to be avoided.
Some information about this and some other changes in Devise can be found in the devise's change-log blog post or in the devise's issue discussion
It may also be worth noting (in addition to #doctororange's post ablve) the following if you are using a custom confirmation mailer view.
The link in the view has also changed here. This is the NEW link code:
<p><%= link_to 'Confirm my account', confirmation_url(#resource, confirmation_token: #token) %></p>
This is the OLD link code:
<p><%= link_to 'Confirm my account', user_confirmation_url(#resource, :confirmation_token => #resource.confirmation_token) %></p>

Rails 3 w/ Devise: How to show message on all pages if user hasn't confirmed email address?

I am developing a Rails 3 app using the Devise gem for authentication. I'm also using the confirmable module of Devise to send emails to users when they sign up, asking them to confirm their email address.
I am allowing users to sign in even if they didn't confirm their email address (for a maximum of 20 days), however I want to display a message at the top of every page reminding them they didn't confirm their email address and that they can still login without doing so for X number of days.
Any ideas of how I should approach this? (i.e. any useful gems or tips)
Thanks very much !!!
Looking at the docs here: http://rubydoc.info/github/plataformatec/devise/master/Devise/Models/Confirmable
It seems there is a confirmed? method that you can call on the User object to see if they are confirmed or not.
So I would just put a check for the confirmation in your views/layouts/application.html.erb file:
<% if user_signed_in? && !current_user.confirmed? %>
<div>
Please confirm your account by clicking the link in the email we sent to <%= current_user.email %>
</div>
<% end %>

Resources