omniauth-facebook 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of omniauth-facebook might be problematic. Click here for more details.
- data/.gitignore +1 -0
- data/README.md +71 -15
- data/example/Gemfile +0 -3
- data/example/Gemfile.lock +14 -16
- data/example/config.ru +34 -9
- data/lib/omniauth/facebook/version.rb +1 -1
- data/lib/omniauth/strategies/facebook.rb +102 -42
- data/omniauth-facebook.gemspec +2 -2
- data/spec/omniauth/strategies/facebook_spec.rb +205 -52
- metadata +27 -17
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -26,28 +26,39 @@ Rails.application.config.middleware.use OmniAuth::Builder do
|
|
26
26
|
end
|
27
27
|
```
|
28
28
|
|
29
|
-
See
|
29
|
+
[See the example Sinatra app for full examples](https://github.com/mkdynamic/omniauth-facebook/blob/master/example/config.ru) of both the server and client-side flows (including using the Facebook Javascript SDK).
|
30
30
|
|
31
31
|
## Configuring
|
32
32
|
|
33
33
|
You can configure several options, which you pass in to the `provider` method via a `Hash`:
|
34
34
|
|
35
|
-
* `scope`: A comma-separated list of permissions you want to request from the user. See the Facebook docs for a full list of available permissions: http://developers.facebook.com/docs/reference/api/permissions. Default: `email
|
36
|
-
* `display`: The display context to show the authentication page. Options are: `page`, `popup
|
35
|
+
* `scope`: A comma-separated list of permissions you want to request from the user. See the Facebook docs for a full list of available permissions: http://developers.facebook.com/docs/reference/api/permissions. Default: `email`
|
36
|
+
* `display`: 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/. Default: `page`
|
37
|
+
* `secure_image_url`: Set to `true` to use https for the avatar image url returned in the auth hash. Default is `false`.
|
38
|
+
* `image_size`: Set the size for the returned image url in the auth hash. Valid options are `square` (50x50), `small` (50 pixels wide, variable height), `normal` (100 pixels wide, variable height), or `large` (about 200 pixels wide, variable height). Default is `square` (50x50).
|
39
|
+
|
40
|
+
For example, to request `email`, `user_birthday` and `read_stream` permissions and display the authentication page in a popup window:
|
37
41
|
|
38
|
-
For example, to request `email`, `offline_access` and `read_stream` permissions and display the authentication page in a popup window:
|
39
|
-
|
40
42
|
```ruby
|
41
43
|
Rails.application.config.middleware.use OmniAuth::Builder do
|
42
|
-
provider :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET'],
|
44
|
+
provider :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET'],
|
45
|
+
:scope => 'email,user_birthday,read_stream', :display => 'popup'
|
43
46
|
end
|
44
47
|
```
|
45
48
|
|
46
|
-
|
49
|
+
### Per-Request Options
|
50
|
+
|
51
|
+
If you want to set the `display` format or `scope` on a per-request basis, you can just pass it to the OmniAuth request phase URL, for example: `/auth/facebook?display=popup` or `/auth/facebook?scope=email`.
|
52
|
+
|
53
|
+
You can also pass through a `state` param which will be passed along to the callback url.
|
54
|
+
|
55
|
+
### Custom Callback URL/Path
|
47
56
|
|
48
|
-
|
57
|
+
You can set a custom `callback_url` or `callback_path` option to override the default value. See [OmniAuth::Strategy#callback_url](https://github.com/intridea/omniauth/blob/master/lib/omniauth/strategy.rb#L387) for more details on the default.
|
49
58
|
|
50
|
-
|
59
|
+
## Auth Hash
|
60
|
+
|
61
|
+
Here's an example *Auth Hash* available in `request.env['omniauth.auth']`:
|
51
62
|
|
52
63
|
```ruby
|
53
64
|
{
|
@@ -61,12 +72,13 @@ Here's an example *Authentication Hash* available in `request.env['omniauth.auth
|
|
61
72
|
:last_name => 'Bloggs',
|
62
73
|
:image => 'http://graph.facebook.com/1234567/picture?type=square',
|
63
74
|
:urls => { :Facebook => 'http://www.facebook.com/jbloggs' },
|
64
|
-
:location => 'Palo Alto, California'
|
75
|
+
:location => 'Palo Alto, California',
|
76
|
+
:verified => true
|
65
77
|
},
|
66
78
|
:credentials => {
|
67
79
|
:token => 'ABCDEF...', # OAuth 2.0 access_token, which you may wish to store
|
68
|
-
:expires_at => 1321747205, # when the access token expires (
|
69
|
-
:expires => true #
|
80
|
+
:expires_at => 1321747205, # when the access token expires (it always will)
|
81
|
+
:expires => true # this will always be true
|
70
82
|
},
|
71
83
|
:extra => {
|
72
84
|
:raw_info => {
|
@@ -92,9 +104,53 @@ The precise information available may depend on the permissions which you reques
|
|
92
104
|
|
93
105
|
## Client-side Flow
|
94
106
|
|
95
|
-
|
107
|
+
You can use the Facebook Javascript SDK with `FB.login`, and just hit the callback endpoint (`/auth/facebook/callback` by default) once the user has authenticated in the success callback.
|
108
|
+
|
109
|
+
Note that you must enable cookies in the `FB.init` config for this process to work.
|
110
|
+
|
111
|
+
See the example Sinatra app under `example/` and read the [Facebook docs on Client-Side Authentication](https://developers.facebook.com/docs/authentication/client-side/) for more details.
|
112
|
+
|
113
|
+
### How it Works
|
114
|
+
|
115
|
+
The client-side flow is supported by parsing the authorization code from the signed request which Facebook places in a cookie.
|
116
|
+
|
117
|
+
When you call `/auth/facebook/callback` in the success callback of `FB.login` that will pass the cookie back to the server. omniauth-facebook will see this cookie and:
|
118
|
+
|
119
|
+
1. parse it,
|
120
|
+
2. extract the authorization code contained in it
|
121
|
+
3. and hit Facebook and obtain an access token which will get placed in the `request.env['omniauth.auth']['credentials']` hash.
|
122
|
+
|
123
|
+
Note that this access token will be the same token obtained and available in the client through the hash [as (detailed in the Facebook docs](https://developers.facebook.com/docs/authentication/client-side/)).
|
124
|
+
|
125
|
+
## Canvas Apps
|
126
|
+
|
127
|
+
Canvas apps will send a signed request with the initial POST, therefore you *can* (if it makes sense for your app) pass this to the authorize endpoint (`/auth/facebook` by default) in the querystring.
|
128
|
+
|
129
|
+
There are then 2 scenarios for what happens next:
|
130
|
+
|
131
|
+
1. A user has already granted access to your app, this will contain an access token. In this case, omniauth-facebook will skip asking the user for authentication and immediately redirect to the callback endpoint (`/auth/facebook/callback` by default) with the access token present in the `request.env['omniauth.auth']['credentials']` hash.
|
132
|
+
|
133
|
+
2. A user has not granted access to your app, and the signed request *will not* contain an access token. In this case omniauth-facebook will simply follow the standard auth flow.
|
134
|
+
|
135
|
+
Take a look at [the example Sinatra app for one option of how you can integrate with a canvas page](https://github.com/mkdynamic/omniauth-facebook/blob/master/example/config.ru).
|
136
|
+
|
137
|
+
Bear in mind you have several options (including [authenticated referrals](https://developers.facebook.com/docs/opengraph/authentication/#referrals)). Read [the Facebook docs on canvas page authentication](https://developers.facebook.com/docs/authentication/canvas/) for more info.
|
138
|
+
|
139
|
+
## Token Expiry
|
140
|
+
|
141
|
+
Since Facebook deprecated the `offline_access` permission, this has become more complex. The expiration time of the access token you obtain will depend on which flow you are using. See below for more details.
|
142
|
+
|
143
|
+
### Client-Side Flow
|
144
|
+
|
145
|
+
If you use the client-side flow, Facebook will give you back a short lived access token (~ 2 hours).
|
146
|
+
|
147
|
+
You can exchange this short lived access token for a longer lived version. Read the [Facebook docs about the offline_access deprecation](https://developers.facebook.com/roadmap/offline-access-removal/) for more information.
|
148
|
+
|
149
|
+
### Server-Side Flow
|
150
|
+
|
151
|
+
If you use the server-side flow, Facebook will give you back a longer loved access token (~ 60 days).
|
96
152
|
|
97
|
-
|
153
|
+
If you're having issue getting a long lived token with the server-side flow, make sure to enable the 'deprecate offline_access setting' in you Facebook app config. Read the [Facebook docs about the offline_access deprecation](https://developers.facebook.com/roadmap/offline-access-removal/) for more information.
|
98
154
|
|
99
155
|
## Supported Rubies
|
100
156
|
|
@@ -113,7 +169,7 @@ gem 'jruby-openssl', :platform => :jruby
|
|
113
169
|
|
114
170
|
## License
|
115
171
|
|
116
|
-
Copyright (c)
|
172
|
+
Copyright (c) 2012 by Mark Dodwell
|
117
173
|
|
118
174
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
119
175
|
|
data/example/Gemfile
CHANGED
data/example/Gemfile.lock
CHANGED
@@ -1,30 +1,29 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../
|
3
3
|
specs:
|
4
|
-
omniauth-facebook (1.
|
5
|
-
omniauth-oauth2 (~> 1.0.
|
4
|
+
omniauth-facebook (1.2.0)
|
5
|
+
omniauth-oauth2 (~> 1.0.2)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: http://rubygems.org/
|
9
9
|
specs:
|
10
|
-
|
11
|
-
|
12
|
-
addressable (~> 2.2.6)
|
13
|
-
multipart-post (~> 1.1.3)
|
14
|
-
rack (>= 1.1.0, < 2)
|
10
|
+
faraday (0.8.0)
|
11
|
+
multipart-post (~> 1.1)
|
15
12
|
hashie (1.2.0)
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
httpauth (0.1)
|
14
|
+
multi_json (1.3.4)
|
15
|
+
multipart-post (1.1.5)
|
16
|
+
oauth2 (0.6.1)
|
19
17
|
faraday (~> 0.7)
|
20
|
-
|
21
|
-
|
18
|
+
httpauth (~> 0.1)
|
19
|
+
multi_json (~> 1.3)
|
20
|
+
omniauth (1.1.0)
|
22
21
|
hashie (~> 1.2)
|
23
22
|
rack
|
24
|
-
omniauth-oauth2 (1.0.
|
25
|
-
oauth2 (~> 0.
|
23
|
+
omniauth-oauth2 (1.0.2)
|
24
|
+
oauth2 (~> 0.6.0)
|
26
25
|
omniauth (~> 1.0)
|
27
|
-
rack (1.
|
26
|
+
rack (1.4.1)
|
28
27
|
rack-protection (1.2.0)
|
29
28
|
rack
|
30
29
|
sinatra (1.3.2)
|
@@ -38,5 +37,4 @@ PLATFORMS
|
|
38
37
|
|
39
38
|
DEPENDENCIES
|
40
39
|
omniauth-facebook!
|
41
|
-
rack (~> 1.3.6)
|
42
40
|
sinatra
|
data/example/config.ru
CHANGED
@@ -5,14 +5,17 @@ require 'omniauth-facebook'
|
|
5
5
|
SCOPE = 'email,read_stream'
|
6
6
|
|
7
7
|
class App < Sinatra::Base
|
8
|
+
# turn off sinatra default X-Frame-Options for FB canvas
|
9
|
+
set :protection, :except => :frame_options
|
10
|
+
|
8
11
|
# server-side flow
|
9
12
|
get '/' do
|
10
13
|
# NOTE: you would just hit this endpoint directly from the browser
|
11
|
-
# in a real app. the redirect is just here to setup the root
|
14
|
+
# in a real app. the redirect is just here to setup the root
|
12
15
|
# path in this example sinatra app.
|
13
16
|
redirect '/auth/facebook'
|
14
17
|
end
|
15
|
-
|
18
|
+
|
16
19
|
# client-side flow
|
17
20
|
get '/client-side' do
|
18
21
|
content_type 'text/html'
|
@@ -37,7 +40,6 @@ class App < Sinatra::Base
|
|
37
40
|
appId : '#{ENV['APP_ID']}',
|
38
41
|
status : true, // check login status
|
39
42
|
cookie : true, // enable cookies to allow the server to access the session
|
40
|
-
oauth : true, // enable OAuth 2.0
|
41
43
|
xfbml : true // parse XFBML
|
42
44
|
});
|
43
45
|
};
|
@@ -48,33 +50,56 @@ class App < Sinatra::Base
|
|
48
50
|
js.src = "//connect.facebook.net/en_US/all.js";
|
49
51
|
d.getElementsByTagName('head')[0].appendChild(js);
|
50
52
|
}(document));
|
51
|
-
|
53
|
+
|
52
54
|
$(function() {
|
53
55
|
$('a').click(function(e) {
|
54
56
|
e.preventDefault();
|
55
|
-
|
57
|
+
|
56
58
|
FB.login(function(response) {
|
57
59
|
if (response.authResponse) {
|
58
|
-
|
60
|
+
$('#connect').html('Connected! Hitting OmniAuth callback (GET /auth/facebook/callback)...');
|
61
|
+
|
62
|
+
// since we have cookies enabled, this request will allow omniauth to parse
|
63
|
+
// out the auth code from the signed request in the fbsr_XXX cookie
|
64
|
+
$.getJSON('/auth/facebook/callback', function(json) {
|
65
|
+
$('#connect').html('Connected! Callback complete.');
|
66
|
+
$('#results').html(JSON.stringify(json));
|
67
|
+
});
|
59
68
|
}
|
60
69
|
}, { scope: '#{SCOPE}' });
|
61
70
|
});
|
62
71
|
});
|
63
72
|
</script>
|
64
|
-
|
65
|
-
<p>
|
73
|
+
|
74
|
+
<p id="connect">
|
66
75
|
<a href="#">Connect to FB</a>
|
67
76
|
</p>
|
77
|
+
|
78
|
+
<p id="results" />
|
68
79
|
</body>
|
69
80
|
</html>
|
70
81
|
END
|
71
82
|
end
|
72
83
|
|
84
|
+
# auth via FB canvas and signed request param
|
85
|
+
post '/canvas/' do
|
86
|
+
# we just redirect to /auth/facebook here which will parse the
|
87
|
+
# signed_request FB sends us, asking for auth if the user has
|
88
|
+
# not already granted access, or simply moving straight to the
|
89
|
+
# callback where they have already granted access.
|
90
|
+
#
|
91
|
+
# we pass the state parameter which we can detect in our callback
|
92
|
+
# to do custom rendering/redirection for the canvas app page
|
93
|
+
redirect "/auth/facebook?signed_request=#{request.params['signed_request']}&state=canvas"
|
94
|
+
end
|
95
|
+
|
73
96
|
get '/auth/:provider/callback' do
|
97
|
+
# we can do something special here is +state+ param is canvas
|
98
|
+
# (see notes above in /canvas/ method for more details)
|
74
99
|
content_type 'application/json'
|
75
100
|
MultiJson.encode(request.env)
|
76
101
|
end
|
77
|
-
|
102
|
+
|
78
103
|
get '/auth/failure' do
|
79
104
|
content_type 'application/json'
|
80
105
|
MultiJson.encode(request.env)
|
@@ -1,12 +1,15 @@
|
|
1
1
|
require 'omniauth/strategies/oauth2'
|
2
2
|
require 'base64'
|
3
3
|
require 'openssl'
|
4
|
+
require 'rack/utils'
|
4
5
|
|
5
6
|
module OmniAuth
|
6
7
|
module Strategies
|
7
8
|
class Facebook < OmniAuth::Strategies::OAuth2
|
8
|
-
|
9
|
-
|
9
|
+
class NoAuthorizationCodeError < StandardError; end
|
10
|
+
|
11
|
+
DEFAULT_SCOPE = 'email'
|
12
|
+
|
10
13
|
option :client_options, {
|
11
14
|
:site => 'https://graph.facebook.com',
|
12
15
|
:token_url => '/oauth/access_token'
|
@@ -20,11 +23,11 @@ module OmniAuth
|
|
20
23
|
:header_format => 'OAuth %s',
|
21
24
|
:param_name => 'access_token'
|
22
25
|
}
|
23
|
-
|
26
|
+
|
24
27
|
option :authorize_options, [:scope, :display]
|
25
|
-
|
28
|
+
|
26
29
|
uid { raw_info['id'] }
|
27
|
-
|
30
|
+
|
28
31
|
info do
|
29
32
|
prune!({
|
30
33
|
'nickname' => raw_info['username'],
|
@@ -32,105 +35,162 @@ module OmniAuth
|
|
32
35
|
'name' => raw_info['name'],
|
33
36
|
'first_name' => raw_info['first_name'],
|
34
37
|
'last_name' => raw_info['last_name'],
|
35
|
-
'image' => "http://graph.facebook.com/#{uid}/picture?type
|
38
|
+
'image' => "#{options[:secure_image_url] ? 'https' : 'http'}://graph.facebook.com/#{uid}/picture?type=#{options[:image_size] || 'square'}",
|
36
39
|
'description' => raw_info['bio'],
|
37
40
|
'urls' => {
|
38
41
|
'Facebook' => raw_info['link'],
|
39
42
|
'Website' => raw_info['website']
|
40
43
|
},
|
41
|
-
'location' => (raw_info['location'] || {})['name']
|
44
|
+
'location' => (raw_info['location'] || {})['name'],
|
45
|
+
'verified' => raw_info['verified']
|
42
46
|
})
|
43
47
|
end
|
44
|
-
|
48
|
+
|
45
49
|
credentials do
|
46
50
|
prune!({
|
47
51
|
'expires' => access_token.expires?,
|
48
52
|
'expires_at' => access_token.expires_at
|
49
53
|
})
|
50
54
|
end
|
51
|
-
|
55
|
+
|
52
56
|
extra do
|
53
57
|
prune!({
|
54
58
|
'raw_info' => raw_info
|
55
59
|
})
|
56
60
|
end
|
57
|
-
|
61
|
+
|
58
62
|
def raw_info
|
59
|
-
@raw_info ||= access_token.get('/me').parsed
|
63
|
+
@raw_info ||= access_token.get('/me').parsed || {}
|
60
64
|
end
|
61
65
|
|
62
66
|
def build_access_token
|
63
|
-
|
64
|
-
|
67
|
+
if signed_request_contains_access_token?
|
68
|
+
hash = signed_request.clone
|
69
|
+
::OAuth2::AccessToken.new(
|
70
|
+
client,
|
71
|
+
hash.delete('oauth_token'),
|
72
|
+
hash.merge!(access_token_options.merge(:expires_at => hash.delete('expires')))
|
73
|
+
)
|
74
|
+
else
|
75
|
+
with_authorization_code! { super }.tap do |token|
|
76
|
+
token.options.merge!(access_token_options)
|
77
|
+
end
|
65
78
|
end
|
66
79
|
end
|
67
|
-
|
68
|
-
|
80
|
+
|
81
|
+
def request_phase
|
82
|
+
if signed_request_contains_access_token?
|
83
|
+
# if we already have an access token, we can just hit the
|
84
|
+
# callback URL directly and pass the signed request along
|
85
|
+
params = { :signed_request => raw_signed_request }
|
86
|
+
params[:state] = request.params['state'] if request.params['state']
|
87
|
+
query = Rack::Utils.build_query(params)
|
88
|
+
|
89
|
+
url = callback_url
|
90
|
+
url << "?" unless url.match(/\?/)
|
91
|
+
url << "&" unless url.match(/[\&\?]$/)
|
92
|
+
url << query
|
93
|
+
|
94
|
+
redirect url
|
95
|
+
else
|
96
|
+
super
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# NOTE if we're using code from the signed request
|
69
101
|
# then FB sets the redirect_uri to '' during the authorize
|
70
102
|
# phase + it must match during the access_token phase:
|
71
103
|
# https://github.com/facebook/php-sdk/blob/master/src/base_facebook.php#L348
|
72
104
|
def callback_url
|
73
|
-
if @
|
105
|
+
if @authorization_code_from_signed_request
|
74
106
|
''
|
75
107
|
else
|
76
|
-
|
77
|
-
options.authorize_options.callback_url
|
78
|
-
else
|
79
|
-
super
|
80
|
-
end
|
108
|
+
options[:callback_url] || super
|
81
109
|
end
|
82
110
|
end
|
83
111
|
|
84
112
|
def access_token_options
|
85
113
|
options.access_token_options.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
|
86
114
|
end
|
87
|
-
|
115
|
+
|
116
|
+
##
|
117
|
+
# You can pass +display+, +state+ or +scope+ params to the auth request, if
|
118
|
+
# you need to set them dynamically. You can also set these options
|
119
|
+
# in the OmniAuth config :authorize_params option.
|
120
|
+
#
|
121
|
+
# /auth/facebook?display=popup&state=ABC
|
122
|
+
#
|
88
123
|
def authorize_params
|
89
124
|
super.tap do |params|
|
90
|
-
params.
|
91
|
-
params.merge!(:state => request.params['state']) if request.params['state']
|
125
|
+
%w[display state scope].each { |v| params[v.to_sym] = request.params[v] if request.params[v] }
|
92
126
|
params[:scope] ||= DEFAULT_SCOPE
|
93
127
|
end
|
94
128
|
end
|
95
129
|
|
130
|
+
##
|
131
|
+
# Parse signed request in order, from:
|
132
|
+
#
|
133
|
+
# 1. the request 'signed_request' param (server-side flow from canvas pages) or
|
134
|
+
# 2. a cookie (client-side flow via JS SDK)
|
135
|
+
#
|
96
136
|
def signed_request
|
97
|
-
@signed_request ||=
|
98
|
-
|
99
|
-
parse_signed_request(cookie)
|
100
|
-
end
|
137
|
+
@signed_request ||= raw_signed_request &&
|
138
|
+
parse_signed_request(raw_signed_request)
|
101
139
|
end
|
102
|
-
|
140
|
+
|
103
141
|
private
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
142
|
+
|
143
|
+
def raw_signed_request
|
144
|
+
request.params['signed_request'] ||
|
145
|
+
request.cookies["fbsr_#{client.id}"]
|
146
|
+
end
|
147
|
+
|
148
|
+
##
|
149
|
+
# If the signed_request comes from a FB canvas page and the user
|
150
|
+
# has already authorized your application, the JSON object will be
|
151
|
+
# contain the access token.
|
152
|
+
#
|
153
|
+
# https://developers.facebook.com/docs/authentication/canvas/
|
154
|
+
#
|
155
|
+
def signed_request_contains_access_token?
|
156
|
+
signed_request &&
|
157
|
+
signed_request['oauth_token']
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
# Picks the authorization code in order, from:
|
162
|
+
#
|
163
|
+
# 1. the request 'code' param (manual callback from standard server-side flow)
|
164
|
+
# 2. a signed request (see #signed_request for more)
|
165
|
+
#
|
166
|
+
def with_authorization_code!
|
109
167
|
if request.params.key?('code')
|
110
168
|
yield
|
111
|
-
|
112
|
-
request.params['code'] =
|
113
|
-
@
|
169
|
+
elsif code_from_signed_request = signed_request && signed_request['code']
|
170
|
+
request.params['code'] = code_from_signed_request
|
171
|
+
@authorization_code_from_signed_request = true
|
114
172
|
begin
|
115
173
|
yield
|
116
174
|
ensure
|
117
175
|
request.params.delete('code')
|
118
|
-
@
|
176
|
+
@authorization_code_from_signed_request = false
|
119
177
|
end
|
178
|
+
else
|
179
|
+
raise NoAuthorizationCodeError, 'must pass either a `code` parameter or a signed request (via `signed_request` parameter or a `fbsr_XXX` cookie)'
|
120
180
|
end
|
121
181
|
end
|
122
|
-
|
182
|
+
|
123
183
|
def prune!(hash)
|
124
|
-
hash.delete_if do |_, value|
|
184
|
+
hash.delete_if do |_, value|
|
125
185
|
prune!(value) if value.is_a?(Hash)
|
126
186
|
value.nil? || (value.respond_to?(:empty?) && value.empty?)
|
127
187
|
end
|
128
188
|
end
|
129
|
-
|
189
|
+
|
130
190
|
def parse_signed_request(value)
|
131
191
|
signature, encoded_payload = value.split('.')
|
132
192
|
|
133
|
-
decoded_hex_signature = base64_decode_url(signature)
|
193
|
+
decoded_hex_signature = base64_decode_url(signature)
|
134
194
|
decoded_payload = MultiJson.decode(base64_decode_url(encoded_payload))
|
135
195
|
|
136
196
|
unless decoded_payload['algorithm'] == 'HMAC-SHA256'
|
data/omniauth-facebook.gemspec
CHANGED
@@ -15,8 +15,8 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
16
16
|
s.require_paths = ['lib']
|
17
17
|
|
18
|
-
s.add_runtime_dependency 'omniauth-oauth2', '~> 1.0.
|
18
|
+
s.add_runtime_dependency 'omniauth-oauth2', '~> 1.0.2'
|
19
19
|
|
20
|
-
s.add_development_dependency 'rspec', '~> 2
|
20
|
+
s.add_development_dependency 'rspec', '~> 2'
|
21
21
|
s.add_development_dependency 'rake'
|
22
22
|
end
|
@@ -8,11 +8,12 @@ describe OmniAuth::Strategies::Facebook do
|
|
8
8
|
@request = double('Request')
|
9
9
|
@request.stub(:params) { {} }
|
10
10
|
@request.stub(:cookies) { {} }
|
11
|
-
|
11
|
+
@request.stub(:env) { {} }
|
12
|
+
|
12
13
|
@client_id = '123'
|
13
14
|
@client_secret = '53cr3tz'
|
14
15
|
end
|
15
|
-
|
16
|
+
|
16
17
|
subject do
|
17
18
|
args = [@client_id, @client_secret, @options].compact
|
18
19
|
OmniAuth::Strategies::Facebook.new(nil, *args).tap do |strategy|
|
@@ -37,26 +38,34 @@ describe OmniAuth::Strategies::Facebook do
|
|
37
38
|
end
|
38
39
|
|
39
40
|
describe '#callback_url' do
|
40
|
-
it "returns
|
41
|
-
|
42
|
-
@
|
43
|
-
subject.
|
41
|
+
it "returns the default callback url" do
|
42
|
+
url_base = 'http://auth.request.com'
|
43
|
+
@request.stub(:url) { "#{url_base}/some/page" }
|
44
|
+
subject.stub(:script_name) { '' } # as not to depend on Rack env
|
45
|
+
subject.callback_url.should eq("#{url_base}/auth/facebook/callback")
|
44
46
|
end
|
45
47
|
|
46
|
-
it "
|
48
|
+
it "returns path from callback_path option" do
|
49
|
+
@options = { :callback_path => "/auth/FB/done"}
|
47
50
|
url_base = 'http://auth.request.com'
|
48
51
|
@request.stub(:url) { "#{url_base}/page/path" }
|
49
|
-
subject.stub(:script_name) {
|
50
|
-
subject.callback_url.should eq("#{url_base}/auth/
|
52
|
+
subject.stub(:script_name) { '' } # as not to depend on Rack env
|
53
|
+
subject.callback_url.should eq("#{url_base}/auth/FB/done")
|
54
|
+
end
|
55
|
+
|
56
|
+
it "returns url from callback_url option" do
|
57
|
+
url = 'https://auth.myapp.com/auth/fb/callback'
|
58
|
+
@options = { :callback_url => url }
|
59
|
+
subject.callback_url.should eq(url)
|
51
60
|
end
|
52
61
|
end
|
53
62
|
|
54
63
|
describe '#authorize_params' do
|
55
|
-
it 'includes default scope for email
|
64
|
+
it 'includes default scope for email' do
|
56
65
|
subject.authorize_params.should be_a(Hash)
|
57
|
-
subject.authorize_params[:scope].should eq('email
|
66
|
+
subject.authorize_params[:scope].should eq('email')
|
58
67
|
end
|
59
|
-
|
68
|
+
|
60
69
|
it 'includes display parameter from request when present' do
|
61
70
|
@request.stub(:params) { { 'display' => 'touch' } }
|
62
71
|
subject.authorize_params.should be_a(Hash)
|
@@ -68,6 +77,12 @@ describe OmniAuth::Strategies::Facebook do
|
|
68
77
|
subject.authorize_params.should be_a(Hash)
|
69
78
|
subject.authorize_params[:state].should eq('some_state')
|
70
79
|
end
|
80
|
+
|
81
|
+
it 'overrides default scope with parameter passed from request' do
|
82
|
+
@request.stub(:params) { { 'scope' => 'email' } }
|
83
|
+
subject.authorize_params.should be_a(Hash)
|
84
|
+
subject.authorize_params[:scope].should eq('email')
|
85
|
+
end
|
71
86
|
end
|
72
87
|
|
73
88
|
describe '#token_params' do
|
@@ -85,24 +100,24 @@ describe OmniAuth::Strategies::Facebook do
|
|
85
100
|
subject.access_token_options[:header_format].should eq('OAuth %s')
|
86
101
|
end
|
87
102
|
end
|
88
|
-
|
103
|
+
|
89
104
|
describe '#uid' do
|
90
105
|
before :each do
|
91
106
|
subject.stub(:raw_info) { { 'id' => '123' } }
|
92
107
|
end
|
93
|
-
|
108
|
+
|
94
109
|
it 'returns the id from raw_info' do
|
95
110
|
subject.uid.should eq('123')
|
96
111
|
end
|
97
112
|
end
|
98
|
-
|
113
|
+
|
99
114
|
describe '#info' do
|
100
|
-
before :each do
|
101
|
-
@raw_info ||= { 'name' => 'Fred Smith' }
|
102
|
-
subject.stub(:raw_info) { @raw_info }
|
103
|
-
end
|
104
|
-
|
105
115
|
context 'when optional data is not present in raw info' do
|
116
|
+
before :each do
|
117
|
+
@raw_info ||= { 'name' => 'Fred Smith' }
|
118
|
+
subject.stub(:raw_info) { @raw_info }
|
119
|
+
end
|
120
|
+
|
106
121
|
it 'has no email key' do
|
107
122
|
subject.info.should_not have_key('email')
|
108
123
|
end
|
@@ -110,33 +125,42 @@ describe OmniAuth::Strategies::Facebook do
|
|
110
125
|
it 'has no nickname key' do
|
111
126
|
subject.info.should_not have_key('nickname')
|
112
127
|
end
|
113
|
-
|
128
|
+
|
114
129
|
it 'has no first name key' do
|
115
130
|
subject.info.should_not have_key('first_name')
|
116
131
|
end
|
117
|
-
|
132
|
+
|
118
133
|
it 'has no last name key' do
|
119
134
|
subject.info.should_not have_key('last_name')
|
120
135
|
end
|
121
|
-
|
136
|
+
|
122
137
|
it 'has no location key' do
|
123
138
|
subject.info.should_not have_key('location')
|
124
139
|
end
|
125
|
-
|
140
|
+
|
126
141
|
it 'has no description key' do
|
127
142
|
subject.info.should_not have_key('description')
|
128
143
|
end
|
129
|
-
|
144
|
+
|
130
145
|
it 'has no urls' do
|
131
146
|
subject.info.should_not have_key('urls')
|
132
147
|
end
|
148
|
+
|
149
|
+
it 'has no verified key' do
|
150
|
+
subject.info.should_not have_key('verified')
|
151
|
+
end
|
133
152
|
end
|
134
|
-
|
135
|
-
context 'when data is present in raw info' do
|
153
|
+
|
154
|
+
context 'when optional data is present in raw info' do
|
155
|
+
before :each do
|
156
|
+
@raw_info ||= { 'name' => 'Fred Smith' }
|
157
|
+
subject.stub(:raw_info) { @raw_info }
|
158
|
+
end
|
159
|
+
|
136
160
|
it 'returns the name' do
|
137
161
|
subject.info['name'].should eq('Fred Smith')
|
138
162
|
end
|
139
|
-
|
163
|
+
|
140
164
|
it 'returns the email' do
|
141
165
|
@raw_info['email'] = 'fred@smith.com'
|
142
166
|
subject.info['email'].should eq('fred@smith.com')
|
@@ -146,44 +170,44 @@ describe OmniAuth::Strategies::Facebook do
|
|
146
170
|
@raw_info['username'] = 'fredsmith'
|
147
171
|
subject.info['nickname'].should eq('fredsmith')
|
148
172
|
end
|
149
|
-
|
173
|
+
|
150
174
|
it 'returns the first name' do
|
151
175
|
@raw_info['first_name'] = 'Fred'
|
152
176
|
subject.info['first_name'].should eq('Fred')
|
153
177
|
end
|
154
|
-
|
178
|
+
|
155
179
|
it 'returns the last name' do
|
156
180
|
@raw_info['last_name'] = 'Smith'
|
157
181
|
subject.info['last_name'].should eq('Smith')
|
158
182
|
end
|
159
|
-
|
183
|
+
|
160
184
|
it 'returns the location name as location' do
|
161
185
|
@raw_info['location'] = { 'id' => '104022926303756', 'name' => 'Palo Alto, California' }
|
162
186
|
subject.info['location'].should eq('Palo Alto, California')
|
163
187
|
end
|
164
|
-
|
188
|
+
|
165
189
|
it 'returns bio as description' do
|
166
190
|
@raw_info['bio'] = 'I am great'
|
167
191
|
subject.info['description'].should eq('I am great')
|
168
192
|
end
|
169
|
-
|
193
|
+
|
170
194
|
it 'returns the square format facebook avatar url' do
|
171
195
|
@raw_info['id'] = '321'
|
172
196
|
subject.info['image'].should eq('http://graph.facebook.com/321/picture?type=square')
|
173
197
|
end
|
174
|
-
|
198
|
+
|
175
199
|
it 'returns the Facebook link as the Facebook url' do
|
176
200
|
@raw_info['link'] = 'http://www.facebook.com/fredsmith'
|
177
201
|
subject.info['urls'].should be_a(Hash)
|
178
202
|
subject.info['urls']['Facebook'].should eq('http://www.facebook.com/fredsmith')
|
179
203
|
end
|
180
|
-
|
204
|
+
|
181
205
|
it 'returns website url' do
|
182
206
|
@raw_info['website'] = 'https://my-wonderful-site.com'
|
183
207
|
subject.info['urls'].should be_a(Hash)
|
184
208
|
subject.info['urls']['Website'].should eq('https://my-wonderful-site.com')
|
185
209
|
end
|
186
|
-
|
210
|
+
|
187
211
|
it 'return both Facebook link and website urls' do
|
188
212
|
@raw_info['link'] = 'http://www.facebook.com/fredsmith'
|
189
213
|
@raw_info['website'] = 'https://my-wonderful-site.com'
|
@@ -191,21 +215,45 @@ describe OmniAuth::Strategies::Facebook do
|
|
191
215
|
subject.info['urls']['Facebook'].should eq('http://www.facebook.com/fredsmith')
|
192
216
|
subject.info['urls']['Website'].should eq('https://my-wonderful-site.com')
|
193
217
|
end
|
218
|
+
|
219
|
+
it 'returns the positive verified status' do
|
220
|
+
@raw_info['verified'] = true
|
221
|
+
subject.info['verified'].should be_true
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'returns the negative verified status' do
|
225
|
+
@raw_info['verified'] = false
|
226
|
+
subject.info['verified'].should be_false
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'returns the secure facebook avatar url when `secure_image_url` option is specified' do
|
231
|
+
@options = { :secure_image_url => true }
|
232
|
+
raw_info = { 'name' => 'Fred Smith', 'id' => '321' }
|
233
|
+
subject.stub(:raw_info) { raw_info }
|
234
|
+
subject.info['image'].should eq('https://graph.facebook.com/321/picture?type=square')
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'returns the image size specified in the `image_size` option' do
|
238
|
+
@options = { :image_size => 'normal' }
|
239
|
+
raw_info = { 'name' => 'Fred Smith', 'id' => '321' }
|
240
|
+
subject.stub(:raw_info) { raw_info }
|
241
|
+
subject.info['image'].should eq('http://graph.facebook.com/321/picture?type=normal')
|
194
242
|
end
|
195
243
|
end
|
196
|
-
|
244
|
+
|
197
245
|
describe '#raw_info' do
|
198
246
|
before :each do
|
199
247
|
@access_token = double('OAuth2::AccessToken')
|
200
248
|
subject.stub(:access_token) { @access_token }
|
201
249
|
end
|
202
|
-
|
250
|
+
|
203
251
|
it 'performs a GET to https://graph.facebook.com/me' do
|
204
252
|
@access_token.stub(:get) { double('OAuth2::Response').as_null_object }
|
205
253
|
@access_token.should_receive(:get).with('/me')
|
206
254
|
subject.raw_info
|
207
255
|
end
|
208
|
-
|
256
|
+
|
209
257
|
it 'returns a Hash' do
|
210
258
|
@access_token.stub(:get).with('/me') do
|
211
259
|
raw_response = double('Faraday::Response')
|
@@ -217,6 +265,15 @@ describe OmniAuth::Strategies::Facebook do
|
|
217
265
|
subject.raw_info.should be_a(Hash)
|
218
266
|
subject.raw_info['ohai'].should eq('thar')
|
219
267
|
end
|
268
|
+
|
269
|
+
it 'returns an empty hash when the response is false' do
|
270
|
+
@access_token.stub(:get).with('/me') do
|
271
|
+
response = double('OAuth2::Response')
|
272
|
+
response.stub(:parsed => false)
|
273
|
+
response
|
274
|
+
end
|
275
|
+
subject.raw_info.should be_a(Hash)
|
276
|
+
end
|
220
277
|
end
|
221
278
|
|
222
279
|
describe '#credentials' do
|
@@ -228,24 +285,24 @@ describe OmniAuth::Strategies::Facebook do
|
|
228
285
|
@access_token.stub(:refresh_token)
|
229
286
|
subject.stub(:access_token) { @access_token }
|
230
287
|
end
|
231
|
-
|
288
|
+
|
232
289
|
it 'returns a Hash' do
|
233
290
|
subject.credentials.should be_a(Hash)
|
234
291
|
end
|
235
|
-
|
292
|
+
|
236
293
|
it 'returns the token' do
|
237
294
|
@access_token.stub(:token) { '123' }
|
238
295
|
subject.credentials['token'].should eq('123')
|
239
296
|
end
|
240
|
-
|
297
|
+
|
241
298
|
it 'returns the expiry status' do
|
242
299
|
@access_token.stub(:expires?) { true }
|
243
300
|
subject.credentials['expires'].should eq(true)
|
244
|
-
|
301
|
+
|
245
302
|
@access_token.stub(:expires?) { false }
|
246
303
|
subject.credentials['expires'].should eq(false)
|
247
304
|
end
|
248
|
-
|
305
|
+
|
249
306
|
it 'returns the refresh token and expiry time when expiring' do
|
250
307
|
ten_mins_from_now = (Time.now + 600).to_i
|
251
308
|
@access_token.stub(:expires?) { true }
|
@@ -254,14 +311,14 @@ describe OmniAuth::Strategies::Facebook do
|
|
254
311
|
subject.credentials['refresh_token'].should eq('321')
|
255
312
|
subject.credentials['expires_at'].should eq(ten_mins_from_now)
|
256
313
|
end
|
257
|
-
|
314
|
+
|
258
315
|
it 'does not return the refresh token when it is nil and expiring' do
|
259
316
|
@access_token.stub(:expires?) { true }
|
260
317
|
@access_token.stub(:refresh_token) { nil }
|
261
318
|
subject.credentials['refresh_token'].should be_nil
|
262
319
|
subject.credentials.should_not have_key('refresh_token')
|
263
320
|
end
|
264
|
-
|
321
|
+
|
265
322
|
it 'does not return the refresh token when not expiring' do
|
266
323
|
@access_token.stub(:expires?) { false }
|
267
324
|
@access_token.stub(:refresh_token) { 'XXX' }
|
@@ -269,29 +326,29 @@ describe OmniAuth::Strategies::Facebook do
|
|
269
326
|
subject.credentials.should_not have_key('refresh_token')
|
270
327
|
end
|
271
328
|
end
|
272
|
-
|
329
|
+
|
273
330
|
describe '#extra' do
|
274
331
|
before :each do
|
275
332
|
@raw_info = { 'name' => 'Fred Smith' }
|
276
333
|
subject.stub(:raw_info) { @raw_info }
|
277
334
|
end
|
278
|
-
|
335
|
+
|
279
336
|
it 'returns a Hash' do
|
280
337
|
subject.extra.should be_a(Hash)
|
281
338
|
end
|
282
|
-
|
339
|
+
|
283
340
|
it 'contains raw info' do
|
284
341
|
subject.extra.should eq({ 'raw_info' => @raw_info })
|
285
342
|
end
|
286
343
|
end
|
287
344
|
|
288
345
|
describe '#signed_request' do
|
289
|
-
context 'cookie not present' do
|
346
|
+
context 'cookie/param not present' do
|
290
347
|
it 'is nil' do
|
291
348
|
subject.send(:signed_request).should be_nil
|
292
349
|
end
|
293
350
|
end
|
294
|
-
|
351
|
+
|
295
352
|
context 'cookie present' do
|
296
353
|
before :each do
|
297
354
|
@payload = {
|
@@ -310,6 +367,102 @@ describe OmniAuth::Strategies::Facebook do
|
|
310
367
|
subject.send(:signed_request).should eq(@payload)
|
311
368
|
end
|
312
369
|
end
|
370
|
+
|
371
|
+
context 'param present' do
|
372
|
+
before :each do
|
373
|
+
@payload = {
|
374
|
+
'algorithm' => 'HMAC-SHA256',
|
375
|
+
'oauth_token' => 'XXX',
|
376
|
+
'issued_at' => Time.now.to_i,
|
377
|
+
'user_id' => '123456'
|
378
|
+
}
|
379
|
+
|
380
|
+
@request.stub(:params) do
|
381
|
+
{ 'signed_request' => signed_request(@payload, @client_secret) }
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
it 'parses the access code out from the param' do
|
386
|
+
subject.send(:signed_request).should eq(@payload)
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
context 'cookie + param present' do
|
391
|
+
before :each do
|
392
|
+
@payload_from_cookie = {
|
393
|
+
'algorithm' => 'HMAC-SHA256',
|
394
|
+
'from' => 'cookie'
|
395
|
+
}
|
396
|
+
|
397
|
+
@request.stub(:cookies) do
|
398
|
+
{ "fbsr_#{@client_id}" => signed_request(@payload_from_cookie, @client_secret) }
|
399
|
+
end
|
400
|
+
|
401
|
+
@payload_from_param = {
|
402
|
+
'algorithm' => 'HMAC-SHA256',
|
403
|
+
'from' => 'param'
|
404
|
+
}
|
405
|
+
|
406
|
+
@request.stub(:params) do
|
407
|
+
{ 'signed_request' => signed_request(@payload_from_param, @client_secret) }
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
it 'picks param over cookie' do
|
412
|
+
subject.send(:signed_request).should eq(@payload_from_param)
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
describe '#request_phase' do
|
418
|
+
describe 'params contain a signed request with an access token' do
|
419
|
+
before do
|
420
|
+
payload = {
|
421
|
+
'algorithm' => 'HMAC-SHA256',
|
422
|
+
'oauth_token' => 'm4c0d3z'
|
423
|
+
}
|
424
|
+
@raw_signed_request = signed_request(payload, @client_secret)
|
425
|
+
@request.stub(:params) do
|
426
|
+
{ "signed_request" => @raw_signed_request }
|
427
|
+
end
|
428
|
+
|
429
|
+
subject.stub(:callback_url) { '/' }
|
430
|
+
end
|
431
|
+
|
432
|
+
it 'redirects to callback passing along signed request' do
|
433
|
+
subject.should_receive(:redirect).with("/?signed_request=#{Rack::Utils.escape(@raw_signed_request)}").once
|
434
|
+
subject.request_phase
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
describe '#build_access_token' do
|
440
|
+
describe 'params contain a signed request with an access token' do
|
441
|
+
before do
|
442
|
+
@payload = {
|
443
|
+
'algorithm' => 'HMAC-SHA256',
|
444
|
+
'oauth_token' => 'm4c0d3z',
|
445
|
+
'expires' => Time.now.to_i
|
446
|
+
}
|
447
|
+
@raw_signed_request = signed_request(@payload, @client_secret)
|
448
|
+
@request.stub(:params) do
|
449
|
+
{ "signed_request" => @raw_signed_request }
|
450
|
+
end
|
451
|
+
|
452
|
+
subject.stub(:callback_url) { '/' }
|
453
|
+
end
|
454
|
+
|
455
|
+
it 'returns a new access token from the signed request' do
|
456
|
+
result = subject.build_access_token
|
457
|
+
result.should be_an_instance_of(::OAuth2::AccessToken)
|
458
|
+
result.token.should eq(@payload['oauth_token'])
|
459
|
+
end
|
460
|
+
|
461
|
+
it 'returns an access token with the correct expiry time' do
|
462
|
+
result = subject.build_access_token
|
463
|
+
result.expires_at.should eq(@payload['expires'])
|
464
|
+
end
|
465
|
+
end
|
313
466
|
end
|
314
467
|
|
315
468
|
private
|
@@ -324,7 +477,7 @@ private
|
|
324
477
|
Base64.encode64(value).tr('+/', '-_').gsub(/\n/, '')
|
325
478
|
end
|
326
479
|
|
327
|
-
def signature(payload, secret, algorithm = OpenSSL::Digest::SHA256.new)
|
480
|
+
def signature(payload, secret, algorithm = OpenSSL::Digest::SHA256.new)
|
328
481
|
OpenSSL::HMAC.digest(algorithm, secret, payload)
|
329
482
|
end
|
330
483
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: omniauth-facebook
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,33 +9,43 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-05-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: omniauth-oauth2
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 1.0.
|
21
|
+
version: 1.0.2
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.0.2
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: rspec
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ~>
|
31
36
|
- !ruby/object:Gem::Version
|
32
|
-
version: 2
|
37
|
+
version: '2'
|
33
38
|
type: :development
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '2'
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: rake
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ! '>='
|
@@ -43,7 +53,12 @@ dependencies:
|
|
43
53
|
version: '0'
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
47
62
|
description:
|
48
63
|
email:
|
49
64
|
- mark@mkdynamic.co.uk
|
@@ -79,21 +94,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
79
94
|
- - ! '>='
|
80
95
|
- !ruby/object:Gem::Version
|
81
96
|
version: '0'
|
82
|
-
segments:
|
83
|
-
- 0
|
84
|
-
hash: 896289232930300063
|
85
97
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
98
|
none: false
|
87
99
|
requirements:
|
88
100
|
- - ! '>='
|
89
101
|
- !ruby/object:Gem::Version
|
90
102
|
version: '0'
|
91
|
-
segments:
|
92
|
-
- 0
|
93
|
-
hash: 896289232930300063
|
94
103
|
requirements: []
|
95
104
|
rubyforge_project:
|
96
|
-
rubygems_version: 1.8.
|
105
|
+
rubygems_version: 1.8.22
|
97
106
|
signing_key:
|
98
107
|
specification_version: 3
|
99
108
|
summary: Facebook strategy for OmniAuth
|
@@ -101,3 +110,4 @@ test_files:
|
|
101
110
|
- spec/omniauth/strategies/facebook_spec.rb
|
102
111
|
- spec/spec_helper.rb
|
103
112
|
- spec/support/shared_examples.rb
|
113
|
+
has_rdoc:
|