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 +68 -0
- data/lib/session_injector.rb +25 -4
- data/lib/session_injector/version.rb +1 -1
- metadata +5 -5
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
|
data/lib/session_injector.rb
CHANGED
@@ -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
|
-
|
156
|
+
|
157
|
+
# decrypt the token and get the session cookie value
|
153
158
|
handshake = decrypt_handshake_token(token, env)
|
154
|
-
|
155
|
-
|
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);
|
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:
|
5
|
-
prerelease:
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
9
|
- 1
|
10
|
-
|
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-
|
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: []
|