rack-facebook-signed-request 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,9 +1,4 @@
1
- source :rubygems
1
+ source "http://rubygems.org"
2
2
 
3
- gem 'yajl-ruby'
4
- gem 'rack'
5
-
6
- group :development do
7
- gem 'jeweler'
8
- gem 'rcov'
9
- end
3
+ # Specify your gem's dependencies in ..gemspec
4
+ gemspec
data/README CHANGED
@@ -1,23 +1,37 @@
1
1
  = rack-facebook-signed-request
2
2
 
3
- Simple rack middleware which parses and verifies the signed_request canvas parameter.
3
+ Rack middleware which parses and verifies the signed_request canvas parameter and FB JS cookie.
4
4
 
5
5
  See:
6
6
 
7
7
  http://developers.facebook.com/docs/authentication/canvas
8
8
 
9
- == Contributing to rack-facebook-signed-request
10
-
11
- * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
12
- * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
13
- * Fork the project
14
- * Start a feature/bugfix branch
15
- * Commit and push until you are happy with your contribution
16
- * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
17
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
9
+ Required Options:
18
10
 
19
- == Copyright
11
+ You must specify the following options to enable the middleware:
20
12
 
21
- Copyright (c) 2010 . See LICENSE.txt for
22
- further details.
13
+ - app_id
23
14
 
15
+ - secret
16
+
17
+ Additional Custom Options:
18
+
19
+ You can also activate the following options:
20
+
21
+ - inject_facebook (default false): This will automatically inject the asynchronous FB JS SDK include into the response body.
22
+
23
+ Assuming you've enabled the Facebook script injection, you can customize these options:
24
+
25
+ - cookie (default true): Configure the FB JS SDK with cookie support
26
+
27
+ - status (default true)
28
+
29
+ - lang (default 'en_US')
30
+
31
+ - xfbml (default true)
32
+
33
+ Note that this will also add the FB XML namespace attribute into the root html element of the response.
34
+
35
+ RESTful behavior:
36
+
37
+ The Rack middleware will also convert any POST requests containing the signed_request parameter to GET.
data/Rakefile CHANGED
@@ -1,57 +1 @@
1
- require 'rubygems'
2
- require 'bundler'
3
- begin
4
- Bundler.setup(:default, :development)
5
- rescue Bundler::BundlerError => e
6
- $stderr.puts e.message
7
- $stderr.puts "Run `bundle install` to install missing gems"
8
- exit e.status_code
9
- end
10
- require 'rake'
11
-
12
- require 'jeweler'
13
- Jeweler::Tasks.new do |gem|
14
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
- gem.name = "rack-facebook-signed-request"
16
- gem.homepage = "http://github.com/gamesthatgive/rack-facebook-signed-request"
17
- gem.license = "MIT"
18
- gem.summary = %Q{Simple Rack middle for parsing and validation Facebook signed_request param.}
19
- gem.description = %Q{See http://developers.facebook.com/docs/authentication/canvas}
20
- gem.email = "goss@gamesthatgive.net"
21
- gem.authors = ["Kristofer Goss"]
22
-
23
- gem.add_runtime_dependency 'rack'
24
- gem.add_runtime_dependency 'yajl-ruby'
25
-
26
- gem.add_development_dependency 'shoulda', '>= 0'
27
- gem.add_development_dependency 'bundler', '~> 1.0.0'
28
- gem.add_development_dependency 'jeweler', '~> 1.5.1'
29
- gem.add_development_dependency 'rcov', '>= 0'
30
- end
31
- Jeweler::RubygemsDotOrgTasks.new
32
-
33
- require 'rake/testtask'
34
- Rake::TestTask.new(:test) do |test|
35
- test.libs << 'lib' << 'test'
36
- test.pattern = 'test/**/test_*.rb'
37
- test.verbose = true
38
- end
39
-
40
- require 'rcov/rcovtask'
41
- Rcov::RcovTask.new do |test|
42
- test.libs << 'test'
43
- test.pattern = 'test/**/test_*.rb'
44
- test.verbose = true
45
- end
46
-
47
- task :default => :test
48
-
49
- require 'rake/rdoctask'
50
- Rake::RDocTask.new do |rdoc|
51
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
52
-
53
- rdoc.rdoc_dir = 'rdoc'
54
- rdoc.title = "rack-facebook-signed-request #{version}"
55
- rdoc.rdoc_files.include('README*')
56
- rdoc.rdoc_files.include('lib/**/*.rb')
57
- end
1
+ require 'bundler/gem_tasks'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -15,35 +15,63 @@ module Rack
15
15
  @options = options
16
16
  end
17
17
 
18
- def secret
19
- @options.fetch(:secret)
20
- end
21
-
22
18
  def call(env)
23
- request = Rack::Request.new(env)
19
+ @env = env
20
+ @request = Rack::Request.new(env)
24
21
 
25
- signed_request = request.params.delete('signed_request')
26
- unless signed_request
27
- return Rack::Response.new(["Missing signed_request param"], 400).finish
22
+ # RESTify the default POST request from Facebook
23
+ if request.POST['signed_request']
24
+ env['HTTP_METHOD'] = 'GET'
28
25
  end
29
26
 
30
- signature, signed_params = signed_request.split('.')
27
+ app_id, secret = [@options.fetch(:app_id), @options.fetch(:secret)]
28
+ facebook_params = resolve_from_signed_request!(secret) || resolve_from_cookie!(app_id, secret)
29
+ request.params['facebook_params'] = facebook_params if facebook_params
30
+ env['rack.request.query_hash'] = request.params
31
31
 
32
- unless signed_request_is_valid?(secret, signature, signed_params)
33
- return Rack::Response.new(["Invalid signature"], 400).finish
32
+ unless @options[:inject_facebook]
33
+ @app.call(env)
34
+ else
35
+ inject_facebook_script
34
36
  end
37
+ end
35
38
 
36
- signed_params = Yajl::Parser.new.parse(base64_url_decode(signed_params))
39
+ private
37
40
 
38
- # add JSON params to request
39
- signed_params.each do |k,v|
40
- request.params[k] = v
41
- end
41
+ attr_reader :request
42
42
 
43
- @app.call(env)
44
- end
43
+ def resolve_from_cookie!(app_id, secret)
44
+ # extract contents
45
+ cookie = request.cookies["fbs_#{app_id}"]
46
+ return nil unless cookie
45
47
 
46
- private
48
+ hash = {}
49
+ data = cookie.gsub(/"/,"")
50
+ data.split('&').each do |str|
51
+ parts = str.split('=')
52
+ hash[parts.first] = parts.last
53
+ end
54
+ unless signed_cookie_is_valid?(secret, hash)
55
+ return Rack::Response.new(["Invalid cookie signature"], 400).finish
56
+ end
57
+
58
+ # map cookie params to signed request equivalents
59
+ {
60
+ 'oauth_token' => hash.fetch('access_token'),
61
+ 'expires' => hash.fetch('expires'),
62
+ 'user_id' => hash.fetch('uid')
63
+ }
64
+ end
65
+
66
+ def resolve_from_signed_request!(secret)
67
+ return nil unless request.params['signed_request']
68
+ signed_request = request.params['signed_request']
69
+ signature, signed_params = signed_request.split('.')
70
+ unless signed_request_is_valid?(secret, signature, signed_params)
71
+ return Rack::Response.new(["Invalid signed request"], 400).finish
72
+ end
73
+ Yajl::Parser.new.parse(base64_url_decode(signed_params))
74
+ end
47
75
 
48
76
  def signed_request_is_valid?(secret, signature, params)
49
77
  signature = base64_url_decode(signature)
@@ -51,10 +79,56 @@ module Rack
51
79
  return signature == expected_signature
52
80
  end
53
81
 
82
+ def signed_cookie_is_valid?(secret, hash)
83
+ sorted_keys = hash.keys.reject {|k| k== 'sig'}.sort
84
+ test_string = ""
85
+ sorted_keys.each do |key|
86
+ test_string += "#{key}=#{hash[key]}"
87
+ end
88
+ test_string += secret
89
+ Digest::MD5.hexdigest(test_string) == hash['sig']
90
+ end
91
+
54
92
  def base64_url_decode(str)
55
93
  str = str + "=" * (6 - str.size % 6) unless str.size % 6 == 0
56
94
  return Base64.decode64(str.tr("-_", "+/"))
57
95
  end
96
+
97
+ # borrowed from Michael Bleigh's Rack Facebook_Connect for rewriting of the response body
98
+ def inject_facebook_script #:nodoc:
99
+ status, headers, responses = @app.call(@env)
100
+ responses = Array(responses) unless responses.respond_to?(:each)
101
+
102
+ if headers["Content-Type"] =~ %r{(text/html)|(application/xhtml+xml)}
103
+ resp = []
104
+ responses.each do |r|
105
+ r.sub! /(<html[^\/>]*)>/i, '\1 xmlns:fb="http://www.facebook.com/2008/fbml">'
106
+ r.sub! /<\/body>/i, <<-HTML
107
+ <div id="fb-root"></div>
108
+ <script>
109
+ window.fbAsyncInit = function() {
110
+ FB.init({
111
+ appId : '#{@options.fetch(:app_id)}',
112
+ status : #{@options[:status] || true}, // check login status
113
+ cookie : #{@options[:cookie] || true}, // enable cookies to allow the server to access the session
114
+ #{"channelUrl : '#{@options[:channel_url]}', // add channelURL to avoid IE redirect problems" if @options[:channel_url]}
115
+ xfbml : #{@options[:xfbml] || true} // parse XFBML
116
+ });
117
+ };
118
+
119
+ (function() {
120
+ var e = document.createElement('script'); e.async = true;
121
+ e.src = document.location.protocol + '//connect.facebook.net/#{@options[:lang] || 'en_US'}/all.js';
122
+ document.getElementById('fb-root').appendChild(e);
123
+ }());
124
+ </script>
125
+ </body>
126
+ HTML
127
+ resp << r
128
+ end
129
+ end
130
+ Rack::Response.new(resp || responses, status, headers).finish
131
+ end
58
132
  end
59
133
  end
60
134
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-facebook-signed-request
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Kristofer Goss
@@ -19,10 +19,9 @@ date: 2010-11-18 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- type: :runtime
23
- prerelease: false
24
22
  name: yajl-ruby
25
- version_requirements: &id001 !ruby/object:Gem::Requirement
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
26
25
  none: false
27
26
  requirements:
28
27
  - - ">="
@@ -31,12 +30,12 @@ dependencies:
31
30
  segments:
32
31
  - 0
33
32
  version: "0"
34
- requirement: *id001
35
- - !ruby/object:Gem::Dependency
36
33
  type: :runtime
37
- prerelease: false
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
38
36
  name: rack
39
- version_requirements: &id002 !ruby/object:Gem::Requirement
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
40
39
  none: false
41
40
  requirements:
42
41
  - - ">="
@@ -45,12 +44,12 @@ dependencies:
45
44
  segments:
46
45
  - 0
47
46
  version: "0"
48
- requirement: *id002
47
+ type: :runtime
48
+ version_requirements: *id002
49
49
  - !ruby/object:Gem::Dependency
50
- type: :development
51
- prerelease: false
52
50
  name: jeweler
53
- version_requirements: &id003 !ruby/object:Gem::Requirement
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
54
53
  none: false
55
54
  requirements:
56
55
  - - ">="
@@ -59,12 +58,12 @@ dependencies:
59
58
  segments:
60
59
  - 0
61
60
  version: "0"
62
- requirement: *id003
63
- - !ruby/object:Gem::Dependency
64
61
  type: :development
65
- prerelease: false
62
+ version_requirements: *id003
63
+ - !ruby/object:Gem::Dependency
66
64
  name: rcov
67
- version_requirements: &id004 !ruby/object:Gem::Requirement
65
+ prerelease: false
66
+ requirement: &id004 !ruby/object:Gem::Requirement
68
67
  none: false
69
68
  requirements:
70
69
  - - ">="
@@ -73,12 +72,12 @@ dependencies:
73
72
  segments:
74
73
  - 0
75
74
  version: "0"
76
- requirement: *id004
75
+ type: :development
76
+ version_requirements: *id004
77
77
  - !ruby/object:Gem::Dependency
78
- type: :runtime
79
- prerelease: false
80
78
  name: rack
81
- version_requirements: &id005 !ruby/object:Gem::Requirement
79
+ prerelease: false
80
+ requirement: &id005 !ruby/object:Gem::Requirement
82
81
  none: false
83
82
  requirements:
84
83
  - - ">="
@@ -87,12 +86,12 @@ dependencies:
87
86
  segments:
88
87
  - 0
89
88
  version: "0"
90
- requirement: *id005
91
- - !ruby/object:Gem::Dependency
92
89
  type: :runtime
93
- prerelease: false
90
+ version_requirements: *id005
91
+ - !ruby/object:Gem::Dependency
94
92
  name: yajl-ruby
95
- version_requirements: &id006 !ruby/object:Gem::Requirement
93
+ prerelease: false
94
+ requirement: &id006 !ruby/object:Gem::Requirement
96
95
  none: false
97
96
  requirements:
98
97
  - - ">="
@@ -101,12 +100,12 @@ dependencies:
101
100
  segments:
102
101
  - 0
103
102
  version: "0"
104
- requirement: *id006
103
+ type: :runtime
104
+ version_requirements: *id006
105
105
  - !ruby/object:Gem::Dependency
106
- type: :development
107
- prerelease: false
108
106
  name: shoulda
109
- version_requirements: &id007 !ruby/object:Gem::Requirement
107
+ prerelease: false
108
+ requirement: &id007 !ruby/object:Gem::Requirement
110
109
  none: false
111
110
  requirements:
112
111
  - - ">="
@@ -115,12 +114,12 @@ dependencies:
115
114
  segments:
116
115
  - 0
117
116
  version: "0"
118
- requirement: *id007
119
- - !ruby/object:Gem::Dependency
120
117
  type: :development
121
- prerelease: false
118
+ version_requirements: *id007
119
+ - !ruby/object:Gem::Dependency
122
120
  name: bundler
123
- version_requirements: &id008 !ruby/object:Gem::Requirement
121
+ prerelease: false
122
+ requirement: &id008 !ruby/object:Gem::Requirement
124
123
  none: false
125
124
  requirements:
126
125
  - - ~>
@@ -131,12 +130,12 @@ dependencies:
131
130
  - 0
132
131
  - 0
133
132
  version: 1.0.0
134
- requirement: *id008
135
- - !ruby/object:Gem::Dependency
136
133
  type: :development
137
- prerelease: false
134
+ version_requirements: *id008
135
+ - !ruby/object:Gem::Dependency
138
136
  name: jeweler
139
- version_requirements: &id009 !ruby/object:Gem::Requirement
137
+ prerelease: false
138
+ requirement: &id009 !ruby/object:Gem::Requirement
140
139
  none: false
141
140
  requirements:
142
141
  - - ~>
@@ -147,12 +146,12 @@ dependencies:
147
146
  - 5
148
147
  - 1
149
148
  version: 1.5.1
150
- requirement: *id009
151
- - !ruby/object:Gem::Dependency
152
149
  type: :development
153
- prerelease: false
150
+ version_requirements: *id009
151
+ - !ruby/object:Gem::Dependency
154
152
  name: rcov
155
- version_requirements: &id010 !ruby/object:Gem::Requirement
153
+ prerelease: false
154
+ requirement: &id010 !ruby/object:Gem::Requirement
156
155
  none: false
157
156
  requirements:
158
157
  - - ">="
@@ -161,9 +160,10 @@ dependencies:
161
160
  segments:
162
161
  - 0
163
162
  version: "0"
164
- requirement: *id010
163
+ type: :development
164
+ version_requirements: *id010
165
165
  description: See http://developers.facebook.com/docs/authentication/canvas
166
- email: goss@gamesthatgive.net
166
+ email: kris@vitrue.com
167
167
  executables: []
168
168
 
169
169
  extensions: []
@@ -180,8 +180,6 @@ files:
180
180
  - VERSION
181
181
  - lib/rack-facebook-signed-request.rb
182
182
  - lib/rack/facebook/signed_request.rb
183
- - pkg/rack-facebook-signed-request-0.1.0.gem
184
- - rack-facebook-signed-request.gemspec
185
183
  - test/helper.rb
186
184
  - test/test_rack-facebook-signed-request.rb
187
185
  has_rdoc: true
@@ -1,83 +0,0 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
5
-
6
- Gem::Specification.new do |s|
7
- s.name = %q{rack-facebook-signed-request}
8
- s.version = "0.1.0"
9
-
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Kristofer Goss"]
12
- s.date = %q{2010-11-18}
13
- s.description = %q{See http://developers.facebook.com/docs/authentication/canvas}
14
- s.email = %q{goss@gamesthatgive.net}
15
- s.extra_rdoc_files = [
16
- "LICENSE.txt",
17
- "README"
18
- ]
19
- s.files = [
20
- "Gemfile",
21
- "Gemfile.lock",
22
- "LICENSE.txt",
23
- "README",
24
- "Rakefile",
25
- "VERSION",
26
- "lib/rack-facebook-signed-request.rb",
27
- "lib/rack/facebook/signed_request.rb",
28
- "pkg/rack-facebook-signed-request-0.1.0.gem",
29
- "rack-facebook-signed-request.gemspec",
30
- "test/helper.rb",
31
- "test/test_rack-facebook-signed-request.rb"
32
- ]
33
- s.homepage = %q{http://github.com/gamesthatgive/rack-facebook-signed-request}
34
- s.licenses = ["MIT"]
35
- s.require_paths = ["lib"]
36
- s.rubygems_version = %q{1.3.7}
37
- s.summary = %q{Simple Rack middle for parsing and validation Facebook signed_request param.}
38
- s.test_files = [
39
- "test/helper.rb",
40
- "test/test_rack-facebook-signed-request.rb"
41
- ]
42
-
43
- if s.respond_to? :specification_version then
44
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
45
- s.specification_version = 3
46
-
47
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
48
- s.add_runtime_dependency(%q<yajl-ruby>, [">= 0"])
49
- s.add_runtime_dependency(%q<rack>, [">= 0"])
50
- s.add_development_dependency(%q<jeweler>, [">= 0"])
51
- s.add_development_dependency(%q<rcov>, [">= 0"])
52
- s.add_runtime_dependency(%q<rack>, [">= 0"])
53
- s.add_runtime_dependency(%q<yajl-ruby>, [">= 0"])
54
- s.add_development_dependency(%q<shoulda>, [">= 0"])
55
- s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
56
- s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
57
- s.add_development_dependency(%q<rcov>, [">= 0"])
58
- else
59
- s.add_dependency(%q<yajl-ruby>, [">= 0"])
60
- s.add_dependency(%q<rack>, [">= 0"])
61
- s.add_dependency(%q<jeweler>, [">= 0"])
62
- s.add_dependency(%q<rcov>, [">= 0"])
63
- s.add_dependency(%q<rack>, [">= 0"])
64
- s.add_dependency(%q<yajl-ruby>, [">= 0"])
65
- s.add_dependency(%q<shoulda>, [">= 0"])
66
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
67
- s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
68
- s.add_dependency(%q<rcov>, [">= 0"])
69
- end
70
- else
71
- s.add_dependency(%q<yajl-ruby>, [">= 0"])
72
- s.add_dependency(%q<rack>, [">= 0"])
73
- s.add_dependency(%q<jeweler>, [">= 0"])
74
- s.add_dependency(%q<rcov>, [">= 0"])
75
- s.add_dependency(%q<rack>, [">= 0"])
76
- s.add_dependency(%q<yajl-ruby>, [">= 0"])
77
- s.add_dependency(%q<shoulda>, [">= 0"])
78
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
79
- s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
80
- s.add_dependency(%q<rcov>, [">= 0"])
81
- end
82
- end
83
-