How to set a session variable after sign in with Devise? - ruby-on-rails

I'm trying to find out where to set a session variable once a user has logged in using Devise. I found this post (and others):
Set a session variable in devise on sign in
So I tried something like this:
protected
# when a user logs in
def after_sign_in_path_for(resource_or_scope)
session[:current_account_id] = current_user.accounts.find_by_is_default(true).id # get id of row where it's is_default is set as true
abort(session[:current_account_id])
end
..but no joy. I don't want to do an alternative redirect (as the issue in the link was asking), just set a session variable when the user logs in, so I'm not sure if this is the callback I'm wanting. Also, I may have my find_by_ method wrong but I was hoping I could debug (using abort) once the script gets that far - but it doesn't appear to be as abort doesn't seem to be called. Any help much appreciated. Thanks

Make sure the protected method is located inside of ApplicationController.

Related

customizing Devise's authenticate and current_user methods to work with soft delete / acts as paranoid

I am using Devise as authentication for a rails 3.2 app. My user model is called User, and I've been trying to implement a soft delete. I am trying to do this by using acts_as_paranoid, which automatically does the work for you. Things work as expected (others can no longer see the "deleted" user), except that I want the deactivated user to still be able to log in and see a "deactivated" screen, and give them the chance to reactivate their account.
The problem is that Devise (Warden?) is no longer able to find the deleted user. Acts_as_paranoid lets you access the soft deleted records if you use the scope "with_deleted". So I am able to get part of the way there with:
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
where(conditions).with_deleted.first
end
I put this into my user model, and so now when I input the log in information, I'll get the flash message that I have successfully logged in, and it'll touch the "updated_at" column in the user model and increment the sign_in_count, etc. However, it doesn't really authenticate in the sense that the authenticated method returns false and the current_user helper method returns nil. So what is my best strategy to get these working? Can I override the current_user method somehow so that it queries the User model with the with_deleted scope? Do I have to do something with warden, such as added conditions like I did with the find_first_by_auth_conditions method? I cannot figure out how to do this. Any help is appreciated!
also, after I do get it working, I would like to automatically send all soft deleted users to a "deactivated" page where their only options are to permanently delete or reactivate. Is there some way to do this with routing via the "authenticated :user do {} end", or do i have to put a before_filter in the application_controller and check for the users at every request?
I can answer your last question, about routing to the "deactivated page". You can use a lambda to isolate different types of users, like this:
authenticated :user, lambda {|u| u.deactivated? } do
root :to => 'application#deactivated'
end
authenticated :user, lambda {|u| u.active? } do
root :to => 'application#active'
end
Thanks to a tip from the Devise group, it turns out I needed to override the serialize_from_session method, which is inside the authenticatable module and looks like this:
def serialize_from_session(key, salt)
record = to_adapter.get(key)
record if record && record.authenticatable_salt == salt
end
I'd been trying with no success to override modules using initializers (I was trying to override existing strategies, and also to try writing a new one for warden); but I kept getting weird name errors. I still haven't figured that out. However, with the tip, I went ahead and overrode the method inside my User model. Since I don't foresee using some other resource, I didn't mind just editing it like this. The code was just:
def self.serialize_from_session(key, salt)
record = with_deleted.find(key).first
record if record && record.authenticatable_salt == salt
end
This skips the to_adapter.get method altogether (which, to anyone interested, is in orm_adapter, not devise; this took me awhile to find as I was thinking I needed to override this). This probably isn't the cleanest way to do this, but it works well enough for me. And even if I do have a different resource, this should only override the User resource, so I think everything would work fine otherwise.
If problems crop up, I'll add them here. If not, hopefully this helps someone in some way. Certainly took me long enough to get to the bottom of!

Devise - override redirect for the custom login page

I have the following interesting problem. I've created a secondary login form. From that secondary form I want the user always to be redirected to the specific form. I believe Devise is handling redirects in the following function in ApplicationController:
def after_sign_in_path_for(resource)
# custom redirect stuff
end
What would be the best way to tell devise "if I am coming from the custom redirect path, always take me to some specific page and disregard what ever is in params[:redirect]". I was thinking of either inspecting referrer url string or storing stuff on the session, but I am not sure.
What would be the best way to achieve this behaviour? Any suggestions would be appreciated!
Inspecting Devise code, I found that it is possible by clearing out the session variable where Devise stores the return path:
session[:user_return_to] = nil # or some explicit path
Note: Assuming the authenticated model is user.rb

Authlogic and Single Access Token

I am having a hard time finding a simple tutorial on how to enable single access token authentication using authlogic. There is some documentation but it isn't very helpful.
I added single_access_token to my db, I added this:
single_access_allowed_request_types :any
to my Session class. but I still don't understand how a user is authenticated using the credentials param that is passed over every call. My require_authentication before filter does a standard check for current_user like this:
def current_session
return #current_session if #current_session
#current_session = Session.find
end
def current_user
#current_user = current_session && current_session.record
end
But is that enough to work? Does the Session.find method do the magic to log the user is based on my params or do I have to create separate method that actually check if the user_credentials param is there and then find the user based on it and then log that user in. I am confused if I really am "creating" a new session everytime I use a SAT or if I'm just setting current user in a before filter every time an API call is made.
Any help would be amazing! Thanks!
I implemented a single_access_token solution with authlogic and what I had to do was add single_access_allowed_request_types :all to the UserSession model.
Then I added the following to the controller where I wanted to allow single_access_token authentication.
def single_access_allowed?
["some_action_1","some_action_2","some_action_3"].include?(action_name)
end
It looks like you're missing the controller code. So if you had two actions "get_user_info" and "update_user_info" you would add.
def single_access_allowed?
["get_user_info","update_user_info"].include?(action_name)
end
The only thing I had to do make this work was
add a field called single_access_token to my users-table
add a method called single_access_allowed? to each controller where single access should be allowed.
This method would look like this:
# method for authlogic: defines for which action the single-access-token can be used
def single_access_allowed?
(action_name == "deliver") || (action_name == "delivery_status")
end
I did not have to add anything in UserSessionsController or the UserSession object. Authlogic handles that for you. With a single-access-token only one request is authenticated, so there is not a persistent session. Each request has to send the single-access-token. Hence the name: a token to get a single access :)
Hope this helps.
The source code of authlogic is the best documentation on the single access token. This is the specific section that discusses it.
You will need to add a private method called single_access_allowed? in the controller where you are trying to let users access. The Single Access Token is passed by default as a URL encoded parameter using the name user_credentials. So to hit your controller without logging in it will be /your_route/?user_credentials=xxxxxx

Steps to create my own authentication system, need some guidance

I want to learn how to create my own authentication system, please provide some guidance if am doing this wrong.
I will create a Module in my /lib folder /lib/auth.rb
I will require this module in my ApplicationController.
when a user enters their email + password, I will call a method that will do a lookup in the user's table for a user with the same email, I will then compare the passwords. (i'll add encryption with salt later).
If the user entered the correct credentials, I will create a row in the Sessions table, and then write the session GUID to a cookie.
Now whenever I need to check if the user is logged in, or I need the user object, I will check if the cookie exists, if it does, I will lookup the session table for a row with the same guid, if it exists, I will return the session row and then load the User object.
I realize there are many suggestions one can give, but in a nutshell does this sound like a workable solution?
Now to make this usable, I will have to make some helper methods in my ApplicationController right?
How will I access the current_user from within my views?
P.S I know of other authentication systems, I just want to learn how to create my own.
The basic logic you're following is correct. Of course you can always expand on this with features that you need. For instance, you'll need helper methods for things like "logged_in?" and "current_user". Also, you might want to add session expiry, or session retention as a "remember me" feature.
Go for it, you won't learn authentication systems better than building your own then figuring what's wrong with it.
You should really check out the authlogic gem on github.
http://github.com/binarylogic/authlogic
It also has great instructions on how to set up your users.
After Faisal said what I would say, I only give you answer to the last part of your question:
"How will I access the current_user from within my views?"
try something like this:
class User < ...
def self.current=(u)
#current = u
end
def self.current
#current
end
end
In your views (or any part of your code) you can call User.current. Your controller has to assign a validated user to User.current. Your filters can react to "if User.current.nil?" and so on.
If you want to be thread safe, you may use a thread variable instead of #current:
Thread.current[:current_user] = u

Rails 3 + Devise: user_signed_in? for a different user in the database?

I'm building an app where I want to add an online status for a given user.
I know that Devise has a method user_signed_in? built in to check if the user who is using the app is signed in or not. But when I try to use it for a different user like this:
user_signed_in?(user)
user.user_signed_in?
I obviously get an undefined method error.
Does Devise have a method for this or do I have to write my own?
One approach was to store the online status of a given user in the user model.
What's the best solution to this?
I have used Devise on my applications and experienced some of the same problems as you when I first began working with it. You are merely using the helper methods incorrectly. If you'd like to check if the current user has a session and is signed in, you use the helper as such:
if user_signed_in?
which is essentially the same statement as:
if !current_user.nil? && current_user.signed_in
If you'd like to check if a user object is signed in, then you call this: (where user is a User Model Object)
if user.signed_in?
I'm not the author of Devise, but from what I can tell of Warden / Devise neither keep track of who is logged in.
The problem with having an is_online column in the User table is that it is difficult to see who is active on the website. I would add a column to your User model called last_seen as a date-time, and update that with Devise every time the user requests a page. You could then easily add a User.online_count method or also see if a user has been seen at the website in the last 5 minutes.
Use devise_for :user in your routes.rb.

Resources