Rails: 4.0.0,
Devise: 3.0.0rc
Devise seems not to add error messages to models at all, delegating that to validations. That's fine generally. But in the case of the SessionsController, would it make sense to add the errors to the model so that you can use field_with_errors or some such functionality and say 'email is required' if the user does not put their email in.
The snippet below is where I think I would have to modify Devise to get it to add errors to the model.
class Users::SessionsController < Devise::SessionsController
# POST /resource/sign_in
def create
self.resource = warden.authenticate!(auth_options)
debugger
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => after_sign_in_path_for(resource)
end
end
Does anyone have any recommendations on what to do? Is it even a good idea to add errors to the model on sign in via something like the following? error.add(:model, "error message")
It just seems that if I use the default flash[:notice] message, then I miss out on being able to attach the error messages to specific fields, which is my aim.
Your concept is right with any other controllers except SessionsController.
Such vague information is desired for signing in activity. If an user inputted wrong information, he can only see "Invalid email or password" or something similar but not precise error.
The reason is to reduce the chance of brute signing trials.
Related
in my project I'm using devise and I want to create custom sign_in. I decided to override create action in SessionsController. Right now my SessionsController looks like that:
class SessionsController < Devise::SessionsController
def create
user_signed_in? # -> gives true if correct email and password
if !current_user.new_one? # -> I have access to current_user
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_flashing_format?
sign_in(resource_name, resource)
yield resource if block_given?
respond_with resource, location: after_sign_in_path_for(resource)
end
# even if condition is true, user is signed_in
end
end
I'm a little confused because even if I remove whole code from there then user will be signed_in anyway. Can someone explain me why user_signed_in? gives me true before sign_in(resource_name, resource)? It looks like the user is already logged in? In that case, how can specific users not be allowed to log in? I know there is method active_for_authentication?, but I don't want to override it, because I want to allow some users to log in to only part of the application, this method will not allow it and overriding this method will not allow me to do it.
When overriding Devise::SessionsController, you still expect some authentication over the params you have parametered (usually email/password).
user_signed_in? method does not the authentication, it's just a helper and it can check if the formerly processed authentication has succeded in the past or not.
The authentication is achieved with use of 'super' at some point of your code.
See :
https://rubydoc.info/github/heartcombo/devise/main/Devise/Controllers/SignInOut#signed_in%3F-instance_method
to find an answer to your first question
https://github.com/heartcombo/devise#controller-filters-and-helpers
https://github.com/heartcombo/devise#configuring-controllers (use of
super)
Hope it helps
I have an attribute (approved) on the user that i wish for devise to consider before allowing sign in.
I've overriden the session controller and its currently as follows:
class SessionsController < Devise::SessionsController
skip_after_filter :verify_authorized
def create
user = User.find_by_email(params[:user].try(:[], :email))
unless user.approved?
flash[:alert] = "Login fail. Account currently pending approval."
redirect_to :back and return
end
resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => after_sign_in_path_for(resource)
end
def failure
head 403
end
end
Looking at the logic i see that there is a "redirect_to :back" happening, but then it seems devise takes over, logs in the user regardless of that and redirects to root (which is what i've setup to redirect to upon login)
I've never worked with devise before and this is driving me mad. I should return before reaching the 'sign_in' part but something is happening that it still signs in the user regardless of my unless block.
How could i go about intercepting that login if user.approved == false ?
thanks in advance!
Although not the same question, the top answer in this question applies here and fixed the issue for me.
Check if user is active before allowing user to sign in with devise (rails)
Rails 3.2. Hi. Currently out app has an authentication system implemented that we did and we are going to migrate to devise. I am at wits end here trying to get the devise log in to work. Somehow I have drilled down the problem to the part where Devise actually creates a user session.
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_flashing_format?
sign_in(resource_name, resource)
yield resource if block_given?
respond_with resource, location: after_sign_in_path_for(resource)
end
Routes (I have changed the devise controller and literally just pasted the Devise controller code to debug it with pry):
devise_for :users, controllers: { sessions: "sessions" }
The problem here is that warden is not even hitting the database with an authenticate. It just returns nil. I checked my other, simpler application and authenticate's behavior scans the database, regardless if email/password is correct. How do I get Warden/Devise for that matter to actually do a select statement to check the database out?
(If there is anything I can paste to help you guys I'll paste it)
In case it may help someone else: I was running into this very same problem. The source of the trouble turned out to be that I had a custom app/views/sessions/new.html.erb view that was not copied from devise.
See https://github.com/plataformatec/devise/issues/3700 for more information.
Try look in console the authentication keys of your devise model:
(your_model).authentication_keys
If result is different respect keys you need (i.e email), just add explicity in your model:
:authentication_keys => [:email]
I resolved in this way. Hope this help
Bye
I'm using Devise to handle authentication in a Rails app, and I'm using permanent_records to soft delete users. The default scope for my User model is the undeleted users. If a user deletes (deactivates) his account, I want him to be able to reactivate his account by logging in, similar to the way Facebook does it. Problem is, since Devise doesn't know to look for deleted users, no account is found. I thought about overriding the sessions#create method
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => after_sign_in_path_for(resource)
end
But since this is handled by Warden, it seems I'm out of luck. I'm afraid that if I start digging too deep I'm going to start breaking things.
Any ideas?
Thanks!
You need:
Overwrite find_for_authentication method in User model to allow finding for any users https://github.com/plataformatec/devise/blob/master/lib/devise/models/authenticatable.rb#L229
Redefine after_database_authentication method in your model to remove deleted flag here https://github.com/plataformatec/devise/blob/master/lib/devise/models/database_authenticatable.rb#L98
That is all, I believe. No need to touch controller actions.
this works with the paranoia gem:
class << self
def find_for_authentication(conditions)
User.unscoped do
user = super(conditions)
user.restore!(recursive: true) if user.deleted?
user
end
end
end
I am using devise and i am trying to update extra fields has user log in. So in order for me to be able to update my user.longitude i had to create in new controller who is acting has the rails controller sessions. Here the code I have
class MysessionsController < Devise::SessionsController
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
self.resource.longitude = params[:longitude]
self.resource.update_attributes(params[:longitude])
respond_with resource, :location => after_sign_in_path_for(resource)
end
end
I know it doesn't follow restfull and it doesnt work but i need to be able to update my user field and i am not sure how to do, this doesn't give me anything wrong, but it doesn't also modify it properly. Thank for any help
Your line:
self.resource.update_attributes(params[:longitude])
Might return false if the record is invalid and silently fail. Check that out first.
Second, you might want to take a look into alias_method_chain instead of copying the content of the create action of your inherited controller.
Otherwise, this is to update a record after a user logged in. You should save the longitude AFTER he signs in. Wherever you redirect the user after he signs in, let the following call handle the longitude update. Easy way could be to store the longitude in session if it needs to survive.