facebook-signed-request 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in facebook-signed-request.gemspec
4
+ gemspec
data/README.markdown ADDED
@@ -0,0 +1,35 @@
1
+ ## Usage
2
+
3
+ ```ruby
4
+ require 'facebook-signed-request'
5
+ request = Facebook::SignedRequest.new( params[:signed_request], secret )
6
+
7
+ request.valid?
8
+ # => true / false
9
+
10
+ request.errors
11
+ # => [
12
+ # "Invalid Format. See http://developers.facebook.com/docs/authentication/signed_request/",
13
+ # "Invalid Base64 encoding for signature",
14
+ # "Invalid Base64 Encoding for data",
15
+ # "Invalid JSON object",
16
+ # "Invalid Algorithm. Expected: HMAC-SHA256",
17
+ # "Signatures do not match. #{expected} but was #{computed}"
18
+ #]
19
+
20
+ request.data
21
+ # => {
22
+ # "algorithm"=>"HMAC-SHA256",
23
+ # "expires"=>1308988800,
24
+ # "issued_at"=>1308985018,
25
+ # "oauth_token"=>"114998258593813|2.AQBAttRlLVnwqNPZ.3600.1308988800…",
26
+ # "user"=> {
27
+ # "country"=>"de",
28
+ # "locale"=>"en_US",
29
+ # "age"=>{"min"=>21}
30
+ # },
31
+ # "user_id"=>"100000656666199"
32
+ # }
33
+
34
+
35
+ ```
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "facebook-signed-request/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "facebook-signed-request"
7
+ s.version = Facebook::Signed::Request::VERSION
8
+ s.authors = ["hukl"]
9
+ s.email = ["contact@smyck.org"]
10
+ s.homepage = ""
11
+ s.summary = %q{Parses and validates Facebook signed requests}
12
+ s.description = %q{Parses and validates Facebook signed requests}
13
+
14
+ s.rubyforge_project = "facebook-signed-request"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ end
@@ -0,0 +1,13 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'facebook-signed-request'))
2
+
3
+ require "facebook-signed-request/version"
4
+ require 'digest'
5
+ require 'base64'
6
+ require 'json'
7
+ require 'signed_request'
8
+
9
+ module Facebook
10
+
11
+
12
+
13
+ end
@@ -0,0 +1,79 @@
1
+ module Facebook
2
+ class SignedRequest
3
+ attr_reader :errors, :signature, :data
4
+
5
+ def initialize( request_data, secret )
6
+ @encoded_signature, @encoded_data = request_data.split(".", 2)
7
+
8
+ if @encoded_signature.nil? || @encoded_data.nil?
9
+ raise ArgumentError, "Invalid Format. See http://developers.facebook.com/docs/authentication/signed_request/"
10
+ end
11
+
12
+ @errors = []
13
+ @secret = secret
14
+
15
+ @signature = extract_request_signature
16
+ @payload = extract_request_payload
17
+ @data = parse_request_playload
18
+
19
+ validate_algorithm
20
+ validate_signature
21
+ end
22
+
23
+
24
+ def extract_request_signature
25
+ begin
26
+ return base64_url_decode(@encoded_signature).unpack('H*')[0]
27
+ rescue ArgumentError
28
+ @errors << "Invalid Base64 encoding for signature"
29
+ return nil
30
+ end
31
+ end
32
+
33
+ def extract_request_payload
34
+ begin
35
+ base64_url_decode(@encoded_data)
36
+ rescue ArgumentError
37
+ @errors << "Invalid Base64 Encoding for data"
38
+ return nil
39
+ end
40
+ end
41
+
42
+ def parse_request_playload
43
+ begin
44
+ return JSON.parse( @payload )
45
+ rescue
46
+ @errors << "Invalid JSON object"
47
+ return nil
48
+ end
49
+ end
50
+
51
+ def validate_algorithm
52
+ if @data.nil? || @data['algorithm'] != "HMAC-SHA256"
53
+ @errors << "Invalid Algorithm. Expected: HMAC-SHA256"
54
+ end
55
+ end
56
+
57
+ def validate_signature
58
+ digestor = Digest::HMAC.new( @secret, Digest::SHA256 )
59
+ computed_signature = digestor.hexdigest( @encoded_data )
60
+
61
+ if @signature != computed_signature
62
+ message = "Signatures do not match. " \
63
+ "Computed: #{computed_signature} but was #{@signature.inspect}"
64
+
65
+ @errors << message
66
+ end
67
+ end
68
+
69
+ def base64_url_decode( encoded_string )
70
+ encoded_string << '=' until ( encoded_string.length % 4 == 0 )
71
+ Base64.strict_decode64(encoded_string.gsub("-", "+").gsub("_", "/"))
72
+ end
73
+
74
+ def valid?
75
+ @errors.empty?
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,7 @@
1
+ module Facebook
2
+ module Signed
3
+ module Request
4
+ VERSION = "0.0.2"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,41 @@
1
+ $LOAD_PATH.unshift( File.dirname(__FILE__) )
2
+
3
+ require 'test_helper'
4
+
5
+ class SignedRequestTest < Test::Unit::TestCase
6
+
7
+ def setup
8
+ @secret = "897a956a2f7eadcc5783a458fe3e7556"
9
+ @valid_request = "vl0p_bGyDeVZ2I21cJvLd5C9CwpMkU2mcp1eUGWdvWs.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEzMDg5ODg4MDAsImlzc3VlZF9hdCI6MTMwODk4NTAxOCwib2F1dGhfdG9rZW4iOiIxMTQ5NTIyOTg1OTM4MTN8Mi5BUUJBdHRSbExWbndxTlBaLjM2MDAuMTMwODk4ODgwMC4xLTEwMDAwMDY1NDM0MzE5OXxUNDl3M0Jxb1pVZWd5cHJ1NTFHcmE3MGhFRDgiLCJ1c2VyIjp7ImNvdW50cnkiOiJkZSIsImxvY2FsZSI6ImVuX1VTIiwiYWdlIjp7Im1pbiI6MjF9fSwidXNlcl9pZCI6IjEwMDAwMDY1NDM0MzE5OSJ9"
10
+ @invalid_request_1 = "l0p_bGyDeVZ2I21cJvLd5C9CwpMkU2mcp1eUGWdvWs.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEzMDg5ODg4MDAsImlzc3VlZF9hdCI6MTMwODk4NTAxOCwib2F1dGhfdG9rZW4iOiIxMTQ5NTIyOTg1OTM4MTN8Mi5BUUJBdHRSbExWbndxTlBaLjM2MDAuMTMwODk4ODgwMC4xLTEwMDAwMDY1NDM0MzE5OXxUNDl3M0Jxb1pVZWd5cHJ1NTFHcmE3MGhFRDgiLCJ1c2VyIjp7ImNvdW50cnkiOiJkZSIsImxvY2FsZSI6ImVuX1VTIiwiYWdlIjp7Im1pbiI6MjF9fSwidXNlcl9pZCI6IjEwMDAwMDY1NDM0MzE5OSJ9"
11
+ @invalid_request_2 = "vl0p_bGyDeVZ2I21cJvLd5C9CwpMkU2mcp1eUGWdvWs.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEzMDg5ODg4MDAsImlzc3VlZF9hdCI6MTMwODk4NTAxOCwib2F1dGhfdG9rZW4iOiIxMTQ5NTIyOTg1OTM4MTN8Mi5BUUJBdHRSbExWbndxTlBaLjM2MDAuMTMwODk4ODgwMC4xLTEwMDAwMDY1NDM0MzE5OXxUNDl3M0Jxb1pVZWd5cHJ1NTFHcmE3MGhFRDgiLCJ1c2VyIjp7ImNvdW50cnkiOiJkZSIsImxvY2FsZSI6ImVuX1VTIiwiYWdlIjp7Im1pbiI6MjF9fSwidXNlcl9pZCI6IjEwMDAwMDY1NDM0MzE5OSJ"
12
+
13
+ @foo = "Wkq_aQu7mLjm54kOMTXoQrfa-q0_FyHcwFIBeLXNMas.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEzMDkwODYwMDAsImlzc3VlZF9hdCI6MTMwOTA4MDg3MCwib2F1dGhfdG9rZW4iOiIxMTQ5NTIyOTg1OTM4MTN8Mi5BUUJCSXVhZlhlek5xdlR2LjM2MDAuMTMwOTA4NjAwMC4xLTEwMDAwMDY1NDM0MzE5OXxGN3RGZkQ4U2tkRmgydE9tMDAwOG5fRVBJcVkiLCJ1c2VyIjp7ImNvdW50cnkiOiJkZSIsImxvY2FsZSI6ImVuX1VTIiwiYWdlIjp7Im1pbiI6MjF9fSwidXNlcl9pZCI6IjEwMDAwMDY1NDM0MzE5OSJ9"
14
+ end
15
+
16
+ test "parsing a valid request" do
17
+ request = Facebook::SignedRequest.new( @valid_request, @secret )
18
+ assert request.valid?, "Request should be valid"
19
+ assert request.errors == [], "Request should contain no errors"
20
+ end
21
+
22
+ test "parsing a request with invalid signature" do
23
+ request = Facebook::SignedRequest.new( @invalid_request_1, @secret )
24
+ assert_equal false, request.valid?
25
+ assert_equal 2, request.errors.length
26
+ end
27
+
28
+ test "parsing a request with invalid payload" do
29
+ request = Facebook::SignedRequest.new( @invalid_request_2, @secret )
30
+ assert_equal false, request.valid?
31
+ assert_equal 4, request.errors.length
32
+ end
33
+
34
+ test "re-encoding of payload" do
35
+ request = Facebook::SignedRequest.new( @foo, @secret )
36
+
37
+ puts Base64.encode64 request.data.to_json.to_s
38
+
39
+
40
+ end
41
+ end
@@ -0,0 +1,26 @@
1
+ $LOAD_PATH.unshift( File.join( File.dirname(__FILE__), "..", "lib") )
2
+
3
+ require 'test/unit'
4
+ require 'facebook-signed-request'
5
+
6
+ class Test::Unit::TestCase
7
+
8
+ unless defined?(Spec)
9
+ # test "verify something" do
10
+ # ...
11
+ # end
12
+ def self.test(name, &block)
13
+ test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
14
+ defined = instance_method(test_name) rescue false
15
+ raise "#{test_name} is already defined in #{self}" if defined
16
+ if block_given?
17
+ define_method(test_name, &block)
18
+ else
19
+ define_method(test_name) do
20
+ flunk "No implementation provided for #{name}"
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: facebook-signed-request
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - hukl
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-06-26 00:00:00.000000000 +02:00
13
+ default_executable:
14
+ dependencies: []
15
+ description: Parses and validates Facebook signed requests
16
+ email:
17
+ - contact@smyck.org
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - .gitignore
23
+ - Gemfile
24
+ - README.markdown
25
+ - Rakefile
26
+ - facebook-signed-request.gemspec
27
+ - lib/facebook-signed-request.rb
28
+ - lib/facebook-signed-request/signed_request.rb
29
+ - lib/facebook-signed-request/version.rb
30
+ - test/signed_request_test.rb
31
+ - test/test_helper.rb
32
+ has_rdoc: true
33
+ homepage: ''
34
+ licenses: []
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubyforge_project: facebook-signed-request
53
+ rubygems_version: 1.6.2
54
+ signing_key:
55
+ specification_version: 3
56
+ summary: Parses and validates Facebook signed requests
57
+ test_files:
58
+ - test/signed_request_test.rb
59
+ - test/test_helper.rb