20
votes
enable :sessions
set :session_secret, 'secret'

post '/login' do
        session[:loggedInUser] = jsondata['username'].to_s
        puts session[:loggedInUser] + " is the session"
end

Everything is good at this point. When I read the session like this:

get '/debug' do
    session.inspect
end

Its all there. But here comes the problem. When I go for another post request later on:

post '/foo' do
    # do nothing
end

The session is cleared.

Why? Is this a bug?

EDIT

I have narrowed the problem down: I proxypass Sinatra through nginx, to http://app.local/backend - this is when the issue occurs. If I run Sinatra through http://localhost:4567 it all works as expected.

SOLUTION

Use Rack::Session::Cookie instead of the default enable :sessions:

use Rack::Session::Cookie, :key => "rack.session",
:path => "/backend"
# etc

from the Sinatra FAQ:

If you need to set additional parameters for sessions, like expiration date, use Rack::Session::Cookie directly instead of enable :sessions:

5
What is your version of Sinatra? - Bala
I'm seeing exactly this behavior on 1.4.3, fixed with SOLUTION (which should be the accepted answer, minus :key => "rack.session", :path => "/backend" which isn't required). - davetapley
Just my two cents: this was happening to me because the Rack::Protection module was dropping the session. In my case it was the HttpOrigin that failed. You might find it helpful to enable logging on Rack::Protection to see this. I didn't manage to do it (I'm using Gollum and not sure where to set this options), so I hard-coded an exception in the call method of Rack::Protection::Base. - Yuval
@Yuval you saved the day for me big time! You should post this as a separate answer! - smoyth

5 Answers

18
votes

I was suffering from the same issue as you: sessions were being cleared on post.

I have no idea why this works, but this is my solution:

#enable :sessions
use Rack::Session::Cookie, :key => 'rack.session',
                           :path => '/',
                           :secret => 'your_secret'

I literally just replaced the enable :sessions bit with use Rack::Session::Cookie ... and now all is good in the world.

5
votes

After I add set :session_secret, SESSION_SECRET, everything works.

set :session_secret, SESSION_SECRET
enable :sessions

Then I find, Sinatra's README does mention about that:

To improve security, the session data in the cookie is signed with a session secret. A random secret is generated for you by Sinatra. However, since this secret will change with every start of your application, you might want to set the secret yourself, so all your application instances share it:

set :session_secret, 'super secret'

2
votes

This happens because Sinatra regenerates the session cookie on every start of the application, if you run behind apache or a rack server that can start or switch to another instance you will face this problem.

The easier solution is set the secret to a fixed value with something like:

 set :session_secret, "328479283uf923fu8932fu923uf9832f23f232"
 enable :sessions

The other answer which suggest do this:

#enable :sessions
use Rack::Session::Cookie, :key => 'rack.session',
                           :path => '/',
                           :secret => 'your_secret'

Also work, but just because its setting the secret to a fixed value.

0
votes

At the request of @smoyth I am posting my comment as a separate answer.

Just my two cents: this was happening to me because the Rack::Protection module was dropping the session. In my case it was the HttpOrigin that failed. You might find it helpful to enable logging on Rack::Protection to see this. I didn't manage to do it (I'm using Gollum and not sure where to set this options), so I hard-coded an exception in the call method of Rack::Protection::Base.

-3
votes

I do not see any issues at all. Here is my code. Try this and see if you still have that issue.

require 'sinatra'

configure do
  enable :sessions
  set :session_secret, 'secret'
end

get '/login' do
  session[:foo] = Time.now
  "Session value set."
end

get '/fetch' do
  "Session value: #{session[:foo]}"
end

get '/foo' do
  "Session value: #{session[:foo]}"
end

get '/logout' do
  session.clear
  redirect '/foo'
end

http://localhost:4567/login #=> Session value set.
http://localhost:4567/fetch #=> Session value: 2013-09-17 09:42:56 +0100
http://localhost:4567/foo #=> Session value: 2013-09-17 09:42:56 +0100
http://localhost:4567/logout #=>(redirects to) http://localhost:4567/foo #=> Session value: