facebook_session 0.0.5 → 1.0.0

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.
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