session_injector 0.0.1.snapshot → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ Overview
2
+ ========
3
+
4
+ If you are developing an application that serves subdomains, the `:all` cookie store domain parameter will most likely serve your needs. However if your application serves distinct domains, you will most likely encounter some difficulties, as secure browsers will not accept "third party cookies" (i.e. any cookies you issue for a different domain will be disregarded).
5
+
6
+ There are a couple of approaches, neither of which are particularly elegant: http://stackoverflow.com/questions/263010/whats-your-favorite-cross-domain-cookie-sharing-approach
7
+
8
+ This gem provides a middleware that implements a "handshake" protocol based on a token inserted into a URL parameter, which allows you to transparently re-establish a Rack/Rails session accross domains. It parses incoming parameters for the handshake/token parameter, decrypts and verifies the token, and sets the session id in the request, thereby re-establishing the session on the target domain.
9
+
10
+ Usage
11
+ =====
12
+
13
+ If you are using Rails, insert this into your `config/application.rb`:
14
+
15
+ config.middleware.insert_before ActionDispatch::Cookies, "Rack::Middleware::SessionInjector", :key => '_your_session'
16
+
17
+ Configuration options:
18
+
19
+ # the 'key' for your session (if you have set a custom session key)
20
+ @session_id_key = options[:key]
21
+ # the encryption key. omit for a dynamically generated key
22
+ @token_key = options[:token_key] || generated_token_key
23
+ # receiver-enforced lifetime of token. default: 5 seconds
24
+ @enforced_lifetime = options[:token_lifetime]
25
+ # should we die when we recieve an invalid token, or just continue (without session injection naturally)
26
+ @die_on_handshake_failure = options[:die_on_handshake_failure]
27
+
28
+ There are three public methods through which you can initiate the session transfer:
29
+
30
+ Rack::Middleware::SessionInjector.generate_handshake_token(request, target_domain, lifetime = nil)
31
+ Rack::Middleware::SessionInjector.generate_handshake_parameter(request, target_domain, lifetime = nil)
32
+ Rack::Middleware::SessionInjector.propagate_session(request, target_domain, lifetime = nil)
33
+
34
+ you can append the parameter to a link:
35
+
36
+ link_to "http://otherdomain?#{Rack::Middleware::SessionInjector.generate_handshake_parameter(request, 'myotherhost')}"
37
+
38
+ or tell the middleware to rewrite the Location header on an HTTP redirect response:
39
+
40
+ Rack::Middleware::SessionInjector.propagate_session(request, 'myotherhost')
41
+
42
+ or you can just generate the token and use some custom method to convey it to the request on the target domain:
43
+
44
+ token = Rack::Middleware::SessionInjector.generate_handshake_token(request, 'myotherhost')
45
+
46
+ Security
47
+ ========
48
+
49
+ The "handshake" token is generated via `ActiveSupport::MessageEncryptor` using a dynamically generated key (although you can specify a static key yourself).
50
+
51
+ The token data consists of:
52
+
53
+ handshake = {
54
+ :request_ip => request.ip,
55
+ :request_path => request.fullpath, # more for accounting/stats than anything else
56
+ :src_domain => request.host,
57
+ :tgt_domain => target_domain,
58
+ :token_create_time => Time.now.to_i,
59
+ # the most important thing
60
+ :session_id => extract_session_id(request, session_injector.session_id_key)
61
+ }
62
+
63
+ This token is verified in the following manner:
64
+
65
+ * client request ip must match
66
+ * target domain must match
67
+ * token must not be older than receiver-specified lifetime
68
+ * token must not be older than sender-specified lifetime
@@ -7,6 +7,10 @@ module Rack
7
7
 
8
8
  class InvalidHandshake < StandardError; end
9
9
 
10
+ RACK_COOKIE_STRING = 'rack.request.cookie_string'.freeze
11
+ RACK_COOKIE_HASH = 'rack.request.cookie_hash'.freeze
12
+ HTTP_COOKIE = 'HTTP_COOKIE'.freeze
13
+
10
14
  DEFAULT_OPTIONS = {
11
15
  # use the AbstractStore default key as our session id key
12
16
  # if you have configured a custom session store key, you must
@@ -149,12 +153,29 @@ module Rack
149
153
  request = Rack::Request.new(env)
150
154
  token = request.params[HANDSHAKE_PARAM]
151
155
  return unless token
152
- # decrypt the token and set the session id
156
+
157
+ # decrypt the token and get the session cookie value
153
158
  handshake = decrypt_handshake_token(token, env)
154
- #env[@session_id_key] = handshake[:session_id] if handshake
155
- request.cookies[@session_id_key] = handshake[:session_id]
159
+ return unless handshake
160
+
161
+ cookie_value = handshake[:session_id]
162
+
163
+ # fix up Rack env
164
+ # ensure the cookie string is set
165
+ env[HTTP_COOKIE] = [env[HTTP_COOKIE], "#{@session_id_key}=#{cookie_value}"].compact.join(';')
166
+ # Rack request object parses cookies on demand and stores data in internal env keys
167
+ # but the current implementation is not good about writing back through to the env
168
+ # Since requests objects are transient wrappers we have to be prepared to encounter an env
169
+ # that may already be initialized with some state
170
+ # if the cookie string has already been read by Rack, update Rack's internal cookie string variable
171
+ if env[RACK_COOKIE_STRING]
172
+ env[RACK_COOKIE_STRING] = [env[RACK_COOKIE_STRING], "#{@session_id_key}=#{cookie_value}"].compact.join(';')
173
+ end
174
+ # if the cookie string has already been read by Rack, update Rack's internal cookie hash variable
175
+ request = Rack::Request.new(env)
176
+ request.cookies[@session_id_key] = cookie_value # call cookies() to make Rack::Request do its stuff
156
177
  end
157
-
178
+
158
179
  # decrypts a handshake token sent to us from a source domain
159
180
  def decrypt_handshake_token(token, env)
160
181
  handshake = ActiveSupport::MessageEncryptor.new(@token_key).decrypt_and_verify(token);
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  module SessionInjector
3
- VERSION = "0.0.1.snapshot"
3
+ VERSION = "0.0.1"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: session_injector
3
3
  version: !ruby/object:Gem::Version
4
- hash: -2083286358
5
- prerelease: true
4
+ hash: 29
5
+ prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
9
  - 1
10
- - snapshot
11
- version: 0.0.1.snapshot
10
+ version: 0.0.1
12
11
  platform: ruby
13
12
  authors:
14
13
  - Aaron Hamid
@@ -16,7 +15,7 @@ autorequire:
16
15
  bindir: bin
17
16
  cert_chain: []
18
17
 
19
- date: 2011-04-02 00:00:00 -04:00
18
+ date: 2011-04-04 00:00:00 -04:00
20
19
  default_executable:
21
20
  dependencies:
22
21
  - !ruby/object:Gem::Dependency
@@ -60,6 +59,7 @@ extra_rdoc_files: []
60
59
  files:
61
60
  - lib/session_injector/version.rb
62
61
  - lib/session_injector.rb
62
+ - README.md
63
63
  has_rdoc: true
64
64
  homepage: http://github.com/incandescent/session-injector
65
65
  licenses: []