Imported Upstream version 3.0.0
Praveen Arimbrathodiyil
7 years ago
0 | ## 3.0.0 (2015-10-26) | |
1 | ||
2 | Changes: | |
3 | ||
4 | - Remove query string from redirect_uri on callback by default (#221, @gioblu) | |
5 | - Signed request parsing extracted to `OmniAuth::Facebook::SignedRequest` class. (#183, @simi, @Vrael) | |
6 | - Change default value of `info_fields` to `name,email` for the [graph-api-v2.4](https://developers.facebook.com/blog/post/2015/07/08/graph-api-v2.4/). ([#209](https://github.com/mkdynamic/omniauth-facebook/pull/209)) | |
7 | ||
0 | 8 | ## 2.0.1 (2015-02-21) |
1 | 9 | |
2 | 10 | Bugfixes: |
2 | 2 | |
3 | 3 | --- |
4 | 4 | |
5 | # OmniAuth Facebook [](https://travis-ci.org/mkdynamic/omniauth-facebook) | |
5 | # OmniAuth Facebook [](https://travis-ci.org/mkdynamic/omniauth-facebook) [](https://rubygems.org/gems/omniauth-facebook) | |
6 | ||
6 | 7 | |
7 | 8 | **These notes are based on master, please see tags for README pertaining to specific releases.** |
8 | 9 | |
43 | 44 | `scope` | `email` | A comma-separated list of permissions you want to request from the user. See the Facebook docs for a full list of available permissions: https://developers.facebook.com/docs/reference/login/ |
44 | 45 | `display` | `page` | The display context to show the authentication page. Options are: `page`, `popup` and `touch`. Read the Facebook docs for more details: https://developers.facebook.com/docs/reference/dialogs/oauth/ |
45 | 46 | `image_size` | `square` | Set the size for the returned image url in the auth hash. Valid options include `square` (50x50), `small` (50 pixels wide, variable height), `normal` (100 pixels wide, variable height), or `large` (about 200 pixels wide, variable height). Additionally, you can request a picture of a specific size by setting this option to a hash with `:width` and `:height` as keys. This will return an available profile picture closest to the requested size and requested aspect ratio. If only `:width` or `:height` is specified, we will return a picture whose width or height is closest to the requested size, respectively. |
46 | `info_fields` | | Specify exactly which fields should be returned when getting the user's info. Value should be a comma-separated string as per https://developers.facebook.com/docs/graph-api/reference/user/ (only `/me` endpoint). | |
47 | `info_fields` | 'name,email' | Specify exactly which fields should be returned when getting the user's info. Value should be a comma-separated string as per https://developers.facebook.com/docs/graph-api/reference/user/ (only `/me` endpoint). | |
47 | 48 | `locale` | | Specify locale which should be used when getting the user's info. Value should be locale string as per https://developers.facebook.com/docs/reference/api/locale/. |
48 | `auth_type` | | Optionally specifies the requested authentication features as a comma-separated list, as per https://developers.facebook.com/docs/facebook-login/reauthentication/. Valid values are `https` (checks for the presence of the secure cookie and asks for re-authentication if it is not present), and `reauthenticate` (asks the user to re-authenticate unconditionally). Default is `nil`. | |
49 | `auth_type` | | Optionally specifies the requested authentication features as a comma-separated list, as per https://developers.facebook.com/docs/facebook-login/reauthentication/. Valid values are `https` (checks for the presence of the secure cookie and asks for re-authentication if it is not present), and `reauthenticate` (asks the user to re-authenticate unconditionally). Use 'rerequest' when you want to request premissions. Default is `nil`. | |
49 | 50 | `secure_image_url` | `false` | Set to `true` to use https for the avatar image url returned in the auth hash. |
50 | 51 | `callback_url` / `callback_path` | | Specify a custom callback URL used during the server-side flow. Note this must be allowed by your app configuration on Facebook (see 'Valid OAuth redirect URIs' under the 'Advanced' settings section in the configuration for your Facebook app for more details). |
51 | 52 | |
177 | 178 | |
178 | 179 | |
179 | 180 | [](https://bitdeli.com/free "Bitdeli Badge") |
180 |
2 | 2 | |
3 | 3 | Rake::TestTask.new do |task| |
4 | 4 | task.libs << 'test' |
5 | task.test_files = FileList['test/*_test.rb'] | |
5 | 6 | end |
6 | 7 | |
7 | 8 | task :default => :test |
0 | require 'openssl' | |
1 | ||
2 | module OmniAuth | |
3 | module Facebook | |
4 | class SignedRequest | |
5 | class UnknownSignatureAlgorithmError < NotImplementedError; end | |
6 | SUPPORTED_ALGORITHM = 'HMAC-SHA256' | |
7 | ||
8 | attr_reader :value, :secret | |
9 | ||
10 | def self.parse(value, secret) | |
11 | new(value, secret).payload | |
12 | end | |
13 | ||
14 | def initialize(value, secret) | |
15 | @value = value | |
16 | @secret = secret | |
17 | end | |
18 | ||
19 | def payload | |
20 | @payload ||= parse_signed_request | |
21 | end | |
22 | ||
23 | private | |
24 | ||
25 | def parse_signed_request | |
26 | signature, encoded_payload = value.split('.') | |
27 | return if signature.nil? | |
28 | ||
29 | decoded_hex_signature = base64_decode_url(signature) | |
30 | decoded_payload = MultiJson.decode(base64_decode_url(encoded_payload)) | |
31 | ||
32 | unless decoded_payload['algorithm'] == SUPPORTED_ALGORITHM | |
33 | raise UnknownSignatureAlgorithmError, "unknown algorithm: #{decoded_payload['algorithm']}" | |
34 | end | |
35 | ||
36 | if valid_signature?(decoded_hex_signature, encoded_payload) | |
37 | decoded_payload | |
38 | end | |
39 | end | |
40 | ||
41 | def valid_signature?(signature, payload, algorithm = OpenSSL::Digest::SHA256.new) | |
42 | OpenSSL::HMAC.digest(algorithm, secret, payload) == signature | |
43 | end | |
44 | ||
45 | def base64_decode_url(value) | |
46 | value += '=' * (4 - value.size.modulo(4)) | |
47 | Base64.decode64(value.tr('-_', '+/')) | |
48 | end | |
49 | end | |
50 | end | |
51 | end |
0 | 0 | require 'omniauth/strategies/oauth2' |
1 | require 'base64' | |
1 | require 'omniauth/facebook/signed_request' | |
2 | 2 | require 'openssl' |
3 | 3 | require 'rack/utils' |
4 | 4 | require 'uri' |
7 | 7 | module Strategies |
8 | 8 | class Facebook < OmniAuth::Strategies::OAuth2 |
9 | 9 | class NoAuthorizationCodeError < StandardError; end |
10 | class UnknownSignatureAlgorithmError < NotImplementedError; end | |
11 | 10 | |
12 | 11 | DEFAULT_SCOPE = 'email' |
13 | SUPPORTED_ALGORITHM = 'HMAC-SHA256' | |
14 | 12 | |
15 | 13 | option :client_options, { |
16 | 14 | :site => 'https://graph.facebook.com', |
61 | 59 | |
62 | 60 | def info_options |
63 | 61 | params = {:appsecret_proof => appsecret_proof} |
64 | params.merge!({:fields => options[:info_fields]}) if options[:info_fields] | |
62 | params.merge!({:fields => (options[:info_fields] || 'name,email')}) | |
65 | 63 | params.merge!({:locale => options[:locale]}) if options[:locale] |
66 | 64 | |
67 | 65 | { :params => params } |
73 | 71 | end |
74 | 72 | rescue NoAuthorizationCodeError => e |
75 | 73 | fail!(:no_authorization_code, e) |
76 | rescue UnknownSignatureAlgorithmError => e | |
74 | rescue OmniAuth::Facebook::SignedRequest::UnknownSignatureAlgorithmError => e | |
77 | 75 | fail!(:unknown_signature_algorithm, e) |
78 | 76 | end |
79 | 77 | |
84 | 82 | if @authorization_code_from_signed_request_in_cookie |
85 | 83 | '' |
86 | 84 | else |
87 | options[:callback_url] || super | |
85 | # Fixes regression in omniauth-oauth2 v1.4.0 by https://github.com/intridea/omniauth-oauth2/commit/85fdbe117c2a4400d001a6368cc359d88f40abc7 | |
86 | options[:callback_url] || (full_host + script_name + callback_path) | |
88 | 87 | end |
89 | 88 | end |
90 | 89 | |
119 | 118 | private |
120 | 119 | |
121 | 120 | def signed_request_from_cookie |
122 | @signed_request_from_cookie ||= raw_signed_request_from_cookie && parse_signed_request(raw_signed_request_from_cookie) | |
121 | @signed_request_from_cookie ||= raw_signed_request_from_cookie && OmniAuth::Facebook::SignedRequest.parse(raw_signed_request_from_cookie, client.secret) | |
123 | 122 | end |
124 | 123 | |
125 | 124 | def raw_signed_request_from_cookie |
159 | 158 | end |
160 | 159 | end |
161 | 160 | |
162 | def parse_signed_request(value) | |
163 | signature, encoded_payload = value.split('.') | |
164 | return if signature.nil? | |
165 | ||
166 | decoded_hex_signature = base64_decode_url(signature) | |
167 | decoded_payload = MultiJson.decode(base64_decode_url(encoded_payload)) | |
168 | ||
169 | unless decoded_payload['algorithm'] == SUPPORTED_ALGORITHM | |
170 | raise UnknownSignatureAlgorithmError, "unknown algorithm: #{decoded_payload['algorithm']}" | |
171 | end | |
172 | ||
173 | if valid_signature?(client.secret, decoded_hex_signature, encoded_payload) | |
174 | decoded_payload | |
175 | end | |
176 | end | |
177 | ||
178 | def valid_signature?(secret, signature, payload, algorithm = OpenSSL::Digest::SHA256.new) | |
179 | OpenSSL::HMAC.digest(algorithm, secret, payload) == signature | |
180 | end | |
181 | ||
182 | def base64_decode_url(value) | |
183 | value += '=' * (4 - value.size.modulo(4)) | |
184 | Base64.decode64(value.tr('-_', '+/')) | |
185 | end | |
186 | ||
187 | 161 | def image_url(uid, options) |
188 | 162 | uri_class = options[:secure_image_url] ? URI::HTTPS : URI::HTTP |
189 | 163 | site_uri = URI.parse(client.site) |
0 | 0 | --- !ruby/object:Gem::Specification |
1 | 1 | name: omniauth-facebook |
2 | 2 | version: !ruby/object:Gem::Version |
3 | version: 2.0.1 | |
3 | version: 3.0.0 | |
4 | 4 | platform: ruby |
5 | 5 | authors: |
6 | 6 | - Mark Dodwell |
8 | 8 | autorequire: |
9 | 9 | bindir: bin |
10 | 10 | cert_chain: [] |
11 | date: 2015-02-21 00:00:00.000000000 Z | |
11 | date: 2015-10-27 00:00:00.000000000 Z | |
12 | 12 | dependencies: |
13 | 13 | - !ruby/object:Gem::Dependency |
14 | 14 | name: omniauth-oauth2 |
15 | 15 | requirement: !ruby/object:Gem::Requirement |
16 | 16 | requirements: |
17 | - - "~>" | |
17 | - - ~> | |
18 | 18 | - !ruby/object:Gem::Version |
19 | 19 | version: '1.2' |
20 | 20 | type: :runtime |
21 | 21 | prerelease: false |
22 | 22 | version_requirements: !ruby/object:Gem::Requirement |
23 | 23 | requirements: |
24 | - - "~>" | |
24 | - - ~> | |
25 | 25 | - !ruby/object:Gem::Version |
26 | 26 | version: '1.2' |
27 | 27 | - !ruby/object:Gem::Dependency |
28 | 28 | name: minitest |
29 | 29 | requirement: !ruby/object:Gem::Requirement |
30 | 30 | requirements: |
31 | - - ">=" | |
31 | - - '>=' | |
32 | 32 | - !ruby/object:Gem::Version |
33 | 33 | version: '0' |
34 | 34 | type: :development |
35 | 35 | prerelease: false |
36 | 36 | version_requirements: !ruby/object:Gem::Requirement |
37 | 37 | requirements: |
38 | - - ">=" | |
38 | - - '>=' | |
39 | 39 | - !ruby/object:Gem::Version |
40 | 40 | version: '0' |
41 | 41 | - !ruby/object:Gem::Dependency |
42 | 42 | name: mocha |
43 | 43 | requirement: !ruby/object:Gem::Requirement |
44 | 44 | requirements: |
45 | - - ">=" | |
45 | - - '>=' | |
46 | 46 | - !ruby/object:Gem::Version |
47 | 47 | version: '0' |
48 | 48 | type: :development |
49 | 49 | prerelease: false |
50 | 50 | version_requirements: !ruby/object:Gem::Requirement |
51 | 51 | requirements: |
52 | - - ">=" | |
52 | - - '>=' | |
53 | 53 | - !ruby/object:Gem::Version |
54 | 54 | version: '0' |
55 | 55 | - !ruby/object:Gem::Dependency |
56 | 56 | name: rake |
57 | 57 | requirement: !ruby/object:Gem::Requirement |
58 | 58 | requirements: |
59 | - - ">=" | |
59 | - - '>=' | |
60 | 60 | - !ruby/object:Gem::Version |
61 | 61 | version: '0' |
62 | 62 | type: :development |
63 | 63 | prerelease: false |
64 | 64 | version_requirements: !ruby/object:Gem::Requirement |
65 | 65 | requirements: |
66 | - - ">=" | |
66 | - - '>=' | |
67 | 67 | - !ruby/object:Gem::Version |
68 | 68 | version: '0' |
69 | 69 | description: |
74 | 74 | extensions: [] |
75 | 75 | extra_rdoc_files: [] |
76 | 76 | files: |
77 | - ".gitignore" | |
78 | - ".travis.yml" | |
77 | - .gitignore | |
78 | - .travis.yml | |
79 | 79 | - CHANGELOG.md |
80 | 80 | - Gemfile |
81 | 81 | - README.md |
86 | 86 | - example/config.ru |
87 | 87 | - lib/omniauth-facebook.rb |
88 | 88 | - lib/omniauth/facebook.rb |
89 | - lib/omniauth/facebook/signed_request.rb | |
89 | 90 | - lib/omniauth/facebook/version.rb |
90 | 91 | - lib/omniauth/strategies/facebook.rb |
91 | 92 | - omniauth-facebook.gemspec |
93 | - test/fixtures/payload.json | |
94 | - test/fixtures/signed_request.txt | |
92 | 95 | - test/helper.rb |
96 | - test/signed_request_test.rb | |
97 | - test/strategy_test.rb | |
93 | 98 | - test/support/shared_examples.rb |
94 | - test/test.rb | |
95 | 99 | homepage: https://github.com/mkdynamic/omniauth-facebook |
96 | 100 | licenses: |
97 | 101 | - MIT |
102 | 106 | - lib |
103 | 107 | required_ruby_version: !ruby/object:Gem::Requirement |
104 | 108 | requirements: |
105 | - - ">=" | |
109 | - - '>=' | |
106 | 110 | - !ruby/object:Gem::Version |
107 | 111 | version: '0' |
108 | 112 | required_rubygems_version: !ruby/object:Gem::Requirement |
109 | 113 | requirements: |
110 | - - ">=" | |
114 | - - '>=' | |
111 | 115 | - !ruby/object:Gem::Version |
112 | 116 | version: '0' |
113 | 117 | requirements: [] |
114 | 118 | rubyforge_project: |
115 | rubygems_version: 2.4.5 | |
119 | rubygems_version: 2.0.14 | |
116 | 120 | signing_key: |
117 | 121 | specification_version: 4 |
118 | 122 | summary: Facebook OAuth2 Strategy for OmniAuth |
119 | 123 | test_files: |
124 | - test/fixtures/payload.json | |
125 | - test/fixtures/signed_request.txt | |
120 | 126 | - test/helper.rb |
127 | - test/signed_request_test.rb | |
128 | - test/strategy_test.rb | |
121 | 129 | - test/support/shared_examples.rb |
122 | - test/test.rb |
0 | { | |
1 | "algorithm": "HMAC-SHA256", | |
2 | "expires": 1308988800, | |
3 | "issued_at": 1308985018, | |
4 | "oauth_token": "111111111111111|2.AQBAttRlLVnwqNPZ.3600.1111111111.1-111111111111111|T49w3BqoZUegypru51Gra70hED8", | |
5 | "user": | |
6 | { | |
7 | "country": "de", | |
8 | "locale": "en_US", | |
9 | "age": | |
10 | { | |
11 | "min": 21 | |
12 | } | |
13 | }, | |
14 | "user_id": "111111111111111" | |
15 | } |
0 | 53umfudisP7mKhsi9nZboBg15yMZKhfQAARL9UoZtSE.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEzMDg5ODg4MDAsImlzc3VlZF9hdCI6MTMwODk4NTAxOCwib2F1dGhfdG9rZW4iOiIxMTExMTExMTExMTExMTF8Mi5BUUJBdHRSbExWbndxTlBaLjM2MDAuMTExMTExMTExMS4xLTExMTExMTExMTExMTExMXxUNDl3M0Jxb1pVZWd5cHJ1NTFHcmE3MGhFRDgiLCJ1c2VyIjp7ImNvdW50cnkiOiJkZSIsImxvY2FsZSI6ImVuX1VTIiwiYWdlIjp7Im1pbiI6MjF9fSwidXNlcl9pZCI6IjExMTExMTExMTExMTExMSJ9 |
0 | require 'helper' | |
1 | require 'omniauth/facebook/signed_request' | |
2 | ||
3 | class SignedRequestTest < Minitest::Test | |
4 | def setup | |
5 | @value = fixture('signed_request.txt').strip | |
6 | @secret = "897z956a2z7zzzzz5783z458zz3z7556" | |
7 | @expected_payload = MultiJson.decode(fixture('payload.json')) | |
8 | end | |
9 | ||
10 | def test_signed_request_payload | |
11 | signed_request = OmniAuth::Facebook::SignedRequest.new(@value, @secret) | |
12 | assert_equal @expected_payload, signed_request.payload | |
13 | end | |
14 | ||
15 | def test_signed_request_parse | |
16 | payload = OmniAuth::Facebook::SignedRequest.parse(@value, @secret) | |
17 | assert_equal @expected_payload, payload | |
18 | end | |
19 | ||
20 | private | |
21 | ||
22 | def fixture(name) | |
23 | File.read(File.expand_path("fixtures/#{name}", File.dirname(__FILE__))) | |
24 | end | |
25 | end |
0 | require 'helper' | |
1 | require 'omniauth-facebook' | |
2 | require 'openssl' | |
3 | require 'base64' | |
4 | ||
5 | class StrategyTest < StrategyTestCase | |
6 | include OAuth2StrategyTests | |
7 | end | |
8 | ||
9 | class ClientTest < StrategyTestCase | |
10 | test 'has correct Facebook site' do | |
11 | assert_equal 'https://graph.facebook.com', strategy.client.site | |
12 | end | |
13 | ||
14 | test 'has correct authorize url' do | |
15 | assert_equal 'https://www.facebook.com/dialog/oauth', strategy.client.options[:authorize_url] | |
16 | end | |
17 | ||
18 | test 'has correct token url with versioning' do | |
19 | @options = {:client_options => {:site => 'https://graph.facebook.net/v2.2'}} | |
20 | assert_equal 'oauth/access_token', strategy.client.options[:token_url] | |
21 | assert_equal 'https://graph.facebook.net/v2.2/oauth/access_token', strategy.client.token_url | |
22 | end | |
23 | end | |
24 | ||
25 | class CallbackUrlTest < StrategyTestCase | |
26 | test "returns the default callback url (omitting querystring)" do | |
27 | url_base = 'http://auth.request.com' | |
28 | @request.stubs(:url).returns("#{url_base}/some/page") | |
29 | strategy.stubs(:script_name).returns('') # as not to depend on Rack env | |
30 | strategy.stubs(:query_string).returns('?foo=bar') | |
31 | assert_equal "#{url_base}/auth/facebook/callback", strategy.callback_url | |
32 | end | |
33 | ||
34 | test "returns path from callback_path option (omitting querystring)" do | |
35 | @options = { :callback_path => "/auth/FB/done"} | |
36 | url_base = 'http://auth.request.com' | |
37 | @request.stubs(:url).returns("#{url_base}/page/path") | |
38 | strategy.stubs(:script_name).returns('') # as not to depend on Rack env | |
39 | strategy.stubs(:query_string).returns('?foo=bar') | |
40 | assert_equal "#{url_base}/auth/FB/done", strategy.callback_url | |
41 | end | |
42 | ||
43 | test "returns url from callback_url option" do | |
44 | url = 'https://auth.myapp.com/auth/fb/callback' | |
45 | @options = { :callback_url => url } | |
46 | assert_equal url, strategy.callback_url | |
47 | end | |
48 | end | |
49 | ||
50 | class AuthorizeParamsTest < StrategyTestCase | |
51 | test 'includes default scope for email' do | |
52 | assert strategy.authorize_params.is_a?(Hash) | |
53 | assert_equal 'email', strategy.authorize_params[:scope] | |
54 | end | |
55 | ||
56 | test 'includes display parameter from request when present' do | |
57 | @request.stubs(:params).returns({ 'display' => 'touch' }) | |
58 | assert strategy.authorize_params.is_a?(Hash) | |
59 | assert_equal 'touch', strategy.authorize_params[:display] | |
60 | end | |
61 | ||
62 | test 'includes auth_type parameter from request when present' do | |
63 | @request.stubs(:params).returns({ 'auth_type' => 'reauthenticate' }) | |
64 | assert strategy.authorize_params.is_a?(Hash) | |
65 | assert_equal 'reauthenticate', strategy.authorize_params[:auth_type] | |
66 | end | |
67 | ||
68 | test 'overrides default scope with parameter passed from request' do | |
69 | @request.stubs(:params).returns({ 'scope' => 'email' }) | |
70 | assert strategy.authorize_params.is_a?(Hash) | |
71 | assert_equal 'email', strategy.authorize_params[:scope] | |
72 | end | |
73 | end | |
74 | ||
75 | class TokeParamsTest < StrategyTestCase | |
76 | test 'has correct parse strategy' do | |
77 | assert_equal :query, strategy.token_params[:parse] | |
78 | end | |
79 | end | |
80 | ||
81 | class AccessTokenOptionsTest < StrategyTestCase | |
82 | test 'has correct param name by default' do | |
83 | assert_equal 'access_token', strategy.access_token_options[:param_name] | |
84 | end | |
85 | ||
86 | test 'has correct header format by default' do | |
87 | assert_equal 'OAuth %s', strategy.access_token_options[:header_format] | |
88 | end | |
89 | end | |
90 | ||
91 | class UidTest < StrategyTestCase | |
92 | def setup | |
93 | super | |
94 | strategy.stubs(:raw_info).returns({ 'id' => '123' }) | |
95 | end | |
96 | ||
97 | test 'returns the id from raw_info' do | |
98 | assert_equal '123', strategy.uid | |
99 | end | |
100 | end | |
101 | ||
102 | class InfoTest < StrategyTestCase | |
103 | test 'returns the secure facebook avatar url when `secure_image_url` option is specified' do | |
104 | @options = { :secure_image_url => true } | |
105 | raw_info = { 'name' => 'Fred Smith', 'id' => '321' } | |
106 | strategy.stubs(:raw_info).returns(raw_info) | |
107 | assert_equal 'https://graph.facebook.com/321/picture', strategy.info['image'] | |
108 | end | |
109 | ||
110 | test 'returns the image_url based of the client site' do | |
111 | @options = { :secure_image_url => true, :client_options => {:site => "https://blah.facebook.com/v2.2"}} | |
112 | raw_info = { 'name' => 'Fred Smith', 'id' => '321' } | |
113 | strategy.stubs(:raw_info).returns(raw_info) | |
114 | assert_equal 'https://blah.facebook.com/v2.2/321/picture', strategy.info['image'] | |
115 | end | |
116 | ||
117 | test 'returns the image with size specified in the `image_size` option' do | |
118 | @options = { :image_size => 'normal' } | |
119 | raw_info = { 'name' => 'Fred Smith', 'id' => '321' } | |
120 | strategy.stubs(:raw_info).returns(raw_info) | |
121 | assert_equal 'http://graph.facebook.com/321/picture?type=normal', strategy.info['image'] | |
122 | end | |
123 | ||
124 | test 'returns the image with size specified as a symbol in the `image_size` option' do | |
125 | @options = { :image_size => :normal } | |
126 | raw_info = { 'name' => 'Fred Smith', 'id' => '321' } | |
127 | strategy.stubs(:raw_info).returns(raw_info) | |
128 | assert_equal 'http://graph.facebook.com/321/picture?type=normal', strategy.info['image'] | |
129 | end | |
130 | ||
131 | test 'returns the image with width and height specified in the `image_size` option' do | |
132 | @options = { :image_size => { :width => 123, :height => 987 } } | |
133 | raw_info = { 'name' => 'Fred Smith', 'id' => '321' } | |
134 | strategy.stubs(:raw_info).returns(raw_info) | |
135 | assert_match 'width=123', strategy.info['image'] | |
136 | assert_match 'height=987', strategy.info['image'] | |
137 | assert_match 'http://graph.facebook.com/321/picture?', strategy.info['image'] | |
138 | end | |
139 | end | |
140 | ||
141 | class InfoTestOptionalDataPresent < StrategyTestCase | |
142 | def setup | |
143 | super | |
144 | @raw_info ||= { 'name' => 'Fred Smith' } | |
145 | strategy.stubs(:raw_info).returns(@raw_info) | |
146 | end | |
147 | ||
148 | test 'returns the name' do | |
149 | assert_equal 'Fred Smith', strategy.info['name'] | |
150 | end | |
151 | ||
152 | test 'returns the email' do | |
153 | @raw_info['email'] = 'fred@smith.com' | |
154 | assert_equal 'fred@smith.com', strategy.info['email'] | |
155 | end | |
156 | ||
157 | test 'returns the username as nickname' do | |
158 | @raw_info['username'] = 'fredsmith' | |
159 | assert_equal 'fredsmith', strategy.info['nickname'] | |
160 | end | |
161 | ||
162 | test 'returns the first name' do | |
163 | @raw_info['first_name'] = 'Fred' | |
164 | assert_equal 'Fred', strategy.info['first_name'] | |
165 | end | |
166 | ||
167 | test 'returns the last name' do | |
168 | @raw_info['last_name'] = 'Smith' | |
169 | assert_equal 'Smith', strategy.info['last_name'] | |
170 | end | |
171 | ||
172 | test 'returns the location name as location' do | |
173 | @raw_info['location'] = { 'id' => '104022926303756', 'name' => 'Palo Alto, California' } | |
174 | assert_equal 'Palo Alto, California', strategy.info['location'] | |
175 | end | |
176 | ||
177 | test 'returns bio as description' do | |
178 | @raw_info['bio'] = 'I am great' | |
179 | assert_equal 'I am great', strategy.info['description'] | |
180 | end | |
181 | ||
182 | test 'returns the facebook avatar url' do | |
183 | @raw_info['id'] = '321' | |
184 | assert_equal 'http://graph.facebook.com/321/picture', strategy.info['image'] | |
185 | end | |
186 | ||
187 | test 'returns the Facebook link as the Facebook url' do | |
188 | @raw_info['link'] = 'http://www.facebook.com/fredsmith' | |
189 | assert_kind_of Hash, strategy.info['urls'] | |
190 | assert_equal 'http://www.facebook.com/fredsmith', strategy.info['urls']['Facebook'] | |
191 | end | |
192 | ||
193 | test 'returns website url' do | |
194 | @raw_info['website'] = 'https://my-wonderful-site.com' | |
195 | assert_kind_of Hash, strategy.info['urls'] | |
196 | assert_equal 'https://my-wonderful-site.com', strategy.info['urls']['Website'] | |
197 | end | |
198 | ||
199 | test 'return both Facebook link and website urls' do | |
200 | @raw_info['link'] = 'http://www.facebook.com/fredsmith' | |
201 | @raw_info['website'] = 'https://my-wonderful-site.com' | |
202 | assert_kind_of Hash, strategy.info['urls'] | |
203 | assert_equal 'http://www.facebook.com/fredsmith', strategy.info['urls']['Facebook'] | |
204 | assert_equal 'https://my-wonderful-site.com', strategy.info['urls']['Website'] | |
205 | end | |
206 | ||
207 | test 'returns the positive verified status' do | |
208 | @raw_info['verified'] = true | |
209 | assert strategy.info['verified'] | |
210 | end | |
211 | ||
212 | test 'returns the negative verified status' do | |
213 | @raw_info['verified'] = false | |
214 | refute strategy.info['verified'] | |
215 | end | |
216 | end | |
217 | ||
218 | class InfoTestOptionalDataNotPresent < StrategyTestCase | |
219 | def setup | |
220 | super | |
221 | @raw_info ||= { 'name' => 'Fred Smith' } | |
222 | strategy.stubs(:raw_info).returns(@raw_info) | |
223 | end | |
224 | ||
225 | test 'has no email key' do | |
226 | refute_has_key 'email', strategy.info | |
227 | end | |
228 | ||
229 | test 'has no nickname key' do | |
230 | refute_has_key 'nickname', strategy.info | |
231 | end | |
232 | ||
233 | test 'has no first name key' do | |
234 | refute_has_key 'first_name', strategy.info | |
235 | end | |
236 | ||
237 | test 'has no last name key' do | |
238 | refute_has_key 'last_name', strategy.info | |
239 | end | |
240 | ||
241 | test 'has no location key' do | |
242 | refute_has_key 'location', strategy.info | |
243 | end | |
244 | ||
245 | test 'has no description key' do | |
246 | refute_has_key 'description', strategy.info | |
247 | end | |
248 | ||
249 | test 'has no urls' do | |
250 | refute_has_key 'urls', strategy.info | |
251 | end | |
252 | ||
253 | test 'has no verified key' do | |
254 | refute_has_key 'verified', strategy.info | |
255 | end | |
256 | end | |
257 | ||
258 | class RawInfoTest < StrategyTestCase | |
259 | def setup | |
260 | super | |
261 | @access_token = stub('OAuth2::AccessToken') | |
262 | @appsecret_proof = 'appsecret_proof' | |
263 | @options = {:appsecret_proof => @appsecret_proof, :fields => 'name,email'} | |
264 | end | |
265 | ||
266 | test 'performs a GET to https://graph.facebook.com/me' do | |
267 | strategy.stubs(:appsecret_proof).returns(@appsecret_proof) | |
268 | strategy.stubs(:access_token).returns(@access_token) | |
269 | params = {:params => @options} | |
270 | @access_token.expects(:get).with('me', params).returns(stub_everything('OAuth2::Response')) | |
271 | strategy.raw_info | |
272 | end | |
273 | ||
274 | test 'performs a GET to https://graph.facebook.com/me with locale' do | |
275 | @options.merge!({ :locale => 'cs_CZ' }) | |
276 | strategy.stubs(:access_token).returns(@access_token) | |
277 | strategy.stubs(:appsecret_proof).returns(@appsecret_proof) | |
278 | params = {:params => @options} | |
279 | @access_token.expects(:get).with('me', params).returns(stub_everything('OAuth2::Response')) | |
280 | strategy.raw_info | |
281 | end | |
282 | ||
283 | test 'performs a GET to https://graph.facebook.com/me with info_fields' do | |
284 | @options.merge!({:info_fields => 'about'}) | |
285 | strategy.stubs(:access_token).returns(@access_token) | |
286 | strategy.stubs(:appsecret_proof).returns(@appsecret_proof) | |
287 | params = {:params => {:appsecret_proof => @appsecret_proof, :fields => 'about'}} | |
288 | @access_token.expects(:get).with('me', params).returns(stub_everything('OAuth2::Response')) | |
289 | strategy.raw_info | |
290 | end | |
291 | ||
292 | test 'performs a GET to https://graph.facebook.com/me with default info_fields' do | |
293 | strategy.stubs(:access_token).returns(@access_token) | |
294 | strategy.stubs(:appsecret_proof).returns(@appsecret_proof) | |
295 | params = {:params => {:appsecret_proof => @appsecret_proof, :fields => 'name,email'}} | |
296 | @access_token.expects(:get).with('me', params).returns(stub_everything('OAuth2::Response')) | |
297 | strategy.raw_info | |
298 | end | |
299 | ||
300 | test 'returns a Hash' do | |
301 | strategy.stubs(:access_token).returns(@access_token) | |
302 | strategy.stubs(:appsecret_proof).returns(@appsecret_proof) | |
303 | raw_response = stub('Faraday::Response') | |
304 | raw_response.stubs(:body).returns('{ "ohai": "thar" }') | |
305 | raw_response.stubs(:status).returns(200) | |
306 | raw_response.stubs(:headers).returns({'Content-Type' => 'application/json' }) | |
307 | oauth2_response = OAuth2::Response.new(raw_response) | |
308 | params = {:params => @options} | |
309 | @access_token.stubs(:get).with('me', params).returns(oauth2_response) | |
310 | assert_kind_of Hash, strategy.raw_info | |
311 | assert_equal 'thar', strategy.raw_info['ohai'] | |
312 | end | |
313 | ||
314 | test 'returns an empty hash when the response is false' do | |
315 | strategy.stubs(:access_token).returns(@access_token) | |
316 | strategy.stubs(:appsecret_proof).returns(@appsecret_proof) | |
317 | oauth2_response = stub('OAuth2::Response', :parsed => false) | |
318 | params = {:params => @options} | |
319 | @access_token.stubs(:get).with('me', params).returns(oauth2_response) | |
320 | assert_kind_of Hash, strategy.raw_info | |
321 | assert_equal({}, strategy.raw_info) | |
322 | end | |
323 | ||
324 | test 'should not include raw_info in extras hash when skip_info is specified' do | |
325 | @options = { :skip_info => true } | |
326 | strategy.stubs(:raw_info).returns({:foo => 'bar' }) | |
327 | refute_has_key 'raw_info', strategy.extra | |
328 | end | |
329 | end | |
330 | ||
331 | class CredentialsTest < StrategyTestCase | |
332 | def setup | |
333 | super | |
334 | @access_token = stub('OAuth2::AccessToken') | |
335 | @access_token.stubs(:token) | |
336 | @access_token.stubs(:expires?) | |
337 | @access_token.stubs(:expires_at) | |
338 | @access_token.stubs(:refresh_token) | |
339 | strategy.stubs(:access_token).returns(@access_token) | |
340 | end | |
341 | ||
342 | test 'returns a Hash' do | |
343 | assert_kind_of Hash, strategy.credentials | |
344 | end | |
345 | ||
346 | test 'returns the token' do | |
347 | @access_token.stubs(:token).returns('123') | |
348 | assert_equal '123', strategy.credentials['token'] | |
349 | end | |
350 | ||
351 | test 'returns the expiry status' do | |
352 | @access_token.stubs(:expires?).returns(true) | |
353 | assert strategy.credentials['expires'] | |
354 | ||
355 | @access_token.stubs(:expires?).returns(false) | |
356 | refute strategy.credentials['expires'] | |
357 | end | |
358 | ||
359 | test 'returns the refresh token and expiry time when expiring' do | |
360 | ten_mins_from_now = (Time.now + 600).to_i | |
361 | @access_token.stubs(:expires?).returns(true) | |
362 | @access_token.stubs(:refresh_token).returns('321') | |
363 | @access_token.stubs(:expires_at).returns(ten_mins_from_now) | |
364 | assert_equal '321', strategy.credentials['refresh_token'] | |
365 | assert_equal ten_mins_from_now, strategy.credentials['expires_at'] | |
366 | end | |
367 | ||
368 | test 'does not return the refresh token when test is nil and expiring' do | |
369 | @access_token.stubs(:expires?).returns(true) | |
370 | @access_token.stubs(:refresh_token).returns(nil) | |
371 | assert_nil strategy.credentials['refresh_token'] | |
372 | refute_has_key 'refresh_token', strategy.credentials | |
373 | end | |
374 | ||
375 | test 'does not return the refresh token when not expiring' do | |
376 | @access_token.stubs(:expires?).returns(false) | |
377 | @access_token.stubs(:refresh_token).returns('XXX') | |
378 | assert_nil strategy.credentials['refresh_token'] | |
379 | refute_has_key 'refresh_token', strategy.credentials | |
380 | end | |
381 | end | |
382 | ||
383 | class ExtraTest < StrategyTestCase | |
384 | def setup | |
385 | super | |
386 | @raw_info = { 'name' => 'Fred Smith' } | |
387 | strategy.stubs(:raw_info).returns(@raw_info) | |
388 | end | |
389 | ||
390 | test 'returns a Hash' do | |
391 | assert_kind_of Hash, strategy.extra | |
392 | end | |
393 | ||
394 | test 'contains raw info' do | |
395 | assert_equal({ 'raw_info' => @raw_info }, strategy.extra) | |
396 | end | |
397 | end | |
398 | ||
399 | module SignedRequestHelpers | |
400 | def signed_request(payload, secret) | |
401 | encoded_payload = base64_encode_url(MultiJson.encode(payload)) | |
402 | encoded_signature = base64_encode_url(signature(encoded_payload, secret)) | |
403 | [encoded_signature, encoded_payload].join('.') | |
404 | end | |
405 | ||
406 | def base64_encode_url(value) | |
407 | Base64.encode64(value).tr('+/', '-_').gsub(/\n/, '') | |
408 | end | |
409 | ||
410 | def signature(payload, secret, algorithm = OpenSSL::Digest::SHA256.new) | |
411 | OpenSSL::HMAC.digest(algorithm, secret, payload) | |
412 | end | |
413 | end | |
414 | ||
415 | module SignedRequestTests | |
416 | class TestCase < StrategyTestCase | |
417 | include SignedRequestHelpers | |
418 | end | |
419 | ||
420 | class CookieAndParamNotPresentTest < TestCase | |
421 | test 'is nil' do | |
422 | assert_nil strategy.send(:signed_request_from_cookie) | |
423 | end | |
424 | ||
425 | test 'throws an error on calling build_access_token' do | |
426 | assert_raises(OmniAuth::Strategies::Facebook::NoAuthorizationCodeError) { strategy.send(:with_authorization_code!) {} } | |
427 | end | |
428 | end | |
429 | ||
430 | class CookiePresentTest < TestCase | |
431 | def setup(algo = nil) | |
432 | super() | |
433 | @payload = { | |
434 | 'algorithm' => algo || 'HMAC-SHA256', | |
435 | 'code' => 'm4c0d3z', | |
436 | 'issued_at' => Time.now.to_i, | |
437 | 'user_id' => '123456' | |
438 | } | |
439 | ||
440 | @request.stubs(:cookies).returns({"fbsr_#{@client_id}" => signed_request(@payload, @client_secret)}) | |
441 | end | |
442 | ||
443 | test 'parses the access code out from the cookie' do | |
444 | assert_equal @payload, strategy.send(:signed_request_from_cookie) | |
445 | end | |
446 | ||
447 | test 'throws an error if the algorithm is unknown' do | |
448 | setup('UNKNOWN-ALGO') | |
449 | assert_equal "unknown algorithm: UNKNOWN-ALGO", assert_raises(OmniAuth::Facebook::SignedRequest::UnknownSignatureAlgorithmError) { strategy.send(:signed_request_from_cookie) }.message | |
450 | end | |
451 | end | |
452 | ||
453 | class EmptySignedRequestTest < TestCase | |
454 | def setup | |
455 | super | |
456 | @request.stubs(:params).returns({'signed_request' => ''}) | |
457 | end | |
458 | ||
459 | test 'empty param' do | |
460 | assert_equal nil, strategy.send(:signed_request_from_cookie) | |
461 | end | |
462 | end | |
463 | ||
464 | class MissingCodeInParamsRequestTest < TestCase | |
465 | def setup | |
466 | super | |
467 | @request.stubs(:params).returns({}) | |
468 | end | |
469 | ||
470 | test 'calls fail! when a code is not included in the params' do | |
471 | strategy.expects(:fail!).times(1).with(:no_authorization_code, kind_of(OmniAuth::Strategies::Facebook::NoAuthorizationCodeError)) | |
472 | strategy.callback_phase | |
473 | end | |
474 | end | |
475 | ||
476 | class MissingCodeInCookieRequestTest < TestCase | |
477 | def setup(algo = nil) | |
478 | super() | |
479 | @payload = { | |
480 | 'algorithm' => algo || 'HMAC-SHA256', | |
481 | 'code' => nil, | |
482 | 'issued_at' => Time.now.to_i, | |
483 | 'user_id' => '123456' | |
484 | } | |
485 | ||
486 | @request.stubs(:cookies).returns({"fbsr_#{@client_id}" => signed_request(@payload, @client_secret)}) | |
487 | end | |
488 | ||
489 | test 'calls fail! when a code is not included in the cookie' do | |
490 | strategy.expects(:fail!).times(1).with(:no_authorization_code, kind_of(OmniAuth::Strategies::Facebook::NoAuthorizationCodeError)) | |
491 | strategy.callback_phase | |
492 | end | |
493 | end | |
494 | ||
495 | class UnknownAlgorithmInCookieRequestTest < TestCase | |
496 | def setup | |
497 | super() | |
498 | @payload = { | |
499 | 'algorithm' => 'UNKNOWN-ALGO', | |
500 | 'code' => nil, | |
501 | 'issued_at' => Time.now.to_i, | |
502 | 'user_id' => '123456' | |
503 | } | |
504 | ||
505 | @request.stubs(:cookies).returns({"fbsr_#{@client_id}" => signed_request(@payload, @client_secret)}) | |
506 | end | |
507 | ||
508 | test 'calls fail! when an algorithm is unknown' do | |
509 | strategy.expects(:fail!).times(1).with(:unknown_signature_algorithm, kind_of(OmniAuth::Facebook::SignedRequest::UnknownSignatureAlgorithmError)) | |
510 | strategy.callback_phase | |
511 | end | |
512 | end | |
513 | end |
0 | require 'helper' | |
1 | require 'omniauth-facebook' | |
2 | require 'openssl' | |
3 | require 'base64' | |
4 | ||
5 | class StrategyTest < StrategyTestCase | |
6 | include OAuth2StrategyTests | |
7 | end | |
8 | ||
9 | class ClientTest < StrategyTestCase | |
10 | test 'has correct Facebook site' do | |
11 | assert_equal 'https://graph.facebook.com', strategy.client.site | |
12 | end | |
13 | ||
14 | test 'has correct authorize url' do | |
15 | assert_equal 'https://www.facebook.com/dialog/oauth', strategy.client.options[:authorize_url] | |
16 | end | |
17 | ||
18 | test 'has correct token url with versioning' do | |
19 | @options = {:client_options => {:site => 'https://graph.facebook.net/v2.2'}} | |
20 | assert_equal 'oauth/access_token', strategy.client.options[:token_url] | |
21 | assert_equal 'https://graph.facebook.net/v2.2/oauth/access_token', strategy.client.token_url | |
22 | end | |
23 | end | |
24 | ||
25 | class CallbackUrlTest < StrategyTestCase | |
26 | test "returns the default callback url" do | |
27 | url_base = 'http://auth.request.com' | |
28 | @request.stubs(:url).returns("#{url_base}/some/page") | |
29 | strategy.stubs(:script_name).returns('') # as not to depend on Rack env | |
30 | assert_equal "#{url_base}/auth/facebook/callback", strategy.callback_url | |
31 | end | |
32 | ||
33 | test "returns path from callback_path option" do | |
34 | @options = { :callback_path => "/auth/FB/done"} | |
35 | url_base = 'http://auth.request.com' | |
36 | @request.stubs(:url).returns("#{url_base}/page/path") | |
37 | strategy.stubs(:script_name).returns('') # as not to depend on Rack env | |
38 | assert_equal "#{url_base}/auth/FB/done", strategy.callback_url | |
39 | end | |
40 | ||
41 | test "returns url from callback_url option" do | |
42 | url = 'https://auth.myapp.com/auth/fb/callback' | |
43 | @options = { :callback_url => url } | |
44 | assert_equal url, strategy.callback_url | |
45 | end | |
46 | end | |
47 | ||
48 | class AuthorizeParamsTest < StrategyTestCase | |
49 | test 'includes default scope for email' do | |
50 | assert strategy.authorize_params.is_a?(Hash) | |
51 | assert_equal 'email', strategy.authorize_params[:scope] | |
52 | end | |
53 | ||
54 | test 'includes display parameter from request when present' do | |
55 | @request.stubs(:params).returns({ 'display' => 'touch' }) | |
56 | assert strategy.authorize_params.is_a?(Hash) | |
57 | assert_equal 'touch', strategy.authorize_params[:display] | |
58 | end | |
59 | ||
60 | test 'includes auth_type parameter from request when present' do | |
61 | @request.stubs(:params).returns({ 'auth_type' => 'reauthenticate' }) | |
62 | assert strategy.authorize_params.is_a?(Hash) | |
63 | assert_equal 'reauthenticate', strategy.authorize_params[:auth_type] | |
64 | end | |
65 | ||
66 | test 'overrides default scope with parameter passed from request' do | |
67 | @request.stubs(:params).returns({ 'scope' => 'email' }) | |
68 | assert strategy.authorize_params.is_a?(Hash) | |
69 | assert_equal 'email', strategy.authorize_params[:scope] | |
70 | end | |
71 | end | |
72 | ||
73 | class TokeParamsTest < StrategyTestCase | |
74 | test 'has correct parse strategy' do | |
75 | assert_equal :query, strategy.token_params[:parse] | |
76 | end | |
77 | end | |
78 | ||
79 | class AccessTokenOptionsTest < StrategyTestCase | |
80 | test 'has correct param name by default' do | |
81 | assert_equal 'access_token', strategy.access_token_options[:param_name] | |
82 | end | |
83 | ||
84 | test 'has correct header format by default' do | |
85 | assert_equal 'OAuth %s', strategy.access_token_options[:header_format] | |
86 | end | |
87 | end | |
88 | ||
89 | class UidTest < StrategyTestCase | |
90 | def setup | |
91 | super | |
92 | strategy.stubs(:raw_info).returns({ 'id' => '123' }) | |
93 | end | |
94 | ||
95 | test 'returns the id from raw_info' do | |
96 | assert_equal '123', strategy.uid | |
97 | end | |
98 | end | |
99 | ||
100 | class InfoTest < StrategyTestCase | |
101 | test 'returns the secure facebook avatar url when `secure_image_url` option is specified' do | |
102 | @options = { :secure_image_url => true } | |
103 | raw_info = { 'name' => 'Fred Smith', 'id' => '321' } | |
104 | strategy.stubs(:raw_info).returns(raw_info) | |
105 | assert_equal 'https://graph.facebook.com/321/picture', strategy.info['image'] | |
106 | end | |
107 | ||
108 | test 'returns the image_url based of the client site' do | |
109 | @options = { :secure_image_url => true, :client_options => {:site => "https://blah.facebook.com/v2.2"}} | |
110 | raw_info = { 'name' => 'Fred Smith', 'id' => '321' } | |
111 | strategy.stubs(:raw_info).returns(raw_info) | |
112 | assert_equal 'https://blah.facebook.com/v2.2/321/picture', strategy.info['image'] | |
113 | end | |
114 | ||
115 | test 'returns the image with size specified in the `image_size` option' do | |
116 | @options = { :image_size => 'normal' } | |
117 | raw_info = { 'name' => 'Fred Smith', 'id' => '321' } | |
118 | strategy.stubs(:raw_info).returns(raw_info) | |
119 | assert_equal 'http://graph.facebook.com/321/picture?type=normal', strategy.info['image'] | |
120 | end | |
121 | ||
122 | test 'returns the image with size specified as a symbol in the `image_size` option' do | |
123 | @options = { :image_size => :normal } | |
124 | raw_info = { 'name' => 'Fred Smith', 'id' => '321' } | |
125 | strategy.stubs(:raw_info).returns(raw_info) | |
126 | assert_equal 'http://graph.facebook.com/321/picture?type=normal', strategy.info['image'] | |
127 | end | |
128 | ||
129 | test 'returns the image with width and height specified in the `image_size` option' do | |
130 | @options = { :image_size => { :width => 123, :height => 987 } } | |
131 | raw_info = { 'name' => 'Fred Smith', 'id' => '321' } | |
132 | strategy.stubs(:raw_info).returns(raw_info) | |
133 | assert_match 'width=123', strategy.info['image'] | |
134 | assert_match 'height=987', strategy.info['image'] | |
135 | assert_match 'http://graph.facebook.com/321/picture?', strategy.info['image'] | |
136 | end | |
137 | end | |
138 | ||
139 | class InfoTestOptionalDataPresent < StrategyTestCase | |
140 | def setup | |
141 | super | |
142 | @raw_info ||= { 'name' => 'Fred Smith' } | |
143 | strategy.stubs(:raw_info).returns(@raw_info) | |
144 | end | |
145 | ||
146 | test 'returns the name' do | |
147 | assert_equal 'Fred Smith', strategy.info['name'] | |
148 | end | |
149 | ||
150 | test 'returns the email' do | |
151 | @raw_info['email'] = 'fred@smith.com' | |
152 | assert_equal 'fred@smith.com', strategy.info['email'] | |
153 | end | |
154 | ||
155 | test 'returns the username as nickname' do | |
156 | @raw_info['username'] = 'fredsmith' | |
157 | assert_equal 'fredsmith', strategy.info['nickname'] | |
158 | end | |
159 | ||
160 | test 'returns the first name' do | |
161 | @raw_info['first_name'] = 'Fred' | |
162 | assert_equal 'Fred', strategy.info['first_name'] | |
163 | end | |
164 | ||
165 | test 'returns the last name' do | |
166 | @raw_info['last_name'] = 'Smith' | |
167 | assert_equal 'Smith', strategy.info['last_name'] | |
168 | end | |
169 | ||
170 | test 'returns the location name as location' do | |
171 | @raw_info['location'] = { 'id' => '104022926303756', 'name' => 'Palo Alto, California' } | |
172 | assert_equal 'Palo Alto, California', strategy.info['location'] | |
173 | end | |
174 | ||
175 | test 'returns bio as description' do | |
176 | @raw_info['bio'] = 'I am great' | |
177 | assert_equal 'I am great', strategy.info['description'] | |
178 | end | |
179 | ||
180 | test 'returns the facebook avatar url' do | |
181 | @raw_info['id'] = '321' | |
182 | assert_equal 'http://graph.facebook.com/321/picture', strategy.info['image'] | |
183 | end | |
184 | ||
185 | test 'returns the Facebook link as the Facebook url' do | |
186 | @raw_info['link'] = 'http://www.facebook.com/fredsmith' | |
187 | assert_kind_of Hash, strategy.info['urls'] | |
188 | assert_equal 'http://www.facebook.com/fredsmith', strategy.info['urls']['Facebook'] | |
189 | end | |
190 | ||
191 | test 'returns website url' do | |
192 | @raw_info['website'] = 'https://my-wonderful-site.com' | |
193 | assert_kind_of Hash, strategy.info['urls'] | |
194 | assert_equal 'https://my-wonderful-site.com', strategy.info['urls']['Website'] | |
195 | end | |
196 | ||
197 | test 'return both Facebook link and website urls' do | |
198 | @raw_info['link'] = 'http://www.facebook.com/fredsmith' | |
199 | @raw_info['website'] = 'https://my-wonderful-site.com' | |
200 | assert_kind_of Hash, strategy.info['urls'] | |
201 | assert_equal 'http://www.facebook.com/fredsmith', strategy.info['urls']['Facebook'] | |
202 | assert_equal 'https://my-wonderful-site.com', strategy.info['urls']['Website'] | |
203 | end | |
204 | ||
205 | test 'returns the positive verified status' do | |
206 | @raw_info['verified'] = true | |
207 | assert strategy.info['verified'] | |
208 | end | |
209 | ||
210 | test 'returns the negative verified status' do | |
211 | @raw_info['verified'] = false | |
212 | refute strategy.info['verified'] | |
213 | end | |
214 | end | |
215 | ||
216 | class InfoTestOptionalDataNotPresent < StrategyTestCase | |
217 | def setup | |
218 | super | |
219 | @raw_info ||= { 'name' => 'Fred Smith' } | |
220 | strategy.stubs(:raw_info).returns(@raw_info) | |
221 | end | |
222 | ||
223 | test 'has no email key' do | |
224 | refute_has_key 'email', strategy.info | |
225 | end | |
226 | ||
227 | test 'has no nickname key' do | |
228 | refute_has_key 'nickname', strategy.info | |
229 | end | |
230 | ||
231 | test 'has no first name key' do | |
232 | refute_has_key 'first_name', strategy.info | |
233 | end | |
234 | ||
235 | test 'has no last name key' do | |
236 | refute_has_key 'last_name', strategy.info | |
237 | end | |
238 | ||
239 | test 'has no location key' do | |
240 | refute_has_key 'location', strategy.info | |
241 | end | |
242 | ||
243 | test 'has no description key' do | |
244 | refute_has_key 'description', strategy.info | |
245 | end | |
246 | ||
247 | test 'has no urls' do | |
248 | refute_has_key 'urls', strategy.info | |
249 | end | |
250 | ||
251 | test 'has no verified key' do | |
252 | refute_has_key 'verified', strategy.info | |
253 | end | |
254 | end | |
255 | ||
256 | class RawInfoTest < StrategyTestCase | |
257 | def setup | |
258 | super | |
259 | @access_token = stub('OAuth2::AccessToken') | |
260 | @appsecret_proof = 'appsecret_proof' | |
261 | @options = {:appsecret_proof => @appsecret_proof} | |
262 | end | |
263 | ||
264 | test 'performs a GET to https://graph.facebook.com/me' do | |
265 | strategy.stubs(:appsecret_proof).returns(@appsecret_proof) | |
266 | strategy.stubs(:access_token).returns(@access_token) | |
267 | params = {:params => @options} | |
268 | @access_token.expects(:get).with('me', params).returns(stub_everything('OAuth2::Response')) | |
269 | strategy.raw_info | |
270 | end | |
271 | ||
272 | test 'performs a GET to https://graph.facebook.com/me with locale' do | |
273 | @options.merge!({ :locale => 'cs_CZ' }) | |
274 | strategy.stubs(:access_token).returns(@access_token) | |
275 | strategy.stubs(:appsecret_proof).returns(@appsecret_proof) | |
276 | params = {:params => @options} | |
277 | @access_token.expects(:get).with('me', params).returns(stub_everything('OAuth2::Response')) | |
278 | strategy.raw_info | |
279 | end | |
280 | ||
281 | test 'performs a GET to https://graph.facebook.com/me with info_fields' do | |
282 | @options.merge!({:info_fields => 'about'}) | |
283 | strategy.stubs(:access_token).returns(@access_token) | |
284 | strategy.stubs(:appsecret_proof).returns(@appsecret_proof) | |
285 | params = {:params => {:appsecret_proof => @appsecret_proof, :fields => 'about'}} | |
286 | @access_token.expects(:get).with('me', params).returns(stub_everything('OAuth2::Response')) | |
287 | strategy.raw_info | |
288 | end | |
289 | ||
290 | test 'returns a Hash' do | |
291 | strategy.stubs(:access_token).returns(@access_token) | |
292 | strategy.stubs(:appsecret_proof).returns(@appsecret_proof) | |
293 | raw_response = stub('Faraday::Response') | |
294 | raw_response.stubs(:body).returns('{ "ohai": "thar" }') | |
295 | raw_response.stubs(:status).returns(200) | |
296 | raw_response.stubs(:headers).returns({'Content-Type' => 'application/json' }) | |
297 | oauth2_response = OAuth2::Response.new(raw_response) | |
298 | params = {:params => @options} | |
299 | @access_token.stubs(:get).with('me', params).returns(oauth2_response) | |
300 | assert_kind_of Hash, strategy.raw_info | |
301 | assert_equal 'thar', strategy.raw_info['ohai'] | |
302 | end | |
303 | ||
304 | test 'returns an empty hash when the response is false' do | |
305 | strategy.stubs(:access_token).returns(@access_token) | |
306 | strategy.stubs(:appsecret_proof).returns(@appsecret_proof) | |
307 | oauth2_response = stub('OAuth2::Response', :parsed => false) | |
308 | params = {:params => @options} | |
309 | @access_token.stubs(:get).with('me', params).returns(oauth2_response) | |
310 | assert_kind_of Hash, strategy.raw_info | |
311 | assert_equal({}, strategy.raw_info) | |
312 | end | |
313 | ||
314 | test 'should not include raw_info in extras hash when skip_info is specified' do | |
315 | @options = { :skip_info => true } | |
316 | strategy.stubs(:raw_info).returns({:foo => 'bar' }) | |
317 | refute_has_key 'raw_info', strategy.extra | |
318 | end | |
319 | end | |
320 | ||
321 | class CredentialsTest < StrategyTestCase | |
322 | def setup | |
323 | super | |
324 | @access_token = stub('OAuth2::AccessToken') | |
325 | @access_token.stubs(:token) | |
326 | @access_token.stubs(:expires?) | |
327 | @access_token.stubs(:expires_at) | |
328 | @access_token.stubs(:refresh_token) | |
329 | strategy.stubs(:access_token).returns(@access_token) | |
330 | end | |
331 | ||
332 | test 'returns a Hash' do | |
333 | assert_kind_of Hash, strategy.credentials | |
334 | end | |
335 | ||
336 | test 'returns the token' do | |
337 | @access_token.stubs(:token).returns('123') | |
338 | assert_equal '123', strategy.credentials['token'] | |
339 | end | |
340 | ||
341 | test 'returns the expiry status' do | |
342 | @access_token.stubs(:expires?).returns(true) | |
343 | assert strategy.credentials['expires'] | |
344 | ||
345 | @access_token.stubs(:expires?).returns(false) | |
346 | refute strategy.credentials['expires'] | |
347 | end | |
348 | ||
349 | test 'returns the refresh token and expiry time when expiring' do | |
350 | ten_mins_from_now = (Time.now + 600).to_i | |
351 | @access_token.stubs(:expires?).returns(true) | |
352 | @access_token.stubs(:refresh_token).returns('321') | |
353 | @access_token.stubs(:expires_at).returns(ten_mins_from_now) | |
354 | assert_equal '321', strategy.credentials['refresh_token'] | |
355 | assert_equal ten_mins_from_now, strategy.credentials['expires_at'] | |
356 | end | |
357 | ||
358 | test 'does not return the refresh token when test is nil and expiring' do | |
359 | @access_token.stubs(:expires?).returns(true) | |
360 | @access_token.stubs(:refresh_token).returns(nil) | |
361 | assert_nil strategy.credentials['refresh_token'] | |
362 | refute_has_key 'refresh_token', strategy.credentials | |
363 | end | |
364 | ||
365 | test 'does not return the refresh token when not expiring' do | |
366 | @access_token.stubs(:expires?).returns(false) | |
367 | @access_token.stubs(:refresh_token).returns('XXX') | |
368 | assert_nil strategy.credentials['refresh_token'] | |
369 | refute_has_key 'refresh_token', strategy.credentials | |
370 | end | |
371 | end | |
372 | ||
373 | class ExtraTest < StrategyTestCase | |
374 | def setup | |
375 | super | |
376 | @raw_info = { 'name' => 'Fred Smith' } | |
377 | strategy.stubs(:raw_info).returns(@raw_info) | |
378 | end | |
379 | ||
380 | test 'returns a Hash' do | |
381 | assert_kind_of Hash, strategy.extra | |
382 | end | |
383 | ||
384 | test 'contains raw info' do | |
385 | assert_equal({ 'raw_info' => @raw_info }, strategy.extra) | |
386 | end | |
387 | end | |
388 | ||
389 | module SignedRequestHelpers | |
390 | def signed_request(payload, secret) | |
391 | encoded_payload = base64_encode_url(MultiJson.encode(payload)) | |
392 | encoded_signature = base64_encode_url(signature(encoded_payload, secret)) | |
393 | [encoded_signature, encoded_payload].join('.') | |
394 | end | |
395 | ||
396 | def base64_encode_url(value) | |
397 | Base64.encode64(value).tr('+/', '-_').gsub(/\n/, '') | |
398 | end | |
399 | ||
400 | def signature(payload, secret, algorithm = OpenSSL::Digest::SHA256.new) | |
401 | OpenSSL::HMAC.digest(algorithm, secret, payload) | |
402 | end | |
403 | end | |
404 | ||
405 | module SignedRequestTests | |
406 | class TestCase < StrategyTestCase | |
407 | include SignedRequestHelpers | |
408 | end | |
409 | ||
410 | class CookieAndParamNotPresentTest < TestCase | |
411 | test 'is nil' do | |
412 | assert_nil strategy.send(:signed_request_from_cookie) | |
413 | end | |
414 | ||
415 | test 'throws an error on calling build_access_token' do | |
416 | assert_raises(OmniAuth::Strategies::Facebook::NoAuthorizationCodeError) { strategy.send(:with_authorization_code!) {} } | |
417 | end | |
418 | end | |
419 | ||
420 | class CookiePresentTest < TestCase | |
421 | def setup(algo = nil) | |
422 | super() | |
423 | @payload = { | |
424 | 'algorithm' => algo || 'HMAC-SHA256', | |
425 | 'code' => 'm4c0d3z', | |
426 | 'issued_at' => Time.now.to_i, | |
427 | 'user_id' => '123456' | |
428 | } | |
429 | ||
430 | @request.stubs(:cookies).returns({"fbsr_#{@client_id}" => signed_request(@payload, @client_secret)}) | |
431 | end | |
432 | ||
433 | test 'parses the access code out from the cookie' do | |
434 | assert_equal @payload, strategy.send(:signed_request_from_cookie) | |
435 | end | |
436 | ||
437 | test 'throws an error if the algorithm is unknown' do | |
438 | setup('UNKNOWN-ALGO') | |
439 | assert_equal "unknown algorithm: UNKNOWN-ALGO", assert_raises(OmniAuth::Strategies::Facebook::UnknownSignatureAlgorithmError) { strategy.send(:signed_request_from_cookie) }.message | |
440 | end | |
441 | end | |
442 | ||
443 | class EmptySignedRequestTest < TestCase | |
444 | def setup | |
445 | super | |
446 | @request.stubs(:params).returns({'signed_request' => ''}) | |
447 | end | |
448 | ||
449 | test 'empty param' do | |
450 | assert_equal nil, strategy.send(:signed_request_from_cookie) | |
451 | end | |
452 | end | |
453 | ||
454 | class MissingCodeInParamsRequestTest < TestCase | |
455 | def setup | |
456 | super | |
457 | @request.stubs(:params).returns({}) | |
458 | end | |
459 | ||
460 | test 'calls fail! when a code is not included in the params' do | |
461 | strategy.expects(:fail!).times(1).with(:no_authorization_code, kind_of(OmniAuth::Strategies::Facebook::NoAuthorizationCodeError)) | |
462 | strategy.callback_phase | |
463 | end | |
464 | end | |
465 | ||
466 | class MissingCodeInCookieRequestTest < TestCase | |
467 | def setup(algo = nil) | |
468 | super() | |
469 | @payload = { | |
470 | 'algorithm' => algo || 'HMAC-SHA256', | |
471 | 'code' => nil, | |
472 | 'issued_at' => Time.now.to_i, | |
473 | 'user_id' => '123456' | |
474 | } | |
475 | ||
476 | @request.stubs(:cookies).returns({"fbsr_#{@client_id}" => signed_request(@payload, @client_secret)}) | |
477 | end | |
478 | ||
479 | test 'calls fail! when a code is not included in the cookie' do | |
480 | strategy.expects(:fail!).times(1).with(:no_authorization_code, kind_of(OmniAuth::Strategies::Facebook::NoAuthorizationCodeError)) | |
481 | strategy.callback_phase | |
482 | end | |
483 | end | |
484 | ||
485 | class UnknownAlgorithmInCookieRequestTest < TestCase | |
486 | def setup | |
487 | super() | |
488 | @payload = { | |
489 | 'algorithm' => 'UNKNOWN-ALGO', | |
490 | 'code' => nil, | |
491 | 'issued_at' => Time.now.to_i, | |
492 | 'user_id' => '123456' | |
493 | } | |
494 | ||
495 | @request.stubs(:cookies).returns({"fbsr_#{@client_id}" => signed_request(@payload, @client_secret)}) | |
496 | end | |
497 | ||
498 | test 'calls fail! when an algorithm is unknown' do | |
499 | strategy.expects(:fail!).times(1).with(:unknown_signature_algorithm, kind_of(OmniAuth::Strategies::Facebook::UnknownSignatureAlgorithmError)) | |
500 | strategy.callback_phase | |
501 | end | |
502 | end | |
503 | end |