thank_you
thank_you

Reputation: 11107

Session Does Not Last in Rails 4

UPDATE:

I determined that I can change a session in other actions, however if I ever set the session in a POST request, it does not last. If I change the action from a POST to a GET, then the session persists.

ORIGINAL QUESTION:

I have a Rails 4.2.0.rc3 app. Whenever a user signs in, the session is assigned to that user's id. However the session does not last when I run the current_user method. I have a secret_key_base in the secret.yml file. Here is my Sessions Helper.

module SessionsHelper
  def sign_in(user)
    session[:id] = user.id

    puts "signing in" * 100

    puts session[:id].inspect
  end

  def signed_in?
    current_user.present?
  end


  def current_user
    puts "checking session" * 100

    puts session[:id].inspect

    Avatar.find_by(id: session[:id])
  end
end

In my log file, I see that session[:id] is set. When another action is run and the current_user method is called, the session[:id] is nil. What's wrong and how do I fix this?

Upvotes: 3

Views: 2285

Answers (2)

jrochkind
jrochkind

Reputation: 23317

Hm, triggered by your update that this only happens on POST, I'm remembering incompletely a couple things.

  • Rails has CSRF protection, but it is by default only applied on POST, and not GET

  • The defualt Rails CSRF protection logic, in cases of CSRF failure, will silently use a new Session, rather than raising. (I think this was a poor choice of default personally).

I wonder if that's what's happening.

To find out, you want to tell your Rails app to actually raise an exception to noisily fail on a CSRF failure, rather than silently use a new orphan session.

In your app/controllers/application_controller.rb, look for the line protect_from_forgery, and change to protect_from_forgery with: :exception, to tell Rails to actually raise on a CSRF failure. (Actually, it looks like Rails 4.x apps are generated with this new version of the line now, but if you have an app you're upgrading from 3.x, it won't have been. Hooray for railsdiff for making it easy to investigate changes in the rails new generated code accross rails versions).

If you get exception in your demo test case, then knowing what the problem is, you can figure out the appropriate solution: either get the POST request to be made with proper CSRF protection, or turn off CSRF protection for that action. If it's an API sort of request, you may not want CSRF protection.

For more background reading on this sort of problem, see:

Do those sound like your problem?

Upvotes: 7

nPn
nPn

Reputation: 16728

Not sure if this is your issue, but I remembered seeing this when I was looking at upgrading to rails 4.1 (form 4.0)

Here is the link and the section of the doc. I think this would only apply to 4.1 not 4.0

3.5 Cookies serializer Applications created before Rails 4.1 uses Marshal to serialize cookie values into the signed and encrypted cookie jars. If you want to use the new JSON-based format in your application, you can add an initializer file with the following content:

Rails.application.config.action_dispatch.cookies_serializer = :hybrid This would transparently migrate your existing Marshal-serialized cookies into the new JSON-based format.

When using the :json or :hybrid serializer, you should beware that not all Ruby objects can be serialized as JSON. For example, Date and Time objects will be serialized as strings, and Hashes will have their keys stringified.

class CookiesController < ApplicationController def set_cookie cookies.encrypted[:expiration_date] = Date.tomorrow # => Thu, 20 Mar 2014 redirect_to action: 'read_cookie' end def read_cookie cookies.encrypted[:expiration_date] # => "2014-03-20" end end It's advisable that you only store simple data (strings and numbers) in cookies. If you have to store complex objects, you would need to handle the conversion manually when reading the values on subsequent requests.

If you use the cookie session store, this would apply to the session and flash hash as well.

Upvotes: 0

Related Questions