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
Related
I am using the devise for registration, login and confirm email, and here Devise is taking care of everything. Now if i click on link on email devise authenticating my email and now i am able to login.And i am sending small code to mobiles through sms. And my question is there any possibility to authenticate using that code and give the access to login to that particular user.I want to give both access to the user.like email and phone.
As far as I remember Devise doesn't provide this functionality out of the box. But this is what you can do:
Add SMS confirmation code generator to your
app/controllers/confirmations_controller.rb (class
ConfirmationsController < Devise::ConfirmationsController) create
method;
Modify confirmation email in your
app/views/devise/mailer/confirmation_instructions.html.erb to
include SMS confirmation code and instructions how to use it;
Add SMS confirmation page to your application;
If you want to authenticate using two fields for example say using email and phone_code
ovverride
find_for_database_authentication
method in devise model like this
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
login = conditions.delete(:login)
where(conditions).where(["phone_code = :value OR lower(email) = :value", { value: login.strip.downcase }]).first
end
Override the devise views, and replace
<%= f.text_field :email %>
with
<%= f.text_field :login %>
Using this, one can login with either email and phone_no.
But in this case also you must confirm email first.
you can skip email confirmation by removing confirmable module inclusion from devise model which is not recommended.
We have a user whose mail provider seems to be blocking the account from which we send the password reset emails.
I wanted to just get the reset-password-URL running from irb, and mail it by hand. I can't seem to figure out how to run this thing "edit_password_url" or where it lives or in what scope it is defined.
Any tips on how to generate a reset-password url for a user by hand in irb?
You can do it through the console with a little bit of work. Here is how I approached it:
start rails console in your terminal:
$rails c
I looked at the devise mailer view to see what it was calling to create the reset password URL:
<p><%= link_to 'Change my password', edit_password_url(#resource, :reset_password_token => #token) %></p>
The #resource in this code is your user, and the #token is their reset password token
Find your user by id, email or whatever. Then find their reset password token:
u = User.find(1)
token = u.reset_password_token
To get access to the view you will need to create an instance of ActionView::Base
view = ActionView::Base.new
I then tried to access the url helper, but devise complains about
NoMethodError: undefined method `main_app' for #<ActionView::Base>
So I had to type a method into the console to fix that error (see this):
def main_app
Rails.application.class.routes.url_helpers
end
Depending on whether you have your mailer configured correctly in the rails console environment you are in, you may get some errors about the :host param not being set up. To avoid this you could just call _path instead of _url. Now you can call the url helper and pass the variables you set for user and token:
edit_password_path(u, :reset_password_token => token)
=> /users/password/edit?reset_password_token=123
The short answer is that you need to find their reset_password_token and append it to this URL:
http://yourdomain.com/users/password/edit?reset_password_token=<password-token-here>
In Devise 3.3, I did the following:
$ bin/rails c
> include Devise::Controllers::UrlHelpers
I recently found myself in a similar spam-folder situation.
This snippet worked nicely for me with rails 4, devise 4.3.
It also sends the reset password email, but for the sake of simplicity I think that's fine.
user = User.find(123)
token = user.send_reset_password_instructions
Rails
.application
.routes
.url_helpers
.edit_user_password_url(user, reset_password_token: token)
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>
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
I want to give users posibility to add content not being registered but show 'registration form' above submit button on my 'content form'. How to do that using Devise gem? Or maybe I should use some other gems for this functionality?
Thanks.
Generated in standard view user login is default by email. If you wont show registeration form directly in place you want put render file with registeration form:
<%= render :file => 'users/registrations/new' %>
I propose to review the wiki: How To: Add sign_in, sign_out, and sign_up links to your layout template
and RailsCasts: Episode 210
You can register the user in the back-end (for default configuration):
user = User.new(params[:user])
if user.save!
#some logic, where user adds content
else
#some logic
If you want to register only with the email, you will need to generate a password, and for example, send it to the user email. See more there: https://github.com/plataformatec/devise/wiki/How-To:-Automatically-generate-password-for-users-(simpler-registration)/