Devise #token not resolving correctly (Devise 3.2.3) - devise

Running a Rails 4 app with Devise 3.2.3 and Devise Confirmable.
Devise generates a confirmation_token for a newly created user that is correctly stored in the database, and then sends out a mailer with confirmation instructions. The token sent out in the mailer is different than the confirmation_token stored in the db for security reasons, but the token sent out in the mailer is not resolving to the correct confirmation token for the user, therefore the user is never confirmed.
Here is an example of what I am talking about:
The user in the database has the following confirmation_token attr set by Devise after being created:
beaa0ed7c9c2da72a99381ee705aa8ebd91672a5c18f5a44deeb43d0665080c4
and the following link is sent out in an email to the user:
http://localhost:3000/users/confirmation?confirmation_token=ZEagTsW1o1Ex_xGdQq7D
I have confirmed that the template for the mailer is using the new #token method instead of the old #resource.confirmation_token
%p Welcome #{#email}!
%p You can confirm your account email through the link below:
%p= link_to 'Confirm my account', confirmation_url(#resource, confirmation_token: #token)
but when I hit the link that devise sends out, the #token does not resolve to the correct confirmation_token attr on the user. From the logs:
Started GET "/users/confirmation?confirmation_token=ZEagTsW1o1Ex_xGdQq7D" for 127.0.0.1 at 2014-04-16 12:15:33 -0700
Processing by ConfirmationsController#show as HTML
Parameters: {"confirmation_token"=>"ZEagTsW1o1Ex_xGdQq7D"}
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`confirmation_token` = 'd876b3a89e02512b7c8ff632f2d8ff33dbe57ccc4df50469ad99e456c45938f8' ORDER BY `users`.`id` ASC LIMIT 1
As you can see, the #token sent out in the email resolved to d876b3a89e02512b7c8ff632f2d8ff33dbe57ccc4df50469ad99e456c45938f8 instead of beaa0ed7c9c2da72a99381ee705aa8ebd91672a5c18f5a44deeb43d0665080c4. Any ideas?

Answered this question after banging my head against this for a while. I had an after_create callback on my user model that was updating an attribute on the user and that was causing Devise to internally regenerate the confirmation_token after the confirmation_instructions email was sent out, therefore the token in the email was no longer current or valid.

Related

Rails Omniauth-github (422) The change you wanted was rejected

I have had this solution for Omniauth & Github implemented and working fine but sometime in the last few months it stopped working.
The error I'm getting when I try to login is: (422) The change you wanted was rejected.
Specifically in the Heroku logs I'm seeing:
ActiveRecord::RecordInvalid (Validation failed: Password can't be blank):
app/models/user.rb:18:in `create_from_omniauth'
app/models/user.rb:14:in `from_omniauth'
app/controllers/sessions_controller.rb:4:in `create'
Do I need to save the credentials when I create the user?
My user model:
def self.from_omniauth(auth)
where(auth.slice("provider", "uid")).first || create_from_omniauth(auth)
end
def self.create_from_omniauth(auth)
create! do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.name = auth["info"]["nickname"]
user.email = auth["info"]["email"]
user.image = auth["info"]["image"]
end
end
Sessions controller:
class SessionsController < ApplicationController
def create
user = User.from_omniauth(env["omniauth.auth"])
session[:user_id] = user.id
redirect_to root_url, notice: "Signed in!"
end
def destroy
session[:user_id] = nil
redirect_to root_url, notice: "Signed out!"
end
end
Facebook's omniauth error "the change you wanted was rejected"
might appear because of your validations set in the model. I had to refactor my validation for users having one unique email, which wasn't working when a user would try to facebook login with the same email.
Look at your logs. heroku logs -t
It looks like you're either validating presence of the password field in your User model or using has_secure_password, which does that under the covers.
If you're doing that validation yourself, you can just add a clause like :if => :password_changed? to the validation.
If you're using has_secure_password, it depends which version of Rails you're using. Any version with these two changes (I believe only Rails 4) support passing a validations: false option to has_secure_password. Otherwise, there's not really a good solution, other than maybe setting a random dummy password when you create the user then letting them change it immediately.
I had this issue when the time on my gitlab server was out of sync, i restarted ntpd, which corrected the time on the server and the problem was resolved

How do I make Devise verify if the user has an email before try to send a new password?

I have a Rails app where I am using Devise for authentication. Devise lets users click to get an email containing a link to reset their password.
My problem is that the email field is not required in the table "user". There's a login field to authenticate, that is sincronized from another system. I can't set email to required.
I want to verify the email field and return a custom message to the user, if the email is not set. If it is, then Devise will continue and reset the password.
I saw in another post, that I have to override the method "email_required?" in the user model, but I still get the error message "Email cant be blank".
def email_required?
false
end
In your user model you should probably have some kind of validation for email like so
validates_presence_of :email
Also if you wanted to migrate the database to have email as not null it would be like so
change_column :users, :email, :string, :null => false

Why won't Devise allow unconfirmed users to login even when allow_unconfirmed_access_for is set?

We have an existing user base and are adding email confirmation. Confirmation is optional but will allow additional features. Users are not required to confirm. I've added the confirmable module and ran migrations. Confirmation works as advertised.
But, users cannot log in since they are not confirmed. All current users have nil confirmation values, which is what we want (users can go back and confirm their email at any time). I've followed all the Devise wiki articles and set allow_unconfirmed_access_for in the initializer:
config.allow_unconfirmed_access_for = 10.years
I've also tried setting it in our user model as well:
devise :confirmable, allow_unconfirmed_access_for: 10.years
I've also tried using other values (1.year, 500.days, etc.)
My SessionsController, which does not differ much from Devise's method (here on github)
class Users::SessionsController < Devise::SessionsController
respond_to :json
def new
redirect_to "/#login"
end
def create
resource = warden.authenticate(auth_options)
if !resource
render json: {error: "Invalid email or password" }, status: 401 and return
end
sign_in(resource_name, resource)
render "sign_in", formats: [:json], locals: { object: resource }
end
end
Devise's the response:
{"error": "You have to confirm your account before continuing."}
Devise 2.1.2 with Rails 3.2.9.
The Devise team have released a version (2.2.4) that supports nil as a valid value for allow_unconfirmed_access_for, meaning no limit. Issue: https://github.com/plataformatec/devise/issues/2275
You can now do:
config.allow_unconfirmed_access_for = nil
I simply needed to do this in my User model, instead of using allow_unconfirmed_access_for:
protected
def confirmation_required?
false
end
I've got the same issue: after turning on devise confirmations previously created accounts are unable to login.
The reason is here:
def confirmation_period_valid?
self.class.allow_unconfirmed_access_for.nil? || (confirmation_sent_at && confirmation_sent_at.utc >= self.class.allow_unconfirmed_access_for.ago)
end
Old accounts have confirmation_sent_at set to nil, that's why they are unable to log in.
One solution is to force confirmation_sent_at like that:
update users set confirmation_sent_at=created_at where confirmation_sent_at is NULL;
You can do it manually, or create a migration.

Log emails sent by devise in rails

I am using devise for user authentication in rails. How can I log the emails sent by devise. I have a model for storing the emails. How can I hook in so that before devise sends emails for new registration, change password, forgot password etc, I can just store the emails in the db?
Create a file called config/initializers/devise_mail_logger.rb and re-open the Devise::Mailer class
devise_mail_logger.rb:
Devise::Mailer.class_eval do
def devise_mail_with_logger(record, action)
email = devise_mail_without_logger(record, action)
#code to log this email to DB goes here
end
alias_method_chain :devise_mail, :logger
end
The email object will have the message body, subject, recipient details. You can pass this object to the model you have to store emails.

submit from HAML form clears session data (Rails3)?

I have the following model:
Project
- name:text
- description:text
And this is snippet of the form I've written in HAML for new project:
%p Create a new project:
%form{:method => "post", :action=>"/projects/"}
%label{:for => "project-name[name]"} Name:
%input{:type=>'text', :size=>40, :name=>'projectname', :id=>'project-name'}
%br/
%label{:for => "project-description[description]"} Description:
%textarea{:rows=>'10',:cols=>'10',:name=>'projectdescription',:id=>'project-description'}
%br/
%input{:type=>'submit', :value=>'Create'}
when the user clicks submit, the session data seems to be cleared.
Here is why I suspect this:
I am using omniauth, when a user signs in, I set session[:user_id] = user.id
I have a redirect in the project controller for ensuring the user is always signed in for all actions
the index and new actions are handled properly.
instead of the create action being handled, the user is bounced to the sign in page (as per the before_filter)
I replicated the same functionality with erb files and there no issue. When I drop in the new.haml file the error shows up again.
Any ideas?
I believe that what's happening here, is that you have `protect_from_forgery' in your ApplicationController, but the csrf token is not sent to application. Check if you have a relevant rails.js included if it's AJAX call. And if you were using form_for, then the helper would automatically insert the hidden field with the csrf token.