ActiveRecord Backup and New Session

I am new to Rails and am experiencing a strange problem that I do not understand. I use ActiveRecord as a session repository and must add a session identifier as a JSON response property for all requests. I also use Devise if this affects the situation. The problem is that if the request is executed by the user without cookies (or, at least, without the session identifier in the cookie), session.id is empty or - attention - please, not the same value that is set in the response cookie.

For debugging, I add this code as after_filter in the ApplicationController:

puts session.id puts request.session_options[:id] 

Both values ​​are the same. They correspond to the value in the cookie, if present. Otherwise, if the session identifier is not in the cookie, the cookie set after this request has a different meaning.

My opinion is that session_id gets a new value after it is actually stored in the database, where it should be unique. Database Transfer:

 def change create_table :sessions do |t| t.string :session_id, :null => false t.text :data t.timestamps end add_index :sessions, :session_id, :unique => true add_index :sessions, :updated_at end 

My question is: how can I get the actual session.id value of a new session before the first response is displayed?

UPD:

I just created a new Rails application that uses ActiveRecord session storage without Devise, and I can get session.id that will be set in the cookie just before the response using this application code controller:

 class ApplicationController < ActionController::Base after_filter :show_session def show_session puts session.id end end 

But in my existing application with Devise, I get a value that really looks like a session identifier, but this does not match the value set in the cookie using the Set-Cookie response header, and the value actually stored in the session table in the database, It looks like Devise has a conflict with ActiveRecord session storage. One needs to go deeper to understand this.

UPD 2

Looks like I found the root of the problem. As I said, I use Devise to log in with Omniauth. According to the documentation, the sign_in method resets the session identifier for security reasons. But after that, reset session.id returns the old value that was automatically set. I use this code as an Omniauth callback:

 def facebook_access_token sign_in @user puts session.id end 

And in the console, I get a session ID different from the one set in the Set-Cookie response header. If I comment out the line "sign_in", these values ​​match. New question: how can I get a new session id value after reset inside the sign_in method? Is this an internal implementation of Warden / Devise or something else?

+4
source share
3 answers

The problem I experienced was caused by the default Warden configuration. He updated the session identifier, but somehow the new identifier was not available through session.id.

The only way I decided to stop this behavior is to include this code in config / initializers / devise.rb:

 Warden::Manager.after_set_user do |user,auth,opts| auth.env["rack.session.options"][:renew] = false end 

This method is probably not very good for security reasons, but I have no other ideas for a week of searching and reading sources.

+2
source

Updating is still important and you should not disable it

The identifier of a new session is generated after the controller is executed, therefore, after you have the opportunity to set a response for sending to the client.

The solution is to manually activate the session id update

In the ApplicationController add a method:

 protected def commit_session_now! return unless session.options[:renew] object = session.options.instance_variable_get('@by') env = session.options.instance_variable_get('@env') session_id = object.send(:destroy_session, env, session.id || object.generate_sid, session.options) session_data = session.to_hash.delete_if { |k,v| v.nil? } object.send(:set_session, env, session_id, session_data, session.options) session.options[:renew] = false session.options[:id] = session_id end 

Then in your controller, you simply call this method until you get the session identifier for the response

 def my_action ... commit_session_now! render json: {session_id: session.id}, status: :ok end 

Code in commit_session_now! comes from Rack::Session::Abstract::ID#commit_session https://github.com/rack/rack/blob/master/lib/rack/session/abstract/id.rb#L327

+3
source

Without knowing the details of your application, my suggestion would be to use before_filter in your ApplicationController:

 class ApplicationController < ActionController::Base before_filter :use_session_id protected def use_session_id # Do something with session.id # This will get called before any rendering happens end end 
0
source

Source: https://habr.com/ru/post/1500476/


All Articles