facebook_session 0.0.5 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 02a8da46d6b40c4957295584de6d46b3fe590399
4
- data.tar.gz: 91631fffb4376c561ac11c4d7c977908efc68ca3
3
+ metadata.gz: 7b5d4f0754ffc4ec193b5d4361c452ad0d4cbb6a
4
+ data.tar.gz: c6b7aab9c0612dd22cc62e349ca7bf3445061a42
5
5
  SHA512:
6
- metadata.gz: 1da341cdd46386525f7d0ceddadc4cf0f103fff1bb090010dc8168cb0c7e863d22d70b2f1305ed6e541743f7a67d38ce60c6f68a67412c604b048a652e2a0ef9
7
- data.tar.gz: 51ece5975d8db0c0f7df2ebb0d5fc069845435c67ffb3bbebb8a39765e88f91c7afb9bf723bf27f0c4427d4389512ff95ac059ced96ef45a160745b7fcf86858
6
+ metadata.gz: 4145e761156a6e411a39eb90fcc5a8af36834e84740d02c61b5503f67b59e5db8f64c18536a29d362757865395ac265fc0dd8baa03f392d571f3a8134c127251
7
+ data.tar.gz: 63a7742caf13f59f3eec877707e8e40c29912ca79ab2ba3cf3af2591878aa60730c7b1d57dc16070b845d72f2b82c3222c8d70c09d04a2423076d917b909186e
@@ -0,0 +1,17 @@
1
+ module FacebookSession
2
+ class DecodeableStruct
3
+ class << self
4
+ def decode(message)
5
+ self.new(FacebookSession.message_decoder.decode(message))
6
+ end
7
+ end
8
+
9
+ def initialize(data={})
10
+ data.each do |key, value|
11
+ if self.respond_to?("#{key.to_s}=".to_sym)
12
+ self.send("#{key.to_s}=".to_sym, value)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -2,10 +2,8 @@ module FacebookSession
2
2
  module Helper
3
3
 
4
4
  def facebook_session
5
- return @facebook_session if @facebook_session
6
- raise 'FacebookSession not configured!' unless FacebookSession.config?
7
- if facebook_cookie = cookies["fbsr_#{FacebookSession.application_id}"]
8
- @facebook_session = FacebookSession::Session.parse_cookie(facebook_cookie)
5
+ if message = cookies["fbsr_#{FacebookSession.application_id}"]
6
+ @facebook_session ||= FacebookSession::Session.decode(message)
9
7
  end
10
8
  end
11
9
 
@@ -14,10 +12,8 @@ module FacebookSession
14
12
  end
15
13
 
16
14
  def facebook_signed_request
17
- return @facebook_signed_request if @facebook_signed_request
18
- raise 'FacebookSession not configured!' unless FacebookSession.config?
19
- if facebook_signed_request = params[:signed_request]
20
- @facebook_signed_request = FacebookSession::SignedRequest.parse_request(facebook_signed_request)
15
+ if message = params[:signed_request]
16
+ @facebook_signed_request ||= FacebookSession::SignedRequest.decode(message)
21
17
  end
22
18
  end
23
19
 
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+
3
+ module FacebookSession
4
+ class MessageDecoder
5
+ def initialize(secret)
6
+ @secret = secret
7
+ end
8
+
9
+ def decode(string)
10
+ encoded_digest, payload = string.split('.')
11
+ digest = decode_base64(encoded_digest)
12
+
13
+ if valid_digest?(payload, digest)
14
+ parse_payload(payload)
15
+ else
16
+ raise FacebookSession::InvalidSignature
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def decode_base64(str)
23
+ str += '=' while str.length % 4 != 0 # Pad string with =
24
+ Base64.urlsafe_decode64(str)
25
+ end
26
+
27
+ def parse_payload(payload)
28
+ parsed_payload = JSON.parse(decode_base64(payload))
29
+ parsed_payload.symbolize_keys!
30
+ parsed_payload
31
+ end
32
+
33
+ def secure_compare(a, b)
34
+ return false unless a.bytesize == b.bytesize
35
+
36
+ l = a.unpack "C#{a.bytesize}"
37
+
38
+ res = 0
39
+ b.each_byte { |byte| res |= byte ^ l.shift }
40
+ res == 0
41
+ end
42
+
43
+ def generate_digest(data)
44
+ require 'openssl' unless defined?(OpenSSL)
45
+ OpenSSL::HMAC.digest(
46
+ OpenSSL::Digest.new('sha256'),
47
+ @secret,
48
+ data
49
+ )
50
+ end
51
+
52
+ def valid_digest?(data, digest)
53
+ data.present? && digest.present? && secure_compare(digest, generate_digest(data))
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,10 @@
1
+ require 'rails'
2
+
3
+ module PagesCore
4
+ class Railtie < ::Rails::Railtie
5
+ initializer :extend_rails do
6
+ ActionView::Base.send :include, FacebookSession::Helper
7
+ ActionController::Base.send :include, FacebookSession::Helper
8
+ end
9
+ end
10
+ end
@@ -1,21 +1,5 @@
1
1
  module FacebookSession
2
- class Session
2
+ class Session < DecodeableStruct
3
3
  attr_accessor :user_id, :oauth_token, :algorithm, :issued_at
4
-
5
- class << self
6
- def parse_cookie(cookie)
7
- if session_data = FacebookSession.decode_payload(cookie)
8
- self.new(session_data)
9
- else
10
- nil
11
- end
12
- end
13
- end
14
-
15
- def initialize(session_data={})
16
- session_data.each do |key, value|
17
- self.send("#{key.to_s}=".to_sym, value) if self.respond_to?("#{key.to_s}=".to_sym)
18
- end
19
- end
20
4
  end
21
5
  end
@@ -1,21 +1,6 @@
1
1
  module FacebookSession
2
- class SignedRequest
3
- attr_accessor :code, :algorithm, :issued_at, :user_id, :user, :oauth_token, :expires, :app_data, :page
4
-
5
- class << self
6
- def parse_request(request)
7
- if request_data = FacebookSession.decode_payload(request)
8
- self.new(request_data)
9
- else
10
- nil
11
- end
12
- end
13
- end
14
-
15
- def initialize(request_data={})
16
- request_data.each do |key, value|
17
- self.send("#{key.to_s}=".to_sym, value) if self.respond_to?("#{key.to_s}=".to_sym)
18
- end
19
- end
2
+ class SignedRequest < DecodeableStruct
3
+ attr_accessor :user_id, :oauth_token, :algorithm, :issued_at
4
+ attr_accessor :code, :user, :expires, :app_data, :page
20
5
  end
21
6
  end
@@ -1,3 +1,3 @@
1
1
  module FacebookSession
2
- VERSION = "0.0.5"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -1,60 +1,59 @@
1
+ require 'base64'
2
+
3
+ require File.join(File.dirname(__FILE__), 'facebook_session/decodeable_struct')
1
4
  require File.join(File.dirname(__FILE__), 'facebook_session/helper')
5
+ require File.join(File.dirname(__FILE__), 'facebook_session/message_decoder')
2
6
  require File.join(File.dirname(__FILE__), 'facebook_session/session')
7
+ require File.join(File.dirname(__FILE__), 'facebook_session/railtie')
3
8
  require File.join(File.dirname(__FILE__), 'facebook_session/signed_request')
4
9
 
5
10
  module FacebookSession
11
+ class InvalidSignature < StandardError; end
12
+ class NotConfiguredError < StandardError; end
13
+
6
14
  class << self
7
15
 
8
- def config
9
- @@config ||= {}
16
+ def clear_config!
17
+ @@config = nil
10
18
  end
11
19
 
12
20
  def config?
13
- self.config[:application_id] && self.config[:application_secret] ? true : false
21
+ config[:application_id] && config[:application_secret] ? true : false
14
22
  end
15
23
 
16
24
  def configure(options={})
17
25
  options.each do |key, val|
18
- self.config[key.to_sym] = val
26
+ config[key.to_sym] = val
19
27
  end
20
- self.config
28
+ config
21
29
  end
22
30
 
23
31
  def application_id
24
- self.config[:application_id]
32
+ if config[:application_id].kind_of?(Proc)
33
+ config[:application_id].call
34
+ else
35
+ config[:application_id]
36
+ end
25
37
  end
26
38
 
27
39
  def application_secret
28
- self.config[:application_secret]
40
+ if config[:application_secret].kind_of?(Proc)
41
+ config[:application_secret].call
42
+ else
43
+ config[:application_secret]
44
+ end
29
45
  end
30
46
 
31
- def base64_url_decode(string)
32
- encoded_string = string.gsub('-','+').gsub('_','/')
33
- encoded_string += '=' while (encoded_string.length % 4 != 0)
34
- Base64.decode64(encoded_string)
47
+ def message_decoder
48
+ raise NotConfiguredError unless config?
49
+ FacebookSession::MessageDecoder.new(application_secret)
35
50
  end
36
51
 
37
- def decode_payload(string)
38
- encoded_sig, payload = string.split('.')
39
- sig = base64_url_decode(encoded_sig)
40
- decoded_payload = JSON.parse(base64_url_decode(payload))
41
- decoded_payload.symbolize_keys!
42
-
43
- expected_sig = OpenSSL::HMAC.digest(
44
- OpenSSL::Digest.new('sha256'),
45
- FacebookSession.application_secret,
46
- payload
47
- )
52
+ private
48
53
 
49
- if sig == expected_sig
50
- decoded_payload
51
- else
52
- nil
53
- end
54
+ def config
55
+ @@config ||= {}
54
56
  end
55
-
56
57
  end
57
58
  end
58
59
 
59
- ActionView::Base.send :include, FacebookSession::Helper
60
- ActionController::Base.send :include, FacebookSession::Helper
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ describe FacebookSession do
4
+ before(:each) do
5
+ FacebookSession.clear_config!
6
+ end
7
+
8
+ describe ".config?" do
9
+ subject { FacebookSession.config? }
10
+
11
+ context "when no config is set" do
12
+ it { is_expected.to eq(false) }
13
+ end
14
+
15
+ context "when one option is missing" do
16
+ before { FacebookSession.configure(application_id: "foo") }
17
+ it { is_expected.to eq(false) }
18
+ end
19
+
20
+ context "when both options are set" do
21
+ before { FacebookSession.configure(application_id: "foo", application_secret: "bar") }
22
+ it { is_expected.to eq(true) }
23
+ end
24
+ end
25
+
26
+ describe ".application_id" do
27
+ subject { FacebookSession.application_id }
28
+
29
+ context "when unconfigured" do
30
+ it { is_expected.to eq(nil) }
31
+ end
32
+
33
+ context "when configured with a string" do
34
+ before { FacebookSession.configure(application_id: "foo") }
35
+ it { is_expected.to eq("foo") }
36
+ end
37
+
38
+ context "when configured with a proc" do
39
+ before { FacebookSession.configure(application_id: -> { "foo" }) }
40
+ it { is_expected.to eq("foo") }
41
+ end
42
+ end
43
+
44
+ describe ".application_secret" do
45
+ subject { FacebookSession.application_secret }
46
+
47
+ context "when unconfigured" do
48
+ it { is_expected.to eq(nil) }
49
+ end
50
+
51
+ context "when configured with a string" do
52
+ before { FacebookSession.configure(application_secret: "foo") }
53
+ it { is_expected.to eq("foo") }
54
+ end
55
+
56
+ context "when configured with a proc" do
57
+ before { FacebookSession.configure(application_secret: -> { "foo" }) }
58
+ it { is_expected.to eq("foo") }
59
+ end
60
+ end
61
+
62
+ describe ".message_decoder" do
63
+ subject { FacebookSession.message_decoder }
64
+
65
+ context "without configuration" do
66
+ it "should raise an error" do
67
+ expect { subject }.to raise_error(FacebookSession::NotConfiguredError)
68
+ end
69
+ end
70
+
71
+ context "with configuration" do
72
+ before { FacebookSession.configure(application_id: "foo", application_secret: "bar") }
73
+ it { is_expected.to be_a(FacebookSession::MessageDecoder) }
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+
3
+ class TestController
4
+ include FacebookSession::Helper
5
+ def initialize(options = {})
6
+ @fb_session = options[:fb_session]
7
+ @signed_request = options[:signed_request]
8
+ end
9
+
10
+ def cookies
11
+ { "fbsr_foo" => @fb_session }
12
+ end
13
+
14
+ def params
15
+ { signed_request: @signed_request }
16
+ end
17
+ end
18
+
19
+ describe FacebookSession::Helper do
20
+ before(:all) { FacebookSession.configure(application_id: "foo", application_secret: "bar") }
21
+
22
+ let(:json) { '{"user_id": "123", "oauth_token": "abc", "algorithm": "sha256", "issued_at": "2014"}' }
23
+ let(:payload) { Base64.urlsafe_encode64(json) }
24
+ let(:digest) { Base64.urlsafe_encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), "bar", payload)) }
25
+ let(:message) { "#{digest}.#{payload}" }
26
+ let(:controller) { TestController.new }
27
+
28
+ describe "#facebook_session" do
29
+ subject { controller.facebook_session }
30
+
31
+ context "without session data" do
32
+ it { is_expected.to eq(nil) }
33
+ end
34
+
35
+ context "with session data" do
36
+ let(:controller) { TestController.new(fb_session: message) }
37
+ it { is_expected.to be_a(FacebookSession::Session) }
38
+ it "should decode the message" do
39
+ expect(subject.user_id).to eq("123")
40
+ end
41
+ end
42
+ end
43
+
44
+ describe "#facebook_session?" do
45
+ subject { controller.facebook_session? }
46
+
47
+ context "without session data" do
48
+ it { is_expected.to eq(false) }
49
+ end
50
+
51
+ context "with session data" do
52
+ let(:controller) { TestController.new(fb_session: message) }
53
+ it { is_expected.to eq(true) }
54
+ end
55
+ end
56
+
57
+ describe "#facebook_signed_request" do
58
+ subject { controller.facebook_signed_request }
59
+
60
+ context "without a signed request" do
61
+ it { is_expected.to eq(nil) }
62
+ end
63
+
64
+ context "with a signed request" do
65
+ let(:controller) { TestController.new(signed_request: message) }
66
+ it { is_expected.to be_a(FacebookSession::SignedRequest) }
67
+ it "should decode the message" do
68
+ expect(subject.user_id).to eq("123")
69
+ end
70
+ end
71
+ end
72
+
73
+ describe "#facebook_signed_request?" do
74
+ subject { controller.facebook_signed_request? }
75
+
76
+ context "without a signed request" do
77
+ it { is_expected.to eq(false) }
78
+ end
79
+
80
+ context "with a signed request" do
81
+ let(:controller) { TestController.new(signed_request: message) }
82
+ it { is_expected.to eq(true) }
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe FacebookSession::MessageDecoder do
4
+ let(:secret) { "topsecret" }
5
+ let(:verifier) { FacebookSession::MessageDecoder.new(secret) }
6
+ let(:json) { '{"foo": "bar"}' }
7
+ let(:payload) { Base64.urlsafe_encode64(json).gsub(/[=]+$/, '') }
8
+ let(:digest) { Base64.urlsafe_encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), secret, payload)).gsub(/[=]+$/, '') }
9
+ let(:message) { "#{digest}.#{payload}" }
10
+
11
+ describe "#decode" do
12
+ subject { verifier.decode(message) }
13
+
14
+ context "with valid data" do
15
+ it { is_expected.to eq({foo: "bar"}) }
16
+ end
17
+
18
+ context "with invalid digest" do
19
+ let(:digest) { "1639a467ae544a4a9b4a5623fe56a2f93276087b" }
20
+ it "should raise an error" do
21
+ expect { subject }.to raise_error(FacebookSession::InvalidSignature)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe FacebookSession::Session do
4
+ before(:all) { FacebookSession.configure(application_id: "foo", application_secret: "bar") }
5
+
6
+ let(:json) { '{"user_id": "123", "oauth_token": "abc", "algorithm": "sha256", "issued_at": "2014"}' }
7
+ let(:payload) { Base64.urlsafe_encode64(json) }
8
+ let(:digest) { Base64.urlsafe_encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), "bar", payload)) }
9
+ let(:message) { "#{digest}.#{payload}" }
10
+
11
+ describe ".decode" do
12
+ subject { FacebookSession::Session.decode(message) }
13
+
14
+ context "with a valid message" do
15
+ it { is_expected.to be_a(FacebookSession::Session) }
16
+ it "should recieve the data" do
17
+ expect(subject.user_id).to eq("123")
18
+ expect(subject.oauth_token).to eq("abc")
19
+ expect(subject.algorithm).to eq("sha256")
20
+ expect(subject.issued_at).to eq("2014")
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe FacebookSession::SignedRequest do
4
+ before(:all) { FacebookSession.configure(application_id: "foo", application_secret: "bar") }
5
+
6
+ let(:json) { '{"user_id": "123", "oauth_token": "abc", "algorithm": "sha256", "issued_at": "2014", "code": "code", "user": "user", "expires": "expires", "app_data": "app_data", "page": "page"}' }
7
+ let(:payload) { Base64.urlsafe_encode64(json) }
8
+ let(:digest) { Base64.urlsafe_encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), "bar", payload)) }
9
+ let(:message) { "#{digest}.#{payload}" }
10
+
11
+ describe ".decode" do
12
+ subject { FacebookSession::SignedRequest.decode(message) }
13
+
14
+ context "with a valid message" do
15
+ it { is_expected.to be_a(FacebookSession::SignedRequest) }
16
+ it "should recieve the data" do
17
+ expect(subject.user_id).to eq("123")
18
+ expect(subject.oauth_token).to eq("abc")
19
+ expect(subject.algorithm).to eq("sha256")
20
+ expect(subject.issued_at).to eq("2014")
21
+ expect(subject.code).to eq("code")
22
+ expect(subject.user).to eq("user")
23
+ expect(subject.expires).to eq("expires")
24
+ expect(subject.app_data).to eq("app_data")
25
+ expect(subject.page).to eq("page")
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ require "codeclimate-test-reporter"
2
+ CodeClimate::TestReporter.start
3
+
4
+ require_relative '../lib/facebook_session'
5
+
6
+ RSpec.configure do |config|
7
+ end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: facebook_session
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Inge Jørgensen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-30 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2014-09-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 3.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 3.0.0
13
27
  description: Rails plugin for simple Facebook session authentication
14
28
  email: inge@manualdesign.no
15
29
  executables: []
@@ -17,11 +31,20 @@ extensions: []
17
31
  extra_rdoc_files: []
18
32
  files:
19
33
  - lib/facebook_session.rb
34
+ - lib/facebook_session/decodeable_struct.rb
20
35
  - lib/facebook_session/helper.rb
36
+ - lib/facebook_session/message_decoder.rb
37
+ - lib/facebook_session/railtie.rb
21
38
  - lib/facebook_session/session.rb
22
39
  - lib/facebook_session/signed_request.rb
23
40
  - lib/facebook_session/version.rb
24
- homepage: https://github.com/manualdesign/simple_session
41
+ - spec/facebook_session_spec.rb
42
+ - spec/helper_spec.rb
43
+ - spec/message_decoder_spec.rb
44
+ - spec/session_spec.rb
45
+ - spec/signed_request_spec.rb
46
+ - spec/spec_helper.rb
47
+ homepage: https://github.com/manualdesign/facebook_session
25
48
  licenses: []
26
49
  metadata: {}
27
50
  post_install_message:
@@ -32,7 +55,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
32
55
  requirements:
33
56
  - - ">="
34
57
  - !ruby/object:Gem::Version
35
- version: '0'
58
+ version: 1.9.2
36
59
  required_rubygems_version: !ruby/object:Gem::Requirement
37
60
  requirements:
38
61
  - - ">="
@@ -40,8 +63,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
40
63
  version: '0'
41
64
  requirements: []
42
65
  rubyforge_project:
43
- rubygems_version: 2.2.0
66
+ rubygems_version: 2.4.1
44
67
  signing_key:
45
68
  specification_version: 4
46
69
  summary: Rails plugin for simple Facebook session authentication
47
- test_files: []
70
+ test_files:
71
+ - spec/facebook_session_spec.rb
72
+ - spec/helper_spec.rb
73
+ - spec/message_decoder_spec.rb
74
+ - spec/session_spec.rb
75
+ - spec/signed_request_spec.rb
76
+ - spec/spec_helper.rb