omniauth-google-oauth2 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.gitignore +3 -0
- data/.travis.yml +7 -0
- data/README.md +56 -3
- data/examples/omni_auth.rb +13 -4
- data/lib/omniauth/google_oauth2/version.rb +1 -1
- data/lib/omniauth/strategies/google_oauth2.rb +30 -14
- data/omniauth-contrib.gemspec +2 -2
- data/spec/omniauth/strategies/google_oauth2_spec.rb +129 -95
- metadata +10 -13
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/.rvmrc +0 -1
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NTE1ZjFkZmYyYTNlM2I4MTI5NmM2MmZlMWFlYjZmNjQ2YjNhNDU1MA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
OTNiNzQ4MGVhMmFkODVhZGU3Yjk0ZTFjNmY1ZDlkNjM5ZTUwMTFkOA==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZGI2YjNkMWU5MzRjNzcyYjIxZTE1NGJhMTFkNWNmZDZmMGU1NjczODAwZjUz
|
10
|
+
Y2U5YWE5ZjU3ZDY4NTc5YTVhMmUyZmNhOTk0M2VjZmY3MTc5YzI4MzRiOWEw
|
11
|
+
MDZmY2YwNTE2YmQ1MGRiNDg5ZTBlNzI3OWU5ZDBkNmQ1NjNjOWM=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YTViOTc3NTgwMTJjYjg0NjhhZDJhYzRlM2E4MmMyZjA2NWQ4YWQ1NWRmODAy
|
14
|
+
YjZlYjQ3OTkxYjRjNTAxNWQwMTVkN2E5ZDhlZDI1ZTg4Y2ZkNTBjYmRiYTYx
|
15
|
+
MjU2N2Q5ZGQxNzVjZWViNDQ4YjM3ZjQ1OGZkMGY3YTk5N2RiNDk=
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Strategy to authenticate with Google via OAuth2 in OmniAuth.
|
4
4
|
|
5
|
-
Get your API key at: https://code.google.com/apis/console/
|
5
|
+
Get your API key at: https://code.google.com/apis/console/ Note the Client ID and the Client Secret.
|
6
6
|
|
7
7
|
For more details, read the Google docs: https://developers.google.com/accounts/docs/OAuth2
|
8
8
|
|
@@ -22,7 +22,7 @@ Here's an example for adding the middleware to a Rails app in `config/initialize
|
|
22
22
|
|
23
23
|
```ruby
|
24
24
|
Rails.application.config.middleware.use OmniAuth::Builder do
|
25
|
-
provider :google_oauth2, ENV["
|
25
|
+
provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_CLIENT_SECRET"]
|
26
26
|
end
|
27
27
|
```
|
28
28
|
|
@@ -30,6 +30,8 @@ You can now access the OmniAuth Google OAuth2 URL: `/auth/google_oauth2`
|
|
30
30
|
|
31
31
|
For more examples please check out `examples/omni_auth.rb`
|
32
32
|
|
33
|
+
NOTE: While developing your application, if you change the scope in the initializer you will need to restart your app server.
|
34
|
+
|
33
35
|
## Configuration
|
34
36
|
|
35
37
|
You can configure several options, which you pass in to the `provider` method via a hash:
|
@@ -56,11 +58,15 @@ You can configure several options, which you pass in to the `provider` method vi
|
|
56
58
|
|
57
59
|
* `access_type`: Defaults to `offline`, so a refresh token is sent to be used when the user is not present at the browser. Can be set to `online`. Note that if you need a refresh token, google requires you to also to specify the option `prompt: 'consent'`, which is not a default.
|
58
60
|
|
61
|
+
* `login_hint`: When your app knows which user it is trying to authenticate, it can provide this parameter as a hint to the authentication server. Passing this hint suppresses the account chooser and either pre-fill the email box on the sign-in form, or select the proper session (if the user is using multiple sign-in), which can help you avoid problems that occur if your app logs in the wrong user account. The value can be either an email address or the sub string, which is equivalent to the user's Google+ ID.
|
62
|
+
|
63
|
+
* `include_granted_scopes`: If this is provided with the value true, and the authorization request is granted, the authorization will include any previous authorizations granted to this user/application combination for other scopes. See Google's [Incremental Autorization](https://developers.google.com/accounts/docs/OAuth2WebServer#incrementalAuth) for additional details.
|
64
|
+
|
59
65
|
Here's an example of a possible configuration where the strategy name is changed, the user is asked for extra permissions, the user is always prompted to select his account when logging in and the user's profile picture is returned as a thumbnail:
|
60
66
|
|
61
67
|
```ruby
|
62
68
|
Rails.application.config.middleware.use OmniAuth::Builder do
|
63
|
-
provider :google_oauth2, ENV["
|
69
|
+
provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_CLIENT_SECRET"],
|
64
70
|
{
|
65
71
|
:name => "google",
|
66
72
|
:scope => "userinfo.email, userinfo.profile, plus.me, http://gdata.youtube.com",
|
@@ -150,6 +156,53 @@ end
|
|
150
156
|
```
|
151
157
|
Detailed example at https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview#google-oauth2-example
|
152
158
|
|
159
|
+
### One-time Code Flow (Hybrid Authentication)
|
160
|
+
|
161
|
+
Google describes the One-time Code Flow [here](https://developers.google.com/+/web/signin/server-side-flow). This hybrid authentication flow has significant functional and security advantages over a pure server-side or pure client-side flow. The following steps occur in this flow:
|
162
|
+
|
163
|
+
1. The client (web browser) authenticates the user directly via Google's JS API. During this process assorted modals may be rendered by Google.
|
164
|
+
2. On successful authentication, Google returns a one-time use code, which requires the Google client secret (which is only available server-side).
|
165
|
+
3. Using a AJAX request, the code is POSTed to the Omniauth Google OAuth2 callback.
|
166
|
+
4. The Omniauth Google OAuth2 gem will validate the code via a server-side request to Google. If the code is valid, then Google will return an access token and, if this is the first time this user is authenticating against this application, a refresh token. Both of these should be stored on the server. The response to the AJAX request indicates the success or failure of this process.
|
167
|
+
|
168
|
+
This flow is immune to replay attacks, and conveys no useful information to a man in the middle.
|
169
|
+
|
170
|
+
The omniauth-google-oauth2 gem supports this mode of operation out of the box. Implementors simply need to add the appropriate JavaScript to their web page, and they can take advantage of this flow. An example JavaScript snippet follows.
|
171
|
+
|
172
|
+
```javascript
|
173
|
+
jQuery(function() {
|
174
|
+
return $.ajax({
|
175
|
+
url: 'https://apis.google.com/js/client:plus.js?onload=gpAsyncInit',
|
176
|
+
dataType: 'script',
|
177
|
+
cache: true
|
178
|
+
});
|
179
|
+
});
|
180
|
+
|
181
|
+
window.gpAsyncInit = function() {
|
182
|
+
$('.googleplus-login').click(function(e) {
|
183
|
+
e.preventDefault();
|
184
|
+
gapi.auth.authorize({
|
185
|
+
immediate: true,
|
186
|
+
response_type: 'code',
|
187
|
+
cookie_policy: 'single_host_origin',
|
188
|
+
client_id: '000000000000.apps.googleusercontent.com',
|
189
|
+
scope: 'email profile'
|
190
|
+
}, function(response) {
|
191
|
+
if (response && !response.error) {
|
192
|
+
// google authentication succeed, now post data to server and handle data securely
|
193
|
+
jQuery.ajax({type: 'POST', url: "/auth/google_oauth2/callback", dataType: 'json', data: response,
|
194
|
+
success: function(json) {
|
195
|
+
// response from server
|
196
|
+
});
|
197
|
+
} else {
|
198
|
+
// google authentication failed
|
199
|
+
}
|
200
|
+
});
|
201
|
+
});
|
202
|
+
};
|
203
|
+
```
|
204
|
+
|
205
|
+
|
153
206
|
## Build Status
|
154
207
|
[![Build Status](https://travis-ci.org/zquestz/omniauth-google-oauth2.png)](https://travis-ci.org/zquestz/omniauth-google-oauth2)
|
155
208
|
|
data/examples/omni_auth.rb
CHANGED
@@ -4,9 +4,11 @@
|
|
4
4
|
|
5
5
|
Rails.application.config.middleware.use OmniAuth::Builder do
|
6
6
|
# Default usage, this will give you offline access and a refresh token
|
7
|
-
# using default scopes '
|
7
|
+
# using default scopes 'email' and 'profile'
|
8
8
|
#
|
9
|
-
provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], {
|
9
|
+
provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], {
|
10
|
+
:scope => 'email,profile'
|
11
|
+
}
|
10
12
|
|
11
13
|
# Manual setup for offline access with a refresh token.
|
12
14
|
# The prompt must be set to 'consent'
|
@@ -17,10 +19,10 @@ Rails.application.config.middleware.use OmniAuth::Builder do
|
|
17
19
|
# }
|
18
20
|
|
19
21
|
# Custom scope supporting youtube. If you are customizing scopes, remember
|
20
|
-
# to include the default scopes '
|
22
|
+
# to include the default scopes 'email' and 'profile'
|
21
23
|
#
|
22
24
|
# provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], {
|
23
|
-
# :scope => 'http://gdata.youtube.com,
|
25
|
+
# :scope => 'http://gdata.youtube.com,email,profile,plus.me'
|
24
26
|
# }
|
25
27
|
|
26
28
|
# Custom scope for users only using Google for account creation/auth and do not require a refresh token.
|
@@ -29,4 +31,11 @@ Rails.application.config.middleware.use OmniAuth::Builder do
|
|
29
31
|
# :access_type => 'online',
|
30
32
|
# :prompt => ''
|
31
33
|
# }
|
34
|
+
|
35
|
+
# To include information about people in your circles you must include the 'plus.login' scope.
|
36
|
+
#
|
37
|
+
# provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], {
|
38
|
+
# :skip_friends => false,
|
39
|
+
# :scope => "email,profile,plus.login"
|
40
|
+
# }
|
32
41
|
end
|
@@ -4,13 +4,14 @@ module OmniAuth
|
|
4
4
|
module Strategies
|
5
5
|
class GoogleOauth2 < OmniAuth::Strategies::OAuth2
|
6
6
|
BASE_SCOPE_URL = "https://www.googleapis.com/auth/"
|
7
|
-
|
7
|
+
BASE_SCOPES = %w[profile email openid]
|
8
|
+
DEFAULT_SCOPE = "email,profile"
|
8
9
|
|
9
10
|
option :name, 'google_oauth2'
|
10
11
|
|
11
12
|
option :skip_friends, true
|
12
13
|
|
13
|
-
option :authorize_options, [:access_type, :hd, :login_hint, :prompt, :request_visible_actions, :scope, :state, :redirect_uri]
|
14
|
+
option :authorize_options, [:access_type, :hd, :login_hint, :prompt, :request_visible_actions, :scope, :state, :redirect_uri, :include_granted_scopes]
|
14
15
|
|
15
16
|
option :client_options, {
|
16
17
|
:site => 'https://accounts.google.com',
|
@@ -26,7 +27,7 @@ module OmniAuth
|
|
26
27
|
|
27
28
|
raw_scope = params[:scope] || DEFAULT_SCOPE
|
28
29
|
scope_list = raw_scope.split(" ").map {|item| item.split(",")}.flatten
|
29
|
-
scope_list.map! { |s| s =~ /^https?:\/\// ? s : "#{BASE_SCOPE_URL}#{s}" }
|
30
|
+
scope_list.map! { |s| s =~ /^https?:\/\// || BASE_SCOPES.include?(s) ? s : "#{BASE_SCOPE_URL}#{s}" }
|
30
31
|
params[:scope] = scope_list.join(" ")
|
31
32
|
params[:access_type] = 'offline' if params[:access_type].nil?
|
32
33
|
|
@@ -34,7 +35,7 @@ module OmniAuth
|
|
34
35
|
end
|
35
36
|
end
|
36
37
|
|
37
|
-
uid { raw_info['
|
38
|
+
uid { raw_info['sub'] || verified_email }
|
38
39
|
|
39
40
|
info do
|
40
41
|
prune!({
|
@@ -42,9 +43,9 @@ module OmniAuth
|
|
42
43
|
:email => verified_email,
|
43
44
|
:first_name => raw_info['given_name'],
|
44
45
|
:last_name => raw_info['family_name'],
|
45
|
-
:image => image_url
|
46
|
+
:image => image_url,
|
46
47
|
:urls => {
|
47
|
-
'Google' => raw_info['
|
48
|
+
'Google' => raw_info['profile']
|
48
49
|
}
|
49
50
|
})
|
50
51
|
end
|
@@ -53,12 +54,12 @@ module OmniAuth
|
|
53
54
|
hash = {}
|
54
55
|
hash[:id_token] = access_token['id_token']
|
55
56
|
hash[:raw_info] = raw_info unless skip_info?
|
56
|
-
hash[:raw_friend_info] = raw_friend_info(raw_info['
|
57
|
+
hash[:raw_friend_info] = raw_friend_info(raw_info['sub']) unless skip_info? || options[:skip_friends]
|
57
58
|
prune! hash
|
58
59
|
end
|
59
60
|
|
60
61
|
def raw_info
|
61
|
-
@raw_info ||= access_token.get('https://www.googleapis.com/
|
62
|
+
@raw_info ||= access_token.get('https://www.googleapis.com/plus/v1/people/me/openIdConnect').parsed
|
62
63
|
end
|
63
64
|
|
64
65
|
def raw_friend_info(id)
|
@@ -66,7 +67,11 @@ module OmniAuth
|
|
66
67
|
end
|
67
68
|
|
68
69
|
def custom_build_access_token
|
69
|
-
if
|
70
|
+
if request.xhr? && request.params['code']
|
71
|
+
verifier = request.params['code']
|
72
|
+
client.auth_code.get_token(verifier, { :redirect_uri => 'postmessage'}.merge(token_params.to_hash(:symbolize_keys => true)),
|
73
|
+
deep_symbolize(options.auth_token_params || {}))
|
74
|
+
elsif verify_token(request.params['id_token'], request.params['access_token'])
|
70
75
|
::OAuth2::AccessToken.from_hash(client, request.params.dup)
|
71
76
|
else
|
72
77
|
orig_build_access_token
|
@@ -85,13 +90,25 @@ module OmniAuth
|
|
85
90
|
end
|
86
91
|
|
87
92
|
def verified_email
|
88
|
-
raw_info['
|
93
|
+
raw_info['email_verified'] ? raw_info['email'] : nil
|
89
94
|
end
|
90
95
|
|
91
|
-
def image_url
|
96
|
+
def image_url
|
92
97
|
original_url = raw_info['picture']
|
93
|
-
|
98
|
+
params_index = original_url.index('/photo.jpg') if original_url
|
94
99
|
|
100
|
+
if params_index && image_size_opts_passed?
|
101
|
+
original_url.insert(params_index, image_params)
|
102
|
+
else
|
103
|
+
original_url
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def image_size_opts_passed?
|
108
|
+
!!(options[:image_size] || options[:image_aspect_ratio])
|
109
|
+
end
|
110
|
+
|
111
|
+
def image_params
|
95
112
|
image_params = []
|
96
113
|
if options[:image_size].is_a?(Integer)
|
97
114
|
image_params << "s#{options[:image_size]}"
|
@@ -101,8 +118,7 @@ module OmniAuth
|
|
101
118
|
end
|
102
119
|
image_params << 'c' if options[:image_aspect_ratio] == 'square'
|
103
120
|
|
104
|
-
|
105
|
-
original_url.insert(params_index, ('/' + image_params.join('-')))
|
121
|
+
'/' + image_params.join('-')
|
106
122
|
end
|
107
123
|
|
108
124
|
def verify_token(id_token, access_token)
|
data/omniauth-contrib.gemspec
CHANGED
@@ -17,8 +17,8 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.require_paths = ["lib"]
|
18
18
|
gem.version = OmniAuth::GoogleOauth2::VERSION
|
19
19
|
|
20
|
-
gem.add_runtime_dependency 'omniauth-oauth2'
|
20
|
+
gem.add_runtime_dependency 'omniauth-oauth2', '~> 1.1'
|
21
21
|
|
22
|
-
gem.add_development_dependency 'rspec', '
|
22
|
+
gem.add_development_dependency 'rspec', '>= 2.14.0'
|
23
23
|
gem.add_development_dependency 'rake'
|
24
24
|
end
|
@@ -11,7 +11,7 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
11
11
|
|
12
12
|
subject do
|
13
13
|
OmniAuth::Strategies::GoogleOauth2.new(app, 'appid', 'secret', @options || {}).tap do |strategy|
|
14
|
-
strategy.
|
14
|
+
allow(strategy).to receive(:request) {
|
15
15
|
request
|
16
16
|
}
|
17
17
|
end
|
@@ -27,31 +27,31 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
27
27
|
|
28
28
|
describe '#client_options' do
|
29
29
|
it 'has correct site' do
|
30
|
-
subject.client.site.
|
30
|
+
expect(subject.client.site).to eq('https://accounts.google.com')
|
31
31
|
end
|
32
32
|
|
33
33
|
it 'has correct authorize_url' do
|
34
|
-
subject.client.options[:authorize_url].
|
34
|
+
expect(subject.client.options[:authorize_url]).to eq('/o/oauth2/auth')
|
35
35
|
end
|
36
36
|
|
37
37
|
it 'has correct token_url' do
|
38
|
-
subject.client.options[:token_url].
|
38
|
+
expect(subject.client.options[:token_url]).to eq('/o/oauth2/token')
|
39
39
|
end
|
40
40
|
|
41
41
|
describe "overrides" do
|
42
42
|
it 'should allow overriding the site' do
|
43
43
|
@options = {:client_options => {'site' => 'https://example.com'}}
|
44
|
-
subject.client.site.
|
44
|
+
expect(subject.client.site).to eq('https://example.com')
|
45
45
|
end
|
46
46
|
|
47
47
|
it 'should allow overriding the authorize_url' do
|
48
48
|
@options = {:client_options => {'authorize_url' => 'https://example.com'}}
|
49
|
-
subject.client.options[:authorize_url].
|
49
|
+
expect(subject.client.options[:authorize_url]).to eq('https://example.com')
|
50
50
|
end
|
51
51
|
|
52
52
|
it 'should allow overriding the token_url' do
|
53
53
|
@options = {:client_options => {'token_url' => 'https://example.com'}}
|
54
|
-
subject.client.options[:token_url].
|
54
|
+
expect(subject.client.options[:token_url]).to eq('https://example.com')
|
55
55
|
end
|
56
56
|
end
|
57
57
|
end
|
@@ -60,135 +60,146 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
60
60
|
[:access_type, :hd, :login_hint, :prompt, :scope, :state].each do |k|
|
61
61
|
it "should support #{k}" do
|
62
62
|
@options = {k => 'http://someval'}
|
63
|
-
subject.authorize_params[k.to_s].
|
63
|
+
expect(subject.authorize_params[k.to_s]).to eq('http://someval')
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
67
|
describe "redirect_uri" do
|
68
68
|
it 'should default to nil' do
|
69
69
|
@options = {}
|
70
|
-
subject.authorize_params['redirect_uri'].
|
70
|
+
expect(subject.authorize_params['redirect_uri']).to eq(nil)
|
71
71
|
end
|
72
72
|
|
73
73
|
it 'should set the redirect_uri parameter if present' do
|
74
74
|
@options = {:redirect_uri => 'https://example.com'}
|
75
|
-
subject.authorize_params['redirect_uri'].
|
75
|
+
expect(subject.authorize_params['redirect_uri']).to eq('https://example.com')
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
79
|
describe 'access_type' do
|
80
80
|
it 'should default to "offline"' do
|
81
81
|
@options = {}
|
82
|
-
subject.authorize_params['access_type'].
|
82
|
+
expect(subject.authorize_params['access_type']).to eq('offline')
|
83
83
|
end
|
84
84
|
|
85
85
|
it 'should set the access_type parameter if present' do
|
86
86
|
@options = {:access_type => 'online'}
|
87
|
-
subject.authorize_params['access_type'].
|
87
|
+
expect(subject.authorize_params['access_type']).to eq('online')
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
91
|
describe 'hd' do
|
92
92
|
it "should default to nil" do
|
93
|
-
subject.authorize_params['hd'].
|
93
|
+
expect(subject.authorize_params['hd']).to eq(nil)
|
94
94
|
end
|
95
95
|
|
96
96
|
it 'should set the hd (hosted domain) parameter if present' do
|
97
97
|
@options = {:hd => 'example.com'}
|
98
|
-
subject.authorize_params['hd'].
|
98
|
+
expect(subject.authorize_params['hd']).to eq('example.com')
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
102
|
describe 'login_hint' do
|
103
103
|
it "should default to nil" do
|
104
|
-
subject.authorize_params['login_hint'].
|
104
|
+
expect(subject.authorize_params['login_hint']).to eq(nil)
|
105
105
|
end
|
106
106
|
|
107
107
|
it 'should set the login_hint parameter if present' do
|
108
108
|
@options = {:login_hint => 'john@example.com'}
|
109
|
-
subject.authorize_params['login_hint'].
|
109
|
+
expect(subject.authorize_params['login_hint']).to eq('john@example.com')
|
110
110
|
end
|
111
111
|
end
|
112
112
|
|
113
113
|
describe 'prompt' do
|
114
114
|
it "should default to nil" do
|
115
|
-
subject.authorize_params['prompt'].
|
115
|
+
expect(subject.authorize_params['prompt']).to eq(nil)
|
116
116
|
end
|
117
117
|
|
118
118
|
it 'should set the prompt parameter if present' do
|
119
119
|
@options = {:prompt => 'consent select_account'}
|
120
|
-
subject.authorize_params['prompt'].
|
120
|
+
expect(subject.authorize_params['prompt']).to eq('consent select_account')
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
124
|
describe 'request_visible_actions' do
|
125
125
|
it "should default to nil" do
|
126
|
-
subject.authorize_params['request_visible_actions'].
|
126
|
+
expect(subject.authorize_params['request_visible_actions']).to eq(nil)
|
127
127
|
end
|
128
128
|
|
129
129
|
it 'should set the request_visible_actions parameter if present' do
|
130
130
|
@options = {:request_visible_actions => 'something'}
|
131
|
-
subject.authorize_params['request_visible_actions'].
|
131
|
+
expect(subject.authorize_params['request_visible_actions']).to eq('something')
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe 'include_granted_scopes' do
|
136
|
+
it 'should default to nil' do
|
137
|
+
expect(subject.authorize_params['include_granted_scopes']).to eq(nil)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'should set the include_granted_scopes parameter if present' do
|
141
|
+
@options = {:include_granted_scopes => 'true'}
|
142
|
+
expect(subject.authorize_params['include_granted_scopes']).to eq('true')
|
132
143
|
end
|
133
144
|
end
|
134
145
|
|
135
146
|
describe 'scope' do
|
136
147
|
it 'should expand scope shortcuts' do
|
137
|
-
@options = {:scope => '
|
138
|
-
subject.authorize_params['scope'].
|
148
|
+
@options = {:scope => 'plus.me'}
|
149
|
+
expect(subject.authorize_params['scope']).to eq('https://www.googleapis.com/auth/plus.me')
|
139
150
|
end
|
140
151
|
|
141
|
-
it 'should leave
|
142
|
-
@options = {:scope => '
|
143
|
-
subject.authorize_params['scope'].
|
152
|
+
it 'should leave base scopes as is' do
|
153
|
+
@options = {:scope => 'profile'}
|
154
|
+
expect(subject.authorize_params['scope']).to eq('profile')
|
144
155
|
end
|
145
156
|
|
146
157
|
it 'should join scopes' do
|
147
|
-
@options = {:scope => '
|
148
|
-
subject.authorize_params['scope'].
|
158
|
+
@options = {:scope => 'profile,email'}
|
159
|
+
expect(subject.authorize_params['scope']).to eq('profile email')
|
149
160
|
end
|
150
161
|
|
151
162
|
it 'should deal with whitespace when joining scopes' do
|
152
|
-
@options = {:scope => '
|
153
|
-
subject.authorize_params['scope'].
|
163
|
+
@options = {:scope => 'profile, email'}
|
164
|
+
expect(subject.authorize_params['scope']).to eq('profile email')
|
154
165
|
end
|
155
166
|
|
156
|
-
it 'should set default scope to
|
157
|
-
subject.authorize_params['scope'].
|
167
|
+
it 'should set default scope to email,profile' do
|
168
|
+
expect(subject.authorize_params['scope']).to eq('email profile')
|
158
169
|
end
|
159
170
|
|
160
171
|
it 'should support space delimited scopes' do
|
161
|
-
@options = {:scope => '
|
162
|
-
subject.authorize_params['scope'].
|
172
|
+
@options = {:scope => 'profile email'}
|
173
|
+
expect(subject.authorize_params['scope']).to eq('profile email')
|
163
174
|
end
|
164
175
|
|
165
176
|
it "should support extremely badly formed scopes" do
|
166
|
-
@options = {:scope => '
|
167
|
-
subject.authorize_params['scope'].
|
177
|
+
@options = {:scope => 'profile email,foo,steve yeah http://example.com'}
|
178
|
+
expect(subject.authorize_params['scope']).to eq('profile email https://www.googleapis.com/auth/foo https://www.googleapis.com/auth/steve https://www.googleapis.com/auth/yeah http://example.com')
|
168
179
|
end
|
169
180
|
end
|
170
181
|
|
171
182
|
describe 'state' do
|
172
183
|
it 'should set the state parameter' do
|
173
184
|
@options = {:state => 'some_state'}
|
174
|
-
subject.authorize_params['state'].
|
175
|
-
subject.session['omniauth.state'].
|
185
|
+
expect(subject.authorize_params['state']).to eq('some_state')
|
186
|
+
expect(subject.session['omniauth.state']).to eq('some_state')
|
176
187
|
end
|
177
188
|
|
178
189
|
it 'should set the omniauth.state dynamically' do
|
179
|
-
subject.
|
180
|
-
subject.authorize_params['state'].
|
181
|
-
subject.session['omniauth.state'].
|
190
|
+
allow(subject).to receive(:request) { double('Request', {:params => {'state' => 'some_state'}, :env => {}}) }
|
191
|
+
expect(subject.authorize_params['state']).to eq('some_state')
|
192
|
+
expect(subject.session['omniauth.state']).to eq('some_state')
|
182
193
|
end
|
183
194
|
end
|
184
195
|
|
185
196
|
describe "overrides" do
|
186
197
|
it 'should include top-level options that are marked as :authorize_options' do
|
187
198
|
@options = {:authorize_options => [:scope, :foo, :request_visible_actions], :scope => 'http://bar', :foo => 'baz', :hd => "wow", :request_visible_actions => "something"}
|
188
|
-
subject.authorize_params['scope'].
|
189
|
-
subject.authorize_params['foo'].
|
190
|
-
subject.authorize_params['hd'].
|
191
|
-
subject.authorize_params['request_visible_actions'].
|
199
|
+
expect(subject.authorize_params['scope']).to eq('http://bar')
|
200
|
+
expect(subject.authorize_params['foo']).to eq('baz')
|
201
|
+
expect(subject.authorize_params['hd']).to eq(nil)
|
202
|
+
expect(subject.authorize_params['request_visible_actions']).to eq('something')
|
192
203
|
end
|
193
204
|
|
194
205
|
describe "request overrides" do
|
@@ -198,7 +209,7 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
198
209
|
|
199
210
|
it "should set the #{k} authorize option dynamically in the request" do
|
200
211
|
@options = {k => ''}
|
201
|
-
subject.authorize_params[k.to_s].
|
212
|
+
expect(subject.authorize_params[k.to_s]).to eq('http://example.com')
|
202
213
|
end
|
203
214
|
end
|
204
215
|
end
|
@@ -208,7 +219,7 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
208
219
|
|
209
220
|
it "should support request overrides from custom authorize_options" do
|
210
221
|
@options = {:authorize_options => [:foo], :foo => ''}
|
211
|
-
subject.authorize_params['foo'].
|
222
|
+
expect(subject.authorize_params['foo']).to eq('something')
|
212
223
|
end
|
213
224
|
end
|
214
225
|
end
|
@@ -218,34 +229,34 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
218
229
|
describe '#authorize_params' do
|
219
230
|
it 'should include any authorize params passed in the :authorize_params option' do
|
220
231
|
@options = {:authorize_params => {:request_visible_actions => 'something', :foo => 'bar', :baz => 'zip'}, :hd => 'wow', :bad => 'not_included'}
|
221
|
-
subject.authorize_params['request_visible_actions'].
|
222
|
-
subject.authorize_params['foo'].
|
223
|
-
subject.authorize_params['baz'].
|
224
|
-
subject.authorize_params['hd'].
|
225
|
-
subject.authorize_params['bad'].
|
232
|
+
expect(subject.authorize_params['request_visible_actions']).to eq('something')
|
233
|
+
expect(subject.authorize_params['foo']).to eq('bar')
|
234
|
+
expect(subject.authorize_params['baz']).to eq('zip')
|
235
|
+
expect(subject.authorize_params['hd']).to eq('wow')
|
236
|
+
expect(subject.authorize_params['bad']).to eq(nil)
|
226
237
|
end
|
227
238
|
end
|
228
239
|
|
229
240
|
describe '#token_params' do
|
230
241
|
it 'should include any token params passed in the :token_params option' do
|
231
242
|
@options = {:token_params => {:foo => 'bar', :baz => 'zip'}}
|
232
|
-
subject.token_params['foo'].
|
233
|
-
subject.token_params['baz'].
|
243
|
+
expect(subject.token_params['foo']).to eq('bar')
|
244
|
+
expect(subject.token_params['baz']).to eq('zip')
|
234
245
|
end
|
235
246
|
end
|
236
247
|
|
237
248
|
describe "#token_options" do
|
238
249
|
it 'should include top-level options that are marked as :token_options' do
|
239
250
|
@options = {:token_options => [:scope, :foo], :scope => 'bar', :foo => 'baz', :bad => 'not_included'}
|
240
|
-
subject.token_params['scope'].
|
241
|
-
subject.token_params['foo'].
|
242
|
-
subject.token_params['bad'].
|
251
|
+
expect(subject.token_params['scope']).to eq('bar')
|
252
|
+
expect(subject.token_params['foo']).to eq('baz')
|
253
|
+
expect(subject.token_params['bad']).to eq(nil)
|
243
254
|
end
|
244
255
|
end
|
245
256
|
|
246
257
|
describe '#callback_path' do
|
247
258
|
it 'has the correct callback path' do
|
248
|
-
subject.callback_path.
|
259
|
+
expect(subject.callback_path).to eq('/auth/google_oauth2/callback')
|
249
260
|
end
|
250
261
|
end
|
251
262
|
|
@@ -254,7 +265,7 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
254
265
|
OAuth2::Client.new('abc', 'def') do |builder|
|
255
266
|
builder.request :url_encoded
|
256
267
|
builder.adapter :test do |stub|
|
257
|
-
stub.get('/
|
268
|
+
stub.get('/plus/v1/people/me/openIdConnect') {|env| [200, {'content-type' => 'application/json'}, '{"sub": "12345"}']}
|
258
269
|
stub.get('/plus/v1/people/12345/people/visible') {|env| [200, {'content-type' => 'application/json'}, '[{"foo":"bar"}]']}
|
259
270
|
end
|
260
271
|
end
|
@@ -268,13 +279,13 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
268
279
|
let(:access_token) { OAuth2::AccessToken.from_hash(client, {'id_token' => 'xyz'}) }
|
269
280
|
|
270
281
|
it 'should include id_token when set on the access_token' do
|
271
|
-
subject.extra.
|
282
|
+
expect(subject.extra).to include(:id_token => 'xyz')
|
272
283
|
end
|
273
284
|
end
|
274
285
|
|
275
286
|
context 'when the id_token is missing' do
|
276
287
|
it 'should not include id_token' do
|
277
|
-
subject.extra.
|
288
|
+
expect(subject.extra).not_to have_key(:id_token)
|
278
289
|
end
|
279
290
|
end
|
280
291
|
end
|
@@ -284,7 +295,7 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
284
295
|
before { subject.options[:skip_info] = true }
|
285
296
|
|
286
297
|
it 'should not include raw_info' do
|
287
|
-
subject.extra.
|
298
|
+
expect(subject.extra).not_to have_key(:raw_info)
|
288
299
|
end
|
289
300
|
end
|
290
301
|
|
@@ -292,7 +303,7 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
292
303
|
before { subject.options[:skip_info] = false }
|
293
304
|
|
294
305
|
it 'should include raw_info' do
|
295
|
-
subject.extra[:raw_info].
|
306
|
+
expect(subject.extra[:raw_info]).to eq('sub' => '12345')
|
296
307
|
end
|
297
308
|
end
|
298
309
|
end
|
@@ -302,7 +313,7 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
302
313
|
before { subject.options[:skip_info] = true }
|
303
314
|
|
304
315
|
it 'should not include raw_friend_info' do
|
305
|
-
subject.extra.
|
316
|
+
expect(subject.extra).not_to have_key(:raw_friend_info)
|
306
317
|
end
|
307
318
|
end
|
308
319
|
|
@@ -313,7 +324,7 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
313
324
|
before { subject.options[:skip_friends] = true }
|
314
325
|
|
315
326
|
it 'should not include raw_friend_info' do
|
316
|
-
subject.extra.
|
327
|
+
expect(subject.extra).not_to have_key(:raw_friend_info)
|
317
328
|
end
|
318
329
|
end
|
319
330
|
|
@@ -321,7 +332,7 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
321
332
|
before { subject.options[:skip_friends] = false }
|
322
333
|
|
323
334
|
it 'should not include raw_friend_info' do
|
324
|
-
subject.extra[:raw_friend_info].
|
335
|
+
expect(subject.extra[:raw_friend_info]).to eq([{'foo' => 'bar'}])
|
325
336
|
end
|
326
337
|
end
|
327
338
|
end
|
@@ -330,75 +341,98 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
330
341
|
|
331
342
|
describe 'populate auth hash urls' do
|
332
343
|
it 'should populate url map in auth hash if link present in raw_info' do
|
333
|
-
subject.
|
334
|
-
subject.info[:urls]['Google'].
|
344
|
+
allow(subject).to receive(:raw_info) { {'name' => 'Foo', 'profile' => 'https://plus.google.com/123456'} }
|
345
|
+
expect(subject.info[:urls]['Google']).to eq('https://plus.google.com/123456')
|
335
346
|
end
|
336
347
|
|
337
348
|
it 'should not populate url map in auth hash if no link present in raw_info' do
|
338
|
-
subject.
|
339
|
-
subject.info.
|
349
|
+
allow(subject).to receive(:raw_info) { {'name' => 'Foo'} }
|
350
|
+
expect(subject.info).not_to have_key(:urls)
|
340
351
|
end
|
341
352
|
end
|
342
353
|
|
343
354
|
describe 'image options' do
|
344
355
|
it "should have no image if a picture isn't present" do
|
345
356
|
@options = {:image_aspect_ratio => 'square'}
|
346
|
-
subject.
|
347
|
-
subject.info[:image].
|
357
|
+
allow(subject).to receive(:raw_info) { {'name' => 'User Without Pic'} }
|
358
|
+
expect(subject.info[:image]).to be_nil
|
348
359
|
end
|
349
360
|
|
350
361
|
describe "when a picture is returned from google" do
|
351
362
|
it 'should return the image with size specified in the `image_size` option' do
|
352
363
|
@options = {:image_size => 50}
|
353
|
-
subject.
|
354
|
-
subject.info[:image].
|
364
|
+
allow(subject).to receive(:raw_info) { {'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg'} }
|
365
|
+
expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/s50/photo.jpg')
|
355
366
|
end
|
356
367
|
|
357
368
|
it 'should return the image with width and height specified in the `image_size` option' do
|
358
369
|
@options = {:image_size => {:width => 50, :height => 40}}
|
359
|
-
subject.
|
360
|
-
subject.info[:image].
|
370
|
+
allow(subject).to receive(:raw_info) { {'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg'} }
|
371
|
+
expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/w50-h40/photo.jpg')
|
361
372
|
end
|
362
373
|
|
363
374
|
it 'should return square image when `image_aspect_ratio` is specified' do
|
364
375
|
@options = {:image_aspect_ratio => 'square'}
|
365
|
-
subject.
|
366
|
-
subject.info[:image].
|
376
|
+
allow(subject).to receive(:raw_info) { {'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg'} }
|
377
|
+
expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/c/photo.jpg')
|
367
378
|
end
|
368
379
|
|
369
380
|
it 'should return square sized image when `image_aspect_ratio` and `image_size` is set' do
|
370
381
|
@options = {:image_aspect_ratio => 'square', :image_size => 50}
|
371
|
-
subject.
|
372
|
-
subject.info[:image].
|
382
|
+
allow(subject).to receive(:raw_info) { {'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg'} }
|
383
|
+
expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/s50-c/photo.jpg')
|
373
384
|
end
|
374
385
|
|
375
386
|
it 'should return square sized image when `image_aspect_ratio` and `image_size` has height and width' do
|
376
387
|
@options = {:image_aspect_ratio => 'square', :image_size => {:width => 50, :height => 40}}
|
377
|
-
subject.
|
378
|
-
subject.info[:image].
|
388
|
+
allow(subject).to receive(:raw_info) { {'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg'} }
|
389
|
+
expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/w50-h40-c/photo.jpg')
|
390
|
+
end
|
391
|
+
|
392
|
+
it 'should return original image if image url does not end in `photo.jpg`' do
|
393
|
+
@options = {:image_size => 50}
|
394
|
+
subject.stub(:raw_info) { {'picture' => 'https://lh3.googleusercontent.com/url/photograph.jpg'} }
|
395
|
+
subject.info[:image].should eq('https://lh3.googleusercontent.com/url/photograph.jpg')
|
379
396
|
end
|
380
397
|
end
|
381
398
|
|
382
399
|
it 'should return original image if no options are provided' do
|
383
|
-
subject.
|
384
|
-
subject.info[:image].
|
400
|
+
allow(subject).to receive(:raw_info) { {'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg'} }
|
401
|
+
expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/photo.jpg')
|
385
402
|
end
|
386
403
|
end
|
387
404
|
|
388
405
|
describe 'build_access_token' do
|
389
|
-
it 'should
|
390
|
-
request.
|
391
|
-
|
392
|
-
|
406
|
+
it 'should use a hybrid authorization request_uri if this is an AJAX request with a code parameter' do
|
407
|
+
allow(request).to receive(:xhr?).and_return(true)
|
408
|
+
allow(request).to receive(:params).and_return('code' => 'valid_code')
|
409
|
+
|
410
|
+
client = double(:client)
|
411
|
+
auth_code = double(:auth_code)
|
412
|
+
allow(client).to receive(:auth_code).and_return(auth_code)
|
413
|
+
expect(subject).to receive(:client).and_return(client)
|
414
|
+
expect(auth_code).to receive(:get_token).with('valid_code', { :redirect_uri => 'postmessage'}, {})
|
415
|
+
|
416
|
+
expect(subject).not_to receive(:orig_build_access_token)
|
417
|
+
subject.build_access_token
|
418
|
+
end
|
419
|
+
|
420
|
+
it 'should read access_token from hash if this is not an AJAX request with a code parameter' do
|
421
|
+
allow(request).to receive(:xhr?).and_return(false)
|
422
|
+
allow(request).to receive(:params).and_return('id_token' => 'valid_id_token', 'access_token' => 'valid_access_token')
|
423
|
+
expect(subject).to receive(:verify_token).with('valid_id_token', 'valid_access_token').and_return true
|
424
|
+
expect(subject).to receive(:client).and_return(:client)
|
393
425
|
|
394
426
|
token = subject.build_access_token
|
395
|
-
token.
|
396
|
-
token.token.
|
397
|
-
token.client.
|
427
|
+
expect(token).to be_instance_of(::OAuth2::AccessToken)
|
428
|
+
expect(token.token).to eq('valid_access_token')
|
429
|
+
expect(token.client).to eq(:client)
|
398
430
|
end
|
399
431
|
|
400
|
-
it 'should call super' do
|
401
|
-
|
432
|
+
it 'should call super if this is not an AJAX request' do
|
433
|
+
allow(request).to receive(:xhr?).and_return(false)
|
434
|
+
allow(request).to receive(:params).and_return('code' => 'valid_code')
|
435
|
+
expect(subject).to receive(:orig_build_access_token)
|
402
436
|
subject.build_access_token
|
403
437
|
end
|
404
438
|
end
|
@@ -413,7 +447,7 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
413
447
|
:issued_to => '000000000000.apps.googleusercontent.com',
|
414
448
|
:audience => '000000000000.apps.googleusercontent.com',
|
415
449
|
:user_id => '000000000000000000000',
|
416
|
-
:scope => '
|
450
|
+
:scope => 'profile email',
|
417
451
|
:expires_in => 3514,
|
418
452
|
:email => 'me@example.com',
|
419
453
|
:verified_email => true,
|
@@ -429,11 +463,11 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
429
463
|
|
430
464
|
it 'should verify token if access_token and id_token are valid and app_id equals' do
|
431
465
|
subject.options.client_id = '000000000000.apps.googleusercontent.com'
|
432
|
-
subject.send(:verify_token, 'valid_id_token', 'valid_access_token').
|
466
|
+
expect(subject.send(:verify_token, 'valid_id_token', 'valid_access_token')).to eq(true)
|
433
467
|
end
|
434
468
|
|
435
469
|
it 'should not verify token if access_token and id_token are valid but app_id is false' do
|
436
|
-
subject.send(:verify_token, 'valid_id_token', 'valid_access_token').
|
470
|
+
expect(subject.send(:verify_token, 'valid_id_token', 'valid_access_token')).to eq(false)
|
437
471
|
end
|
438
472
|
|
439
473
|
it 'should raise error if access_token or id_token is invalid' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: omniauth-google-oauth2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josh Ellithorpe
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2014-04-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: omniauth
|
@@ -29,30 +29,30 @@ dependencies:
|
|
29
29
|
name: omniauth-oauth2
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- -
|
32
|
+
- - ~>
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '
|
34
|
+
version: '1.1'
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- -
|
39
|
+
- - ~>
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: '
|
41
|
+
version: '1.1'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: rspec
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- -
|
46
|
+
- - ! '>='
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: 2.
|
48
|
+
version: 2.14.0
|
49
49
|
type: :development
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- -
|
53
|
+
- - ! '>='
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: 2.
|
55
|
+
version: 2.14.0
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: rake
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -75,9 +75,6 @@ extensions: []
|
|
75
75
|
extra_rdoc_files: []
|
76
76
|
files:
|
77
77
|
- .gitignore
|
78
|
-
- .ruby-gemset
|
79
|
-
- .ruby-version
|
80
|
-
- .rvmrc
|
81
78
|
- .travis.yml
|
82
79
|
- Gemfile
|
83
80
|
- README.md
|
data/.ruby-gemset
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
omniauth-google-oauth2
|
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
1.9.3-p484
|
data/.rvmrc
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
rvm use 1.9.3@omniauth-google-oauth2 --create
|