Codebase list ruby-omniauth-facebook / fa9d6bc
Merge tag 'upstream/2.0.0' Upstream version 2.0.0 Nitesh A Jain 9 years ago
16 changed file(s) with 249 addition(s) and 351 deletion(s). Raw diff Collapse all Expand all
55 .powenv
66 tmp
77 bin
8 example/app.log
77 - 1.9.2
88 - 1.9.3
99 - 2.0.0
10 - 2.1
1011 - jruby
12 - rbx
13 matrix:
14 allow_failures:
15 - rvm: rbx
0 ## 2.0.0 (2014-08-07)
1
2 Bugfixes:
3
4 - bump omniauth-oauth2 dependency which addresses CVE-2012-6134 (#162, @linedotstar)
5
06 ## 1.6.0 (2014-01-13)
17
28 Features:
5460 - have `raw_info` return an empty hash if the Facebook response returns false (#44, @brianjlandau)
5561 - prevent oauth2 from interpreting Facebook's expires field as `expires_in`, when it's really `expires_at` (#39, @watsonbox)
5662 - remove deprecated `offline_access` permission (@mkdynamic)
57
58 Changes:
59
6063 - tidy up the `callback_url` option (@mkdynamic)
6164
6265 ## 1.2.0 (2012-01-06)
11
22 gemspec
33
4 gem 'jruby-openssl', :platform => :jruby
4 platforms :rbx do
5 gem 'rubysl', '~> 2.0'
6 end
0 **NOTE: If you're running < 1.5.1, please upgrade to address 2 security vulnerabilities.
1 More details [here](https://github.com/mkdynamic/omniauth-facebook/wiki/CSRF-vulnerability:-CVE-2013-4562) and [here](https://github.com/mkdynamic/omniauth-facebook/wiki/Access-token-vulnerability:-CVE-2013-4593).**
0 **IMPORTANT: If you're running < 1.5.1, please upgrade to the latest version to address 3 security vulnerabilities.
1 More details [here](https://github.com/mkdynamic/omniauth-facebook/wiki/CSRF-vulnerability:-CVE-2013-4562), [here](https://github.com/mkdynamic/omniauth-facebook/wiki/Access-token-vulnerability:-CVE-2013-4593) and [here](http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2012-6134).**
22
33 ---
44
55 # OmniAuth Facebook &nbsp;[![Build Status](https://secure.travis-ci.org/mkdynamic/omniauth-facebook.png?branch=master)](https://travis-ci.org/mkdynamic/omniauth-facebook)
6
7 **These notes are based on master, please see tags for README pertaining to specific releases.**
68
79 Facebook OAuth2 Strategy for OmniAuth.
810
3638
3739 You can configure several options, which you pass in to the `provider` method via a `Hash`:
3840
39 * `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`
40 * `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`
41 * `auth_type`: Optionally specifies the requested authentication features as a comma-separated list, as per https://developers.facebook.com/docs/authentication/reauthentication/.
42 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`.
43 * `secure_image_url`: Set to `true` to use https for the avatar image url returned in the auth hash. Default is `false`.
44 * `image_size`: 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.
45 * `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/reference/api/user/ (only /me endpoint).
46 * `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/.
41 Option name | Default | Explanation
42 --- | --- | ---
43 `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 `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 `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 `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 `secure_image_url` | `false` | Set to `true` to use https for the avatar image url returned in the auth hash.
50 `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).
4751
4852 For example, to request `email`, `user_birthday` and `read_stream` permissions and display the authentication page in a popup window:
4953
5761 ### Per-Request Options
5862
5963 If you want to set the `display` format, `auth_type`, 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`.
60
61 ### Custom Callback URL/Path
62
63 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#L411) for more details on the default.
6464
6565 ## Auth Hash
6666
108108
109109 The precise information available may depend on the permissions which you request.
110110
111 ## Client-side Flow
111 ## Client-side Flow with Facebook Javascript SDK
112112
113113 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.
114114
115 Note that you must enable cookies in the `FB.init` config for this process to work.
115 **Note that you must enable cookies in the `FB.init` config for this process to work.**
116116
117 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.
117 See the example Sinatra app under `example/` and read the [Facebook docs on Login for JavaScript](https://developers.facebook.com/docs/facebook-login/login-flow-for-web/) for more details.
118118
119119 ### How it Works
120120
126126 2. extract the authorization code contained in it
127127 3. and hit Facebook and obtain an access token which will get placed in the `request.env['omniauth.auth']['credentials']` hash.
128128
129 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/).
130
131 ## Canvas Apps
132
133 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.
134
135 There are then 2 scenarios for what happens next:
136
137 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.
138
139 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.
140
141 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).
142
143 Bear in mind you have several [options](https://developers.facebook.com/docs/opengraph/authentication). Read [the Facebook docs on canvas page authentication](https://developers.facebook.com/docs/authentication/canvas/) for more info.
144
145129 ## Token Expiry
146130
147 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.
131 The expiration time of the access token you obtain will depend on which flow you are using.
148132
149133 ### Client-Side Flow
150134
151135 If you use the client-side flow, Facebook will give you back a short lived access token (~ 2 hours).
152136
153 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.
137 You can exchange this short lived access token for a longer lived version. Read the [Facebook docs](https://developers.facebook.com/docs/facebook-login/access-tokens/) for more information on exchanging a short lived token for a long lived token.
154138
155139 ### Server-Side Flow
156140
157141 If you use the server-side flow, Facebook will give you back a longer lived access token (~ 60 days).
158142
159 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.
160
161143 ## Supported Rubies
162144
163145 Actively tested with the following Ruby versions:
164146
147 - MRI 2.1.0
165148 - MRI 2.0.0
166149 - MRI 1.9.3
167150 - MRI 1.9.2
168151 - MRI 1.8.7
169 - JRuby 1.7.4
170
171 *NB.* For JRuby < 1.7, you'll need to install the `jruby-openssl` gem. There's no way to automatically specify this in a Rubygem gemspec, so you need to manually add it your project's own Gemfile:
172
173 ```ruby
174 gem 'jruby-openssl', :platform => :jruby
175 ```
152 - JRuby 1.7.9
153 - Rubinius (latest stable)
176154
177155 ## License
178156
183161 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
184162
185163 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
164
165
166 [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/mkdynamic/omniauth-facebook/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
167
Binary diff not shown
00 source 'https://rubygems.org'
11
22 gem 'sinatra'
3 gem 'sinatra-reloader'
34 gem 'omniauth-facebook', :path => '../'
00 PATH
11 remote: ../
22 specs:
3 omniauth-facebook (1.6.0.rc1)
3 omniauth-facebook (2.0.0.pre1)
44 omniauth-oauth2 (~> 1.1)
55
66 GEM
77 remote: https://rubygems.org/
88 specs:
9 faraday (0.8.8)
10 multipart-post (~> 1.2.0)
11 hashie (2.0.5)
12 httpauth (0.2.0)
13 jwt (0.1.8)
9 backports (3.3.5)
10 faraday (0.9.0)
11 multipart-post (>= 1.2, < 3)
12 hashie (2.1.1)
13 jwt (0.1.13)
1414 multi_json (>= 1.5)
1515 multi_json (1.8.2)
16 multipart-post (1.2.0)
17 oauth2 (0.8.1)
18 faraday (~> 0.8)
19 httpauth (~> 0.1)
20 jwt (~> 0.1.4)
21 multi_json (~> 1.0)
16 multi_xml (0.5.5)
17 multipart-post (2.0.0)
18 oauth2 (0.9.3)
19 faraday (>= 0.8, < 0.10)
20 jwt (~> 0.1.8)
21 multi_json (~> 1.3)
22 multi_xml (~> 0.5)
2223 rack (~> 1.2)
23 omniauth (1.1.4)
24 omniauth (1.2.1)
2425 hashie (>= 1.2, < 3)
25 rack
26 omniauth-oauth2 (1.1.1)
27 oauth2 (~> 0.8.0)
28 omniauth (~> 1.0)
26 rack (~> 1.0)
27 omniauth-oauth2 (1.1.2)
28 faraday (>= 0.8, < 0.10)
29 multi_json (~> 1.3)
30 oauth2 (~> 0.9.3)
31 omniauth (~> 1.2)
2932 rack (1.5.2)
3033 rack-protection (1.5.1)
3134 rack
35 rack-test (0.6.2)
36 rack (>= 1.0)
3237 sinatra (1.4.4)
3338 rack (~> 1.4)
3439 rack-protection (~> 1.4)
3540 tilt (~> 1.3, >= 1.3.4)
41 sinatra-contrib (1.4.2)
42 backports (>= 2.0)
43 multi_json
44 rack-protection
45 rack-test
46 sinatra (~> 1.4.0)
47 tilt (~> 1.3)
48 sinatra-reloader (1.0)
49 sinatra-contrib
3650 tilt (1.4.1)
3751
3852 PLATFORMS
4155 DEPENDENCIES
4256 omniauth-facebook!
4357 sinatra
58 sinatra-reloader
0 require 'sinatra'
1 require "sinatra/reloader"
2 require 'yaml'
3
4 # configure sinatra
5 set :run, false
6 set :raise_errors, true
7
8 # setup logging to file
9 log = File.new("app.log", "a+")
10 $stdout.reopen(log)
11 $stderr.reopen(log)
12 $stderr.sync = true
13 $stdout.sync = true
14
15 # server-side flow
16 get '/server-side' do
17 # NOTE: You would just hit this endpoint directly from the browser in a real app. The redirect is just here to
18 # explicit declare this server-side flow.
19 redirect '/auth/facebook'
20 end
21
22 # client-side flow
23 get '/client-side' do
24 content_type 'text/html'
25 # NOTE: When you enable cookie below in the FB.init call the GET request in the FB.login callback will send a signed
26 # request in a cookie back the OmniAuth callback which will parse out the authorization code and obtain an
27 # access_token with it.
28 <<-END
29 <html>
30 <head>
31 <title>Client-side Flow Example</title>
32 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js" type="text/javascript"></script>
33 </head>
34 <body>
35 <div id="fb-root"></div>
36
37 <script type="text/javascript">
38 window.fbAsyncInit = function() {
39 FB.init({
40 appId : '#{ENV['APP_ID']}',
41 status : true, // check login status
42 cookie : true, // enable cookies to allow the server to access the session
43 xfbml : true // parse XFBML
44 });
45 };
46
47 (function(d) {
48 var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
49 js = d.createElement('script'); js.id = id; js.async = true;
50 js.src = "//connect.facebook.net/en_US/all.js";
51 d.getElementsByTagName('head')[0].appendChild(js);
52 }(document));
53
54 $(function() {
55 $('a').click(function(e) {
56 e.preventDefault();
57
58 FB.login(function(response) {
59 if (response.authResponse) {
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 });
68 }
69 }, { scope: 'email,read_stream', state: 'abc123' });
70 });
71 });
72 </script>
73
74 <p id="connect">
75 <a href="#">Connect to FB!</a>
76 </p>
77
78 <p id="results" />
79 </body>
80 </html>
81 END
82 end
83
84 get '/auth/:provider/callback' do
85 content_type 'application/json'
86 MultiJson.encode(request.env)
87 end
88
89 get '/auth/failure' do
90 content_type 'application/json'
91 MultiJson.encode(request.env)
92 end
00 require 'bundler/setup'
1 require 'sinatra/base'
21 require 'omniauth-facebook'
2 require './app.rb'
33
4 SCOPE = 'email,read_stream'
4 use Rack::Session::Cookie, :secret => 'abc123'
55
6 class App < Sinatra::Base
7 # turn off sinatra default X-Frame-Options for FB canvas
8 set :protection, :except => :frame_options
9
10 # server-side flow
11 get '/' do
12 # NOTE: you would just hit this endpoint directly from the browser
13 # in a real app. the redirect is just here to setup the root
14 # path in this example sinatra app.
15 redirect '/auth/facebook'
16 end
17
18 # client-side flow
19 get '/client-side' do
20 content_type 'text/html'
21 # NOTE: when you enable cookie below in the FB.init call
22 # the GET request in the FB.login callback will send
23 # a signed request in a cookie back the OmniAuth callback
24 # which will parse out the authorization code and obtain
25 # the access_token. This will be the exact same access_token
26 # returned to the client in response.authResponse.accessToken.
27 <<-END
28 <html>
29 <head>
30 <title>Client-side Flow Example</title>
31 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js" type="text/javascript"></script>
32 </head>
33 <body>
34 <div id="fb-root"></div>
35
36 <script type="text/javascript">
37 window.fbAsyncInit = function() {
38 FB.init({
39 appId : '#{ENV['APP_ID']}',
40 status : true, // check login status
41 cookie : true, // enable cookies to allow the server to access the session
42 xfbml : true // parse XFBML
43 });
44 };
45
46 (function(d) {
47 var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
48 js = d.createElement('script'); js.id = id; js.async = true;
49 js.src = "//connect.facebook.net/en_US/all.js";
50 d.getElementsByTagName('head')[0].appendChild(js);
51 }(document));
52
53 $(function() {
54 $('a').click(function(e) {
55 e.preventDefault();
56
57 FB.login(function(response) {
58 if (response.authResponse) {
59 $('#connect').html('Connected! Hitting OmniAuth callback (GET /auth/facebook/callback)...');
60
61 // since we have cookies enabled, this request will allow omniauth to parse
62 // out the auth code from the signed request in the fbsr_XXX cookie
63 $.getJSON('/auth/facebook/callback', function(json) {
64 $('#connect').html('Connected! Callback complete.');
65 $('#results').html(JSON.stringify(json));
66 });
67 }
68 }, { scope: '#{SCOPE}' });
69 });
70 });
71 </script>
72
73 <p id="connect">
74 <a href="#">Connect to FB</a>
75 </p>
76
77 <p id="results" />
78 </body>
79 </html>
80 END
81 end
82
83 # auth via FB canvas and signed request param
84 post '/canvas/' do
85 # we just redirect to /auth/facebook here which will parse the
86 # signed_request FB sends us, asking for auth if the user has
87 # not already granted access, or simply moving straight to the
88 # callback where they have already granted access.
89 redirect "/auth/facebook?signed_request=#{request.params['signed_request']}"
90 end
91
92 get '/auth/:provider/callback' do
93 content_type 'application/json'
94 MultiJson.encode(request.env)
95 end
96
97 get '/auth/failure' do
98 content_type 'application/json'
99 MultiJson.encode(request.env)
100 end
6 use OmniAuth::Builder do
7 provider :facebook, ENV['APP_ID'], ENV['APP_SECRET'], :scope => 'email,read_stream'
1018 end
1029
103 use Rack::Session::Cookie
104
105 use OmniAuth::Builder do
106 provider :facebook, ENV['APP_ID'], ENV['APP_SECRET'], :scope => SCOPE
107 end
108
109 run App.new
10 run Sinatra::Application
00 module OmniAuth
11 module Facebook
2 VERSION = "1.6.0"
2 VERSION = "2.0.0"
33 end
44 end
6767 end
6868
6969 def callback_phase
70 super
70 with_authorization_code! do
71 super
72 end
7173 rescue NoAuthorizationCodeError => e
7274 fail!(:no_authorization_code, e)
7375 rescue UnknownSignatureAlgorithmError => e
7476 fail!(:unknown_signature_algoruthm, e)
7577 end
7678
77 def request_phase
78 if signed_request_contains_access_token?
79 # If we already have an access token, we can just hit the callback URL directly and pass the signed request.
80 params = { :signed_request => raw_signed_request }
81 query = Rack::Utils.build_query(params)
82
83 url = callback_url
84 url << "?" unless url.match(/\?/)
85 url << "&" unless url.match(/[\&\?]$/)
86 url << query
87
88 redirect url
89 else
90 super
91 end
92 end
93
9479 # NOTE If we're using code from the signed request then FB sets the redirect_uri to '' during the authorize
9580 # phase and it must match during the access_token phase:
96 # https://github.com/facebook/php-sdk/blob/master/src/base_facebook.php#L348
81 # https://github.com/facebook/facebook-php-sdk/blob/master/src/base_facebook.php#L477
9782 def callback_url
98 if @authorization_code_from_signed_request
83 if @authorization_code_from_signed_request_in_cookie
9984 ''
10085 else
10186 options[:callback_url] || super
10994 # You can pass +display+, +scope+, or +auth_type+ params to the auth request, if you need to set them dynamically.
11095 # You can also set these options in the OmniAuth config :authorize_params option.
11196 #
112 # /auth/facebook?display=popup
97 # For example: /auth/facebook?display=popup
11398 def authorize_params
11499 super.tap do |params|
115100 %w[display scope auth_type].each do |v|
122107 end
123108 end
124109
125 # Parse signed request in order, from:
126 #
127 # 1. The request 'signed_request' param (server-side flow from canvas pages) or
128 # 2. A cookie (client-side flow via JS SDK)
129 def signed_request
130 @signed_request ||= raw_signed_request && parse_signed_request(raw_signed_request)
131 end
132
133110 protected
134111
135112 def build_access_token
136 if signed_request_contains_access_token?
137 hash = signed_request.clone
138 ::OAuth2::AccessToken.new(
139 client,
140 hash.delete('oauth_token'),
141 hash.merge!(access_token_options.merge(:expires_at => hash.delete('expires')))
142 )
143 else
144 with_authorization_code! { super }.tap do |token|
145 token.options.merge!(access_token_options)
146 end
113 super.tap do |token|
114 token.options.merge!(access_token_options)
147115 end
148116 end
149117
150118 private
151119
152 def raw_signed_request
153 request.params['signed_request'] || request.cookies["fbsr_#{client.id}"]
154 end
155
156 # If the signed_request comes from a FB canvas page and the user has already authorized your application, the JSON
157 # object will be contain the access token.
158 #
159 # https://developers.facebook.com/docs/authentication/canvas/
160 def signed_request_contains_access_token?
161 signed_request && signed_request['oauth_token']
120 def signed_request_from_cookie
121 @signed_request_from_cookie ||= raw_signed_request_from_cookie && parse_signed_request(raw_signed_request_from_cookie)
122 end
123
124 def raw_signed_request_from_cookie
125 request.cookies["fbsr_#{client.id}"]
162126 end
163127
164128 # Picks the authorization code in order, from:
165129 #
166130 # 1. The request 'code' param (manual callback from standard server-side flow)
167 # 2. A signed request (see #signed_request for more)
131 # 2. A signed request from cookie (passed from the client during the client-side flow)
168132 def with_authorization_code!
169133 if request.params.key?('code')
170134 yield
171 elsif code_from_signed_request = signed_request && signed_request['code']
135 elsif code_from_signed_request = signed_request_from_cookie && signed_request_from_cookie['code']
172136 request.params['code'] = code_from_signed_request
173 @authorization_code_from_signed_request = true
137 @authorization_code_from_signed_request_in_cookie = true
138 # NOTE The code from the signed fbsr_XXX cookie is set by the FB JS SDK will confirm that the identity of the
139 # user contained in the signed request matches the user loading the app.
140 original_provider_ignores_state = options.provider_ignores_state
141 options.provider_ignores_state = true
174142 begin
175143 yield
176144 ensure
177145 request.params.delete('code')
178 @authorization_code_from_signed_request = false
146 @authorization_code_from_signed_request_in_cookie = false
147 options.provider_ignores_state = original_provider_ignores_state
179148 end
180149 else
181 raise NoAuthorizationCodeError, 'must pass either a `code` parameter or a signed request (via `signed_request` parameter or a `fbsr_XXX` cookie)'
150 raise NoAuthorizationCodeError, 'must pass either a `code` (via URL or by an `fbsr_XXX` signed request cookie)'
182151 end
183152 end
184153
00 --- !ruby/object:Gem::Specification
11 name: omniauth-facebook
22 version: !ruby/object:Gem::Version
3 version: 1.6.0
3 version: 2.0.0
44 platform: ruby
55 authors:
66 - Mark Dodwell
88 autorequire:
99 bindir: bin
1010 cert_chain: []
11 date: 2014-01-11 00:00:00.000000000 Z
11 date: 2014-08-07 00:00:00.000000000 Z
1212 dependencies:
1313 - !ruby/object:Gem::Dependency
1414 name: omniauth-oauth2
1616 requirements:
1717 - - ~>
1818 - !ruby/object:Gem::Version
19 version: '1.1'
19 version: '1.2'
2020 type: :runtime
2121 prerelease: false
2222 version_requirements: !ruby/object:Gem::Requirement
2323 requirements:
2424 - - ~>
2525 - !ruby/object:Gem::Version
26 version: '1.1'
26 version: '1.2'
2727 - !ruby/object:Gem::Dependency
2828 name: minitest
2929 requirement: !ruby/object:Gem::Requirement
8282 - Rakefile
8383 - example/Gemfile
8484 - example/Gemfile.lock
85 - example/app.rb
8586 - example/config.ru
8687 - lib/omniauth-facebook.rb
8788 - lib/omniauth/facebook.rb
111112 version: '0'
112113 requirements: []
113114 rubyforge_project:
114 rubygems_version: 2.0.2
115 rubygems_version: 2.2.2
115116 signing_key:
116117 specification_version: 4
117118 summary: Facebook OAuth2 Strategy for OmniAuth
1515 s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
1616 s.require_paths = ['lib']
1717
18 s.add_runtime_dependency 'omniauth-oauth2', '~> 1.1'
18 s.add_runtime_dependency 'omniauth-oauth2', '~> 1.2'
1919
2020 s.add_development_dependency 'minitest'
2121 s.add_development_dependency 'mocha'
3535 @request.stubs(:params).returns({})
3636 @request.stubs(:cookies).returns({})
3737 @request.stubs(:env).returns({})
38 @request.stubs(:scheme).returns({})
3839 @request.stubs(:ssl?).returns(false)
3940
4041 @client_id = '123'
114114 @options = { :image_size => { :width => 123, :height => 987 } }
115115 raw_info = { 'name' => 'Fred Smith', 'id' => '321' }
116116 strategy.stubs(:raw_info).returns(raw_info)
117 image_url = strategy.info['image']
118 path, query = image_url.split("?")
119 query_params = Hash[*query.split("&").map {|pair| pair.split("=") }.flatten]
120
121 assert_equal 'http://graph.facebook.com/321/picture', path
122 assert_equal '123', query_params['width']
123 assert_equal '987', query_params['height']
117 assert_match 'width=123', strategy.info['image']
118 assert_match 'height=987', strategy.info['image']
119 assert_match 'http://graph.facebook.com/321/picture?', strategy.info['image']
124120 end
125121 end
126122
397393
398394 class CookieAndParamNotPresentTest < TestCase
399395 test 'is nil' do
400 assert_nil strategy.send(:signed_request)
396 assert_nil strategy.send(:signed_request_from_cookie)
401397 end
402398
403399 test 'throws an error on calling build_access_token' do
404 assert_equal 'must pass either a `code` parameter or a signed request (via `signed_request` parameter or a `fbsr_XXX` cookie)',
405 assert_raises(OmniAuth::Strategies::Facebook::NoAuthorizationCodeError) { strategy.send(:build_access_token) }.message
400 assert_raises(OmniAuth::Strategies::Facebook::NoAuthorizationCodeError) { strategy.send(:with_authorization_code!) {} }
406401 end
407402 end
408403
420415 end
421416
422417 test 'parses the access code out from the cookie' do
423 assert_equal @payload, strategy.send(:signed_request)
418 assert_equal @payload, strategy.send(:signed_request_from_cookie)
424419 end
425420
426421 test 'throws an error if the algorithm is unknown' do
427422 setup('UNKNOWN-ALGO')
428 assert_equal "unknown algorithm: UNKNOWN-ALGO", assert_raises(OmniAuth::Strategies::Facebook::UnknownSignatureAlgorithmError) { strategy.send(:signed_request) }.message
429 end
430 end
431
432 class ParamPresentTest < TestCase
423 assert_equal "unknown algorithm: UNKNOWN-ALGO", assert_raises(OmniAuth::Strategies::Facebook::UnknownSignatureAlgorithmError) { strategy.send(:signed_request_from_cookie) }.message
424 end
425 end
426
427 class EmptySignedRequestTest < TestCase
428 def setup
429 super
430 @request.stubs(:params).returns({'signed_request' => ''})
431 end
432
433 test 'empty param' do
434 assert_equal nil, strategy.send(:signed_request_from_cookie)
435 end
436 end
437
438 class MissingCodeInParamsRequestTest < TestCase
439 def setup
440 super
441 @request.stubs(:params).returns({})
442 end
443
444 test 'calls fail! when a code is not included in the params' do
445 strategy.expects(:fail!).times(1).with(:no_authorization_code, kind_of(Exception))
446 strategy.callback_phase
447 end
448 end
449
450 class MissingCodeInCookieRequestTest < TestCase
433451 def setup(algo = nil)
434452 super()
435453 @payload = {
436454 'algorithm' => algo || 'HMAC-SHA256',
437 'oauth_token' => 'XXX',
455 'code' => nil,
438456 'issued_at' => Time.now.to_i,
439457 'user_id' => '123456'
440458 }
441459
442 @request.stubs(:params).returns({'signed_request' => signed_request(@payload, @client_secret)})
443 end
444
445 test 'parses the access code out from the param' do
446 assert_equal @payload, strategy.send(:signed_request)
447 end
448
449 test 'throws an error if the algorithm is unknown' do
450 setup('UNKNOWN-ALGO')
451 assert_equal "unknown algorithm: UNKNOWN-ALGO", assert_raises(OmniAuth::Strategies::Facebook::UnknownSignatureAlgorithmError) { strategy.send(:signed_request) }.message
452 end
453 end
454
455 class CookieAndParamPresentTest < TestCase
456 def setup
457 super
458 @payload_from_cookie = {
459 'algorithm' => 'HMAC-SHA256',
460 'from' => 'cookie'
461 }
462
463 @request.stubs(:cookies).returns({"fbsr_#{@client_id}" => signed_request(@payload_from_cookie, @client_secret)})
464
465 @payload_from_param = {
466 'algorithm' => 'HMAC-SHA256',
467 'from' => 'param'
468 }
469
470 @request.stubs(:params).returns({'signed_request' => signed_request(@payload_from_param, @client_secret)})
471 end
472
473 test 'picks param over cookie' do
474 assert_equal @payload_from_param, strategy.send(:signed_request)
475 end
476 end
477
478 class EmptySignedRequestTest < TestCase
479 def setup
480 super
481 @request.stubs(:params).returns({'signed_request' => ''})
482 end
483
484 test 'empty param' do
485 assert_equal nil, strategy.send(:signed_request)
486 end
487 end
488
489 end
490
491 class RequestPhaseWithSignedRequestTest < StrategyTestCase
492 include SignedRequestHelpers
493
494 def setup
495 super
496
497 payload = {
498 'algorithm' => 'HMAC-SHA256',
499 'oauth_token' => 'm4c0d3z'
500 }
501 @raw_signed_request = signed_request(payload, @client_secret)
502 @request.stubs(:params).returns("signed_request" => @raw_signed_request)
503
504 strategy.stubs(:callback_url).returns('/')
505 end
506
507 test 'redirects to callback passing along signed request' do
508 strategy.expects(:redirect).with("/?signed_request=#{Rack::Utils.escape(@raw_signed_request)}").once
509 strategy.request_phase
510 end
511 end
512
513 module BuildAccessTokenTests
514 class TestCase < StrategyTestCase
515 include SignedRequestHelpers
516 end
517
518 class ParamsContainSignedRequestWithAccessTokenTest < TestCase
519 def setup
520 super
521
522 @payload = {
523 'algorithm' => 'HMAC-SHA256',
524 'oauth_token' => 'm4c0d3z',
525 'expires' => Time.now.to_i
526 }
527 @raw_signed_request = signed_request(@payload, @client_secret)
528 @request.stubs(:params).returns({"signed_request" => @raw_signed_request})
529
530 strategy.stubs(:callback_url).returns('/')
531 end
532
533 test 'returns a new access token from the signed request' do
534 result = strategy.send(:build_access_token)
535 assert_kind_of ::OAuth2::AccessToken, result
536 assert_equal @payload['oauth_token'], result.token
537 end
538
539 test 'returns an access token with the correct expiry time' do
540 result = strategy.send(:build_access_token)
541 assert_equal @payload['expires'], result.expires_at
542 end
543 end
544 end
460 @request.stubs(:cookies).returns({"fbsr_#{@client_id}" => signed_request(@payload, @client_secret)})
461 end
462
463 test 'calls fail! when a code is not included in the cookie' do
464 strategy.expects(:fail!).times(1).with(:no_authorization_code, kind_of(Exception))
465 strategy.callback_phase
466 end
467 end
468 end