I'm working under rails and, as a beginner, I am trying to understand how the session feature works.
I put my sessions under active record, but when I do this:
#request = Facebook::Request.parse_signed_request(params[:signed_request],
Settings.facebook.app_secret)
puts session
#admin = Admin.find_by_fb_userid(#request["user_id"])
session[#request["user_id"]] = #admin ? #admin : nil
My session continue to display:
{}
Thanks for your help !
How session works?
HTTP is a stateless protocol.It means that it treats each request as an independent transaction
that is unrelated to any previous request so that the communication consists of independent
pairs of requests and responses.
A stateless protocol does not require the server to retain session
information or status about each communications partner for the duration of multiple requests.
The protocol provides no means of storing a user's data between requests.
Therefore, we use Sessions, which allows us to chain multiple requests together into a conversation between client and server, so temporaly keep data.
Session is a hash, so you can add a data into session like to regular hash:
For example, to add a new order into the session can look like this:
e.g. OrdersController
def new
#order = current_user.orders.create # creates a new order for current_user
session[:order] = #order.to_params # adds order information to the session.
end
Sessions can store any kind of string data, but best served by keeping it as small as possible for both speed and security,as third party users can easily decode what information is stored in sessions.
UPDATE
by default session data are stored as cookies but Rails allows to configure session storage in database using ActiveRecordStore or inmemory storage with Redis and Redis store. Each approach has its advantages and disadvantages.
Try printing the session value instead of whole session object:
puts session[#request['user_id']]
Do this before and after setting a session. And make sure you cleanup the session before testing this in browser (clear cookies or start incognito session).
And last but not least, it a bad idea to store whole objects in session. This way you are storing way more data in the session than needed. Try storing just the object id (in your case #admin.id) and then load it whenever needed.
Ok my fault was that I was working on a facebook app I guess and for every refresh, the session is reset by the peer.
But when I continued in my process, everything instantly worked.
And even if session is a stateless protocol, it is used, through the storage that is used to keep the session active all along the session.
Like in PHP or every single language
Related
I have two models doing login (Devise) in my Rails app - Admin and User, both currently use the default cookie store for session data.
I want to be able to identify an Admin session in AJAX requests coming in from the admin, for authorization of these API calls. I plan to do this by setting an encrypted cookie upon Admin login. When the AJAX API call comes in, I open the cookie, grab some identification from it and look for a matching existing Admin session in the store.
As I understand it, to do this, I must have session information stored in the back-end, either by DB or memcache stores.
I expect to have millions of sessions of Users and just a few sessions of Admin at any given time. For this reason, I don't want to just move all session information to a DB or memory, since this is a heap of unneeded data to store. I only want to store/look at Admin session data.
A solution will be creating some custom model which enumerates Admin user sessions, and is maintained by the app. This is simple enough but requires for instance, a way to clean up sessions when they die without signing out. Essentially this is an attempt to duplicate Rails's session store mechanism, which comes with all the problems of storing and maintaining sessions. Instinct tells me to avoid this solution. Am I correct to avoid it?
If so, then my question is, is there a way to configure multiple session stores in a Rails app, a different store for every logged in Model? In this case, have Admin sessions stored in memory, and User sessions stored in cookie. If not, I'll greatly appreciate any comments and suggestions.
Thanks!
You may be thinking about it wrong.
Session are a low level mechanism that you build your authentication on top of. Its just a cookie containing an identifier (a random hash) which is linked to a session storage (by default cookies). This is a simple mechanism to add persistence to a stateless protocol.
Confusingly we also use the concept "sessions" when talking about authentication - for example logging a user in is often referred to as "creating a session". This is complete poppycock as we are just storing a claim (often a user id) in the session that was created when the user first visits the application.
If so, then my question is, is there a way to configure multiple
session stores in a Rails app, a different store for every logged in
Model?
No. Thats a chicken-vs-egg conundrum. In order to know which session storage to use you would need to access the session storage to know which session storage to use... you get the picture.
While you could create your own session storage mechanism that works differently does this is most likely a complete waste of time. Premature optimization is the root of all evil.
As I understand it, to do this, I must have session information stored
in the back-end, either by DB or memcache stores.
Not quite true. You can perfectly well build an authentication solution with just the cookie storage. In that case Rails just keeps a record on the server of which session identifiers are valid.
The main reason you would need to store additional session information in the database or memcached is if you need to store more data in the session than the 4093 bytes allowed by a cookie. Cookie storage is after all much faster and does the job fine 99% of the time. YAGNI.
You should also recognize that not everything needs to be saved in the session storage. For example the Devise trackable module saves log in / out timestamps on the user table as part of the process of authenticating a user. This is "session information" yet has nothing to do with session storage.
I want to be able to identify an Admin session in AJAX requests coming
in from the admin, for authorization of these API calls.
There are many ways to use different authentication logic for different parts of the application such as Warden strategies. For an API you may want to consider using stateless (and sessionless) authentication such as JWT.
In my Rails application I make set of AJAX calls at once and that causes sending the same session cookie for each request.
The problem is that rails sets new session cookie in every response and therefore it expects that cookie value in the request after.
I'm looking for server-side solution because I don't want to chain those requests (they are time consuming).
Is it possible to change this behavior? And what security risks would come with it?
(I'm using Rails 4.1.0)
Many thanks
If the user doesn't already have a session cookie then there is nothing you can do.
If you can guarantee that the user already has a session (for example, if you require users to be logged in) then you may be able to do this with a server side session store.
With a server side session store the session cookie just contains an identifier - even if your overlapping ajax requests change values in the session they will not change the session cookie. In general this is better security wise: for example, old sessions can't be replayed after the user has logged out. Rails switched to the cookie store by default for performance reasons: no external data store needs to be accessed (however it does slightly increase the amount of data sent on each request)
Switching to a serverside session store isn't enough though and still leaves you open to race conditions. It is very easy to end up with a sequence along the lines of
Request A loads session
Request B loads session
Request B completes, saves session
Request A saves session and overwrites the session changes made by B
You need a session store that will attempt to merge any changes it has made with any changes that may have occurred from other requests.
I wrote such a session store some time ago. I haven't updated it for rails 4, since it isn't something i need anymore but you may be able to (or at least find inspiration in it)
I'm coming from PHP background and have a question regarding RoR user sessions. By default PHP uses file storage with write locks for user session data. So it prevents processing of multiple requests by the same client at the same time. How does RoR behaves with sessions?
The default session store in rails store the entirety of the session data in the session cookie itself (known as the cookiestore).
One side effect of this is that if 2 overlapping requests both try and update the session then the last one to send a response back to the client 'wins'.
I don't think any of the session stores commonly in use with Rails have the concurrency property you describe.
I am learning Rails by reading the online guide(for Rails v2.3). The guide is great, however, there is a confusion for me, that's:
there is a chapter explains the Session of Rails and another chapter explains Cookies of Rails. The explanation is easy to understand separately, but when compare the two, reader like me does not see the significant difference between Session and Cookies . Especially under which situation session should be used and under which situation Cookies should be used ?
Besides, in the session chapter, there is a concept of CookieStore , what is the difference between the CookieStore and Cookies then?
Could someone explain to me these?
Sessions & Cookies both hold the ability to store some information (e.g : the current_user id) in between two or more requests which (in http) are otherwise stateless.
But Session is more of an abstract concept related to the notion of being in a certain state for a specific amount of time : the info it contains can be stored in the database, in a server side file, in a redis hash OR in a cookie.
Cookies are always the little text file navigators have to store some persistent data in between requests... But having some data on the client side can be insecure so that's why it is often encrypted. But it's true the notion can overlap with session.
TL;DR : session the abstract concept of holding temporary data. Cookies one (common) way of doing it.
A cookie is a small text file stored in the browser.
A session is the concept of a state of being "in-use", and that state can have data associated with it. Rails keeps track of sessions with cookies, and lets you choose different storage for associated data and access it with the same session interface.
CookieStore means all the session information is stored inside the cookie itself. You can choose to use various other stores where appropriate, and it'll still be available with your session accessor methods.
In addition to the session, you can set other cookies to store information on the user's browser. These are not tied to the session and can be set, accessed and deleted independently.
Example 1, storing a logged-in user's shopping cart in a session:
session[:embarassing_products] = ['ooh',
'naughty',
'lucky_im_using_activerecord_store',
'only_the_session_id_is_in_the_cookie',
'other_data_arent_in_the_browser']
The shopping cart is preserved for the user's session. You can set the session to end when the browser window is closed, when the user logs out, or when a certain amount of time passes.
Example 2, remembering a browser's last language preference for your domain in a cookie:
cookie[:lang] = 'en-US'
This information is stored inside the cookie itself. Unless the cookie expires or is deleted (by you or the user), it stays inside the browser.
As to me the main difference is that the session data stored on the server, whereas the cookies are stored on the client (browser).
So you can trust the data from the session. Information from the cookie can be manipulated, stolen, and thus should not be relied on for critical use (for right access for example).
Second point, is that cookies have a limited size, and are only text-based. You can store in session many complex objects (but beware of memory consumpation), and you don't have to transfer them to client then back at each request.
And typically the session only persists until the user shuts down their browser. That's useful for typical logins. Whereas if you needed information to persist between sessions you could use a cookie with a longer duration, for example a 'remember me' flag that persists even after the browser is restarted.
I'd like to store some persistent data for each browser (user settings), and don't want to require a login to use the site. What's the bset way to achieve this?
I've considered two approaches:
Store the info in the session cookie. The problem with this approach is that I would need to set the expiration time on the session cookie, which has the side effect of causing a user's login session to not expire when the browser closes.
Store the info in the DB and store a retrieval key in a cookie on the client-side. I'm concerned about performance issues, as this would require additional queries and possibly some deserialization to retrieve the data. It looks like Rails switched its default from ActiveRecordStore a while back due to performance reasons: https://web.archive.org/web/20120102024844/https://www.ryandaigle.com/articles/2007/2/21/what-s-new-in-edge-rails-cookie-based-sessions
What's the recommended way to achieve this?
Why not just use a non-session cookie? You can specify how long it will persist on the client, etc. See http://api.rubyonrails.org/classes/ActionController/Cookies.html
If you're concerned about the user messing with the cookie data, you can encrypt the cookie data using a technique like this (taken from http://www.neeraj.name/blog/articles/834-how-cookie-stores-session-data-in-rails):
cookie_data = {:foo => "bar"}
digest = 'SHA1'
secret = 'my_encryption_secret'
data = ActiveSupport::Base64.encode64s(Marshal.dump(cookie_data))
digest_value = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(digest), secret, data)
escaped_data_value = Rack::Utils.escape(data)
final_output = "#{escaped_data_value}--#{digest_value}"
cookies[:user_data] = final_output
and later to read the cookie data:
Marshal.load(ActiveSupport::Base64.decode64(cookies[:user_data]))
It was actually changed from a file-based session store to a cookie-based session store - not from ActiveRecord. I may be wrong but I believe ActiveRecord was a viable option for web farms or distributed setups when a file-based store was the default. Since the cookie-based store was introduced, the web farm scenario has become a non-issue because it's stored at the client-side. Today ActiveRecord is still a viable option where you want to store a greater quantity of data than a cookie permits, when you want to cutdown the overhead of data transmitted in each request (using ActiveRecord means you're only transmitting the session_id), or if you want a centralized-session setup.
I find that in employing an ActiveRecord session store, speed is not a factor for me. In a cookie-based session you generally limit what you store in your session variables anyway, so they tend to be tokens used to lookup data in the database. If the data doesn't need to be persisted beyond the session, then it's viable to store the object in the session rather than just a token, because you're going to hit the database for the associated records anyway. If the object you want to retrieve involves an expensive retrieval operation, and you need it during the lifetime of the session, it might make sense to store that object in the session rather than just a token, and only hit the db once when the session is first established.
Cookies are great, but remember also that a user can delete them when they want, encrypted or not. With privacy concerns on the rise, and quite a few plugins available for browsers that conditionally block cookies, and software that cleans up cookies, you might want to weigh up the pros and cons of using cookies vs. a db-backed session.
#joshsz makes a good point about using a non-session cookie for persisting data beyond the session too. Remember a session has a finite lifetime.