The provided session management middleware that comes with Rack are all based on cookies for identifying the user. Since I'm developing an api, I would rather pass the session-id explicitly as a query string parameter. Looking at the code base, it doesn't seem that this use case was taken into consideration, as all the session middlewares extend from a common class, that reads/writes to cookies.
So my question is - Is there a project that maintains an alternative Rack middleware or a monkey patch for racks built-in middleware's, that will allow me to maintain the session-id over the query string, rather than a cookie store?
Rack middleware is a way to filter a request and response coming into your application. A middleware component sits between the client and the server, processing inbound requests and outbound responses, but it's more than interface that can be used to talk to web server.
2.1 Rails Application's Rack Objectapplication is the primary Rack application object of a Rails application. Any Rack compliant web server should be using Rails.
Rack is a modular interface between web servers and web applications developed in the Ruby programming language. With Rack, application programming interfaces (APIs) for web frameworks and middleware are wrapped into a single method call handling HTTP requests and responses.
Rack can use custom session ID items instead of cookies:
require 'rack/session/abstract/id'
The Rack documentation may be a helpful place to start your search. I believe you're looking for the "skip" option (or "defer" option).
Docs:
ID sets up a basic framework for implementing an id based sessioning service. Cookies sent to the client for maintaining sessions will only contain an id reference. Only #get_session and #set_session are required to be overwritten.
All parameters are optional.
These options can be set on a per request basis, at the location of env['rack.session.options']. Additionally the id of the session can be found within the options hash at the key :id. It is highly not recommended to change its value.
Is Rack::Utils::Context compatible.
Not included by default; you must require 'rack/session/abstract/id' to use.
Source:
class ID
DEFAULT_OPTIONS = {
:key => 'rack.session',
:path => '/',
:domain => nil,
:expire_after => nil,
:secure => false,
:httponly => true,
:defer => false,
:renew => false,
:sidbits => 128,
:cookie_only => true,
:secure_random => (::SecureRandom rescue false)
}
I hope this gives you a lead... when you learn more, can you share your results here?
The magic trick is to combine options :cookie_only => false
with :defer => true
. Of course, the standard Rack::Session::Cookie doesn't make much sense here, so you could do:
use Rack::Session::Pool, :cookie_only => false, :defer => true
Interestingly you can alter the options in run time. In my use case, I actually need to support a traditional cookie-based mechanism alongside the explicit parameter-passing style, so I have done the following:
class WebApp < Sinatra::Base
configure do
use Rack::Session::Pool, :key => 'session_id'
end
before do
# Switch to parameter based session management if the client is an ios device
if env['HTTP_USER_AGENT'] =~ /iOS/
session.options[:cookie_only] = false
session.options[:defer] = true
end
end
get '/' do
session[:user_id] ||= nil # This triggers a session-write, giving us a valid session-id
body "session_id=#{session.id}"
end
end
If you want to eliminate usage of cookies in your API application, but still want to manage sessions. For instance, in my case session identifier came from token.
You need to redefine method extract_session_id
to extract you session identifier from received token. This method have to be redefined on your session store class, because Rack::Session::Abstract::ID
provides default implementation, based on cookies.
It is called from Rack::Session::Abstract::ID#current_session_id
method which calls #id
method on session object, usually represented by Rack::Session::Abstract::SessionHash
instance. And inside this SessionHash#id
method #extract_session_id
from your session store finally called.
I suppose it would be simpler to have session identifier extractor configured independently, but desire to store session identifiers in cookies along with session data lead to that tricked design.
As well it is a bit weird to see cookie interaction in Rack::Session::Abstract::ID
class in method #commit_session
and #set_cookie
.
So to be completely sure that no cookie will be set you could redefine #set_cookie
method on your store as well. The same goal probably can be achived by setting cookie_only: false, defer: true
for your session storage middleware, as mentioned in one of the answer here, but I have not checked this.
Definitely you need to modify your middleware stack to exclude all middlewares which interact with cookies and likely browser specific. On default middleware stack of rails it can be look like this:
# config/application.rb
[
Rack::MethodOverride, # browser specific
ActionDispatch::Cookies,
ActionDispatch::Flash
].each do |middleware|
config.middleware.delete(middleware)
end
As well you definitely need to replace session store to something which store information on server side, like redis, for example:
# config/initializers/session_store.rb
Rails.application.config.session_store ::Custom::Session::TokenRedisStore
In my case, ::Custom::Session::TokenRedisStore
inherits ::RedisSessionStore
and redefined all methods mentioned above.
::RedisSessionStore
contained in redis-session-store
gem. So, obviously you need to add it to you Gemfile
if you going to use it.
I do that for Rails 4.2.x, but same approach can be adopbed to any framework as Rack
everywhere the same.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With