Sinatra sessions

304 views
Skip to first unread message

Larry Siden

unread,
Feb 21, 2010, 2:32:47 AM2/21/10
to rubyd...@googlegroups.com, a2...@googlegroups.com
I'm trying to establish a session in a sinatra app.  In other words, in response to /logon/<hash-digest> (over https), I want Sinatra to establish a new session and return a session id in a cookie, say 'sid'.  

I'm testing my app w/ Cucumber and Rack-test, so I want Cuke to save the cookie so that it will get sent in all subsequent requests so that Sinatra can identify my session.  

I know that Sinatra has a session() method that returns a hash, but I need to see an example of how it's used.  It doesn't make any sense to use it without a session-id that returns the actual session in another hash.  IOW, I think that session() needs to return a hash of hashes, but I can't find any examples that confirm this.

Furthermore, on the mock client side, I can't figure out how to save and send the cookie on subsequent requests.  Do I have to do everything explicitly, as in GET /path {:Cookie => sid=12345'} ?

I've been going over and over the online docs, but I can't seem to find a clear example.  Does anyone have any clear examples, or can tell me how to find them?  (I've been Googling until my fingers are blue but I keep finding the same pages that leave me without clear examples.)

Many thanks,

Larry Siden, 734-926-9614, http://umich.edu/~lsiden

The United States is a nation of laws, badly written and randomly enforced.
--Frank Zappa 1940-1993

Larry Siden

unread,
Feb 21, 2010, 2:45:09 AM2/21/10
to rubyd...@googlegroups.com, a2...@googlegroups.com
PS - I've been looking at this: http://localhost:8808/doc_root/rack-test-0.5.3/rdoc/index.html, but it seems to be using basic authentication, which required authentication with every request.  I wanted to use session-based authentication which establishes a session on the server through /login/<hash-digest>, gets back a cookie with session-id, then simply sends that cookie with session id with each request.

Does any of that make sense?


Larry Siden, 734-926-9614, http://umich.edu/~lsiden

The United States is a nation of laws, badly written and randomly enforced.
--Frank Zappa 1940-1993


lsiden

unread,
Feb 22, 2010, 12:54:41 AM2/22/10
to Detroit.rb
Well, after much searching and reading, I tried something simple:

require 'rubygems'
require 'sinatra'

enable :sessions

...

here's what I found in Firebug after calling localhost:4567/

Content-Type text/html
Content-Length 3
Server WEBrick/1.3.1 (Ruby/1.9.1/2009-07-16)
Date Mon, 22 Feb 2010 05:40:29 GMT
Connection Keep-Alive
Set-Cookie rack.session=BAh7BjoGeEkiCDEyMwY6DWVuY29kaW5nIg1VUy1BU0NJSQ
%3D%3D%0A; path=/

So you can see that Sinatra is already doing exactly what I wanted:
passing a cookie that identifies the session. I confirmed by setting
session the same session var to different values from different
browsers that each instance of session is unique to that session,
exactly as I would have expected, but the docs never stated clearly.
I guess they want you to think it's all "magic". ;)

I'm not sure this will be satisfactory for me, b/c I want a session
that times out after a certain period. Also, I'll be accessing my API
entirely from a XMLHttpRequest object in a browser, so there is no
automatic cookie. I'll have to set/get the cookie header myself if I
want to do it this way. But it might be worth it, b/c now I don't
ever have to explicitly pass the account id for the data my app
serves. That can stay on the server in the session! And since I'll
be talking to the server over SSL, there is little likelihood that
anyone can hijack the session.

Man, this is like talking to myself!

On Feb 21, 2:45 am, Larry Siden <lsi...@gmail.com> wrote:
> PS - I've been looking at this:http://localhost:8808/doc_root/rack-test-0.5.3/rdoc/index.html, but it seems
> to be using basic authentication, which required authentication with every
> request.  I wanted to use session-based authentication which establishes a
> session on the server through /login/<hash-digest>, gets back a cookie with
> session-id, then simply sends that cookie with session id with each request.
>
> Does any of that make sense?
>

> Larry Siden, 734-926-9614,http://umich.edu/~lsiden


>
> The United States is a nation of laws, badly written and randomly enforced.
> --Frank Zappa 1940-1993
>
>
>
> On Sun, Feb 21, 2010 at 2:32 AM, Larry Siden <lsi...@gmail.com> wrote:
> > I'm trying to establish a session in a sinatra app.  In other words, in
> > response to /logon/<hash-digest> (over https), I want Sinatra to establish a
> > new session and return a session id in a cookie, say 'sid'.
>
> > I'm testing my app w/ Cucumber and Rack-test, so I want Cuke to save the
> > cookie so that it will get sent in all subsequent requests so that Sinatra
> > can identify my session.
>
> > I know that Sinatra has a session() method that returns a hash, but I need
> > to see an example of how it's used.  It doesn't make any sense to use it
> > without a session-id that returns the actual session in another hash.  IOW,
> > I think that session() needs to return a hash of hashes, but I can't find
> > any examples that confirm this.
>
> > Furthermore, on the mock client side, I can't figure out how to save and
> > send the cookie on subsequent requests.  Do I have to do everything
> > explicitly, as in GET /path {:Cookie => sid=12345'} ?
>
> > I've been going over and over the online docs, but I can't seem to find a
> > clear example.  Does anyone have any clear examples, or can tell me how to
> > find them?  (I've been Googling until my fingers are blue but I keep finding
> > the same pages that leave me without clear examples.)
>
> > Many thanks,
>

> > Larry Siden, 734-926-9614,http://umich.edu/~lsiden

Adam Elhardt

unread,
Feb 22, 2010, 9:50:51 AM2/22/10
to rubyd...@googlegroups.com
You'll want to implement session timeout yourself.  Set some var like :last_activity and update it whenever there's a request.  That way if the session times out you can pass along some useful information to tell the user what happened.  Also, you can give early warnings that they're about to be logged out, etc.

Using a cookie that expires after a certain time period works but is less elegant.  Once the cookie expires, your server doesn't know that there ever was a cookie for that user, so you can't even tell them _why_ they got logged out.

You've probably already read this a million times, but  http://www.sinatrarb.com/faq.html#sessions tells you how to set (or update) session variables.


--
You received this message because you are subscribed to the Google
Groups "Detroit.rb" group.
To post to this group, send email to rubyd...@googlegroups.com
To unsubscribe from this group, send email to
rubydetroit...@googlegroups.com



--

===================
Sapphire Internet Media
www.sapphireim.com
313.582.2636

Larry Siden

unread,
Feb 22, 2010, 2:50:24 PM2/22/10
to rubyd...@googlegroups.com
Thanks.  I'm working on the timeout part right now.  I want to run a background thread that every 5 min, say, will scan the list of sessions and remove any that are older than, say, 1hr.  

Putting a timeout on the cookie will simply orphan the session, because without an implementation, the timeout hashes will live forever until the server is terminated.

The section Adam referred to threw me off because it gave me no idea how Sinatra or rack distinguishes on user's session from another.  It looked as if all the sessions were all inhabiting the same hash, which didn't make sense!  

Actually, env['rack.session'] is an uber-hash.  env['rack.session'][<session-id>] returns the session for the current request.  So env['rack.session'].each() will iterate over _all_ the live sessions, and allow my background thread to kill any that are stale, i.e. older than some limit.

Piece of cake!  Don't you love computers?! ;)

Larry Siden, 734-926-9614, http://umich.edu/~lsiden


The United States is a nation of laws, badly written and randomly enforced.
--Frank Zappa 1940-1993


lsiden

unread,
Feb 23, 2010, 12:37:10 AM2/23/10
to Detroit.rb
Fooled again! Upon closer examination of gems/rack-1.1.0/lib/rack/
session, I added "p session_data" after line 53 in load_session().
This is what I got after adding several random values to the session:

"\x04\b{\t:\x06xI\"\nhello\x06:\rencoding\"\rUS-ASCII:\x06kI
\"\x065\x06;\x06@\aI\"\tfive\x06;\x06@\aI\"\x065\x06;\x06@\aI\"\bsix
\x06;\x06@\aI\"\x066\x06;\x06@\a"

which is then unmarshalled as:

{:x=>"hello", :k=>"5", "five"=>"5", "six"=>"6", "seven"=>"7"}

So that session cookie wasn't really a session id, but the base64
encoding of the session hash after ruby marshaling. So Sinatra really
does bounce the entire session hash back and forth in a cookie! Not
good!

The issue for me isn't the 4K limit. All I need to store is a token
noting that the user has been authenticated by Openid, and the account
number. The issue for me is that this information should never cross
the wire, since that would open it up to a user hacking into other
users accounts.

So that means that I have to maintain my own sessions. No biggie.

On Feb 22, 2:50 pm, Larry Siden <lsi...@gmail.com> wrote:
> Thanks.  I'm working on the timeout part right now.  I want to run a
> background thread that every 5 min, say, will scan the list of sessions and
> remove any that are older than, say, 1hr.
>
> Putting a timeout on the cookie will simply orphan the session, because
> without an implementation, the timeout hashes will live forever until the
> server is terminated.
>
> The section Adam referred to threw me off because it gave me no idea how
> Sinatra or rack distinguishes on user's session from another.  It looked as
> if all the sessions were all inhabiting the same hash, which didn't make
> sense!
>
> Actually, env['rack.session'] is an uber-hash.
>  env['rack.session'][<session-id>] returns the session for the current
> request.  So env['rack.session'].each() will iterate over _all_ the live
> sessions, and allow my background thread to kill any that are stale, i.e.
> older than some limit.
>
> Piece of cake!  Don't you love computers?! ;)
>

> Larry Siden, 734-926-9614,http://umich.edu/~lsiden
>
> The United States is a nation of laws, badly written and randomly enforced.
> --Frank Zappa 1940-1993
>
>
>
> On Mon, Feb 22, 2010 at 9:50 AM, Adam Elhardt <a...@sapphireim.com> wrote:
> > You'll want to implement session timeout yourself.  Set some var like
> > :last_activity and update it whenever there's a request.  That way if the
> > session times out you can pass along some useful information to tell the
> > user what happened.  Also, you can give early warnings that they're about to
> > be logged out, etc.
>
> > Using a cookie that expires after a certain time period works but is less
> > elegant.  Once the cookie expires, your server doesn't know that there ever
> > was a cookie for that user, so you can't even tell them _why_ they got
> > logged out.
>
> > You've probably already read this a million times, but

> >http://www.sinatrarb.com/faq.html#sessionstells you how to set (or

Adam Elhardt

unread,
Feb 23, 2010, 8:12:09 AM2/23/10
to rubyd...@googlegroups.com
An interesting sidenote is that in 2007, Rails switched to cookie sessions stores by default.  It gives you a noticable advantage in performance and simplicity.  However, they do add a level of security to it in the form of a SHA512 fingerprint they take of the data using a secret key that's stored on the server.  Something like session[:fingerprint] = Digest::SHA1.hexdigest(Marshal.dump(session) + my_server_secret). 

Then you can check it later with session.delete(:fingerprint) == Digest::SHA1.hexdigest(marshal.dump(session) + my_server_secret)

If you want to make things even more secure, add the client IP to the session and check it on every request.

Larry Siden

unread,
Feb 23, 2010, 3:25:58 PM2/23/10
to rubyd...@googlegroups.com
Sounds good, but the only thing I need to pass in the cookie now is the OpenId token that I get from whatever openid server they logged into.  I believe that token is supposed to be unique to the login transaction.  On the server, that token will point through a hash to the account_id user is allowed to have access to (i.e. his own account).  So they can't mess with anybody's account but their own.

Since these transactions are meant to occur through XMLHttpRequest and not the browser address bar, a cookie isn't really necessary.  I can just send the token back and forth as part of the data for each request.

Since the whole thing goes over https, no one else should be able to obtain the openid token or hijack the session.

Correct me if I'm wrong, but I think I've got the bases covered.

Larry Siden, 734-926-9614, http://umich.edu/~lsiden


The United States is a nation of laws, badly written and randomly enforced.
--Frank Zappa 1940-1993


Reply all
Reply to author
Forward
0 new messages