omniauth-google-oauth2 0.1.13 → 0.1.15

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,8 +1,94 @@
1
1
  # OmniAuth Google OAuth2 Strategy
2
2
 
3
- Strategy to auth with Google via OAuth2 in OmniAuth.
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/
6
+
7
+ For more details, read the Google docs: https://developers.google.com/accounts/docs/OAuth2
8
+
9
+ ## Installation
10
+
11
+ Add to your `Gemfile`:
12
+
13
+ ```ruby
14
+ gem "omniauth-google-oauth2"
15
+ ```
16
+
17
+ Then `bundle install`.
18
+
19
+ ## Usage
20
+
21
+ Here's an example, adding the middleware to a Rails app in `config/initializers/omniauth.rb`:
22
+
23
+ ```ruby
24
+ Rails.application.config.middleware.use OmniAuth::Builder do
25
+ provider :google_oauth2, ENV["GOOGLE_KEY"], ENV["GOOGLE_SECRET"]
26
+ end
27
+ ```
28
+
29
+ You can now access the OmniAuth Google OAuth2 URL: `/auth/google_oauth2`
30
+
31
+ ## Configuration
32
+
33
+ You can configure several options, which you pass in to the `provider` method via a hash:
34
+
35
+ * `scope`: A comma-separated list, without spaces, of permissions you want to request from the user. See the [Google OAuth 2.0 Playground](https://developers.google.com/oauthplayground/) for a full list of available permissions. Caveats:
36
+ * The `userinfo.email` and `userinfo.profile` scopes are used by default. By defining your own `scope`, you override these defaults. If you need these scopes, don't forget to add them yourself!
37
+ * Scopes starting with `https://www.googleapis.com/auth/` do not need that prefix specified. So while you should use the smaller scope `books` since that permission starts with the mentioned prefix, you should use the full scope URL `https://docs.google.com/feeds/` to access a user's docs, for example.
38
+ * `approval_prompt`: Determines whether the user is always re-prompted for consent. It's set to `force` by default so a user sees a consent page even if he has previously allowed access a given set of scopes. Set this value to `auto` so that the user only sees the consent page the first time he authorizes a given set of scopes.
39
+ * `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`.
40
+
41
+ Here's an example of a possible configuration where the user is asked for extra permissions and is only prompted once for such permissions:
42
+
43
+ ```ruby
44
+ Rails.application.config.middleware.use OmniAuth::Builder do
45
+ provider :google_oauth2, ENV["GOOGLE_KEY"], ENV["GOOGLE_SECRET"],
46
+ {
47
+ :scope => "userinfo.email,userinfo.profile,plus.me,http://gdata.youtube.com",
48
+ :approval_prompt => "auto"
49
+ }
50
+ end
51
+ ```
52
+
53
+ ## Auth Hash
54
+
55
+ Here's an example of an authentication hash available in the callback by accessing `request.env["omniauth.auth"]`:
56
+
57
+ ```ruby
58
+ {
59
+ :provider => "google_oauth2",
60
+ :uid => "123456789",
61
+ :info => {
62
+ :name => "John Doe",
63
+ :email => "john@company_name.com",
64
+ :first_name => "John",
65
+ :last_name => "Doe",
66
+ :image => "https://lh3.googleusercontent.com/url/photo.jpg"
67
+ },
68
+ :credentials => {
69
+ :token => "token",
70
+ :refresh_token => "another_token",
71
+ :expires_at => 1354920555,
72
+ :expires => true
73
+ },
74
+ :extra => {
75
+ :raw_info => {
76
+ :id => "123456789",
77
+ :email => "user@domain.example.com",
78
+ :verified_email => true,
79
+ :name => "John Doe",
80
+ :given_name => "John",
81
+ :family_name => "Doe",
82
+ :link => "https://plus.google.com/123456789",
83
+ :picture => "https://lh3.googleusercontent.com/url/photo.jpg",
84
+ :gender => "male",
85
+ :birthday => "0000-06-25",
86
+ :locale => "en",
87
+ :hd => "company_name.com"
88
+ }
89
+ }
90
+ }
91
+ ```
6
92
 
7
93
  ## License
8
94
 
@@ -12,4 +98,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
12
98
 
13
99
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14
100
 
15
- 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.
101
+ 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.
data/examples/config.ru CHANGED
@@ -8,6 +8,8 @@ require 'sinatra'
8
8
  require 'omniauth'
9
9
  require 'omniauth-google-oauth2'
10
10
 
11
+ # Do not use for production code.
12
+ # This is only to make setup easier when running through the sample.
11
13
  OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
12
14
 
13
15
  class App < Sinatra::Base
@@ -1,5 +1,5 @@
1
1
  module OmniAuth
2
2
  module GoogleOauth2
3
- VERSION = "0.1.13"
3
+ VERSION = "0.1.15"
4
4
  end
5
5
  end
@@ -20,8 +20,8 @@ module OmniAuth
20
20
  base_scope_url = "https://www.googleapis.com/auth/"
21
21
  super.tap do |params|
22
22
  # Read the params if passed directly to omniauth_authorize_path
23
- %w(scope approval_prompt access_type state hd).each do |k|
24
- params[k.to_sym] = request.params[k] unless [nil, ''].include?(request.params[k])
23
+ options[:authorize_options].each do |k|
24
+ params[k] = request.params[k.to_s] unless [nil, ''].include?(request.params[k.to_s])
25
25
  end
26
26
  scopes = (params[:scope] || DEFAULT_SCOPE).split(",")
27
27
  scopes.map! { |s| s =~ /^https?:\/\// ? s : "#{base_scope_url}#{s}" }
@@ -43,7 +43,10 @@ module OmniAuth
43
43
  :email => verified_email,
44
44
  :first_name => raw_info['given_name'],
45
45
  :last_name => raw_info['family_name'],
46
- :image => raw_info['picture']
46
+ :image => raw_info['picture'],
47
+ :urls => {
48
+ 'Google' => raw_info['link']
49
+ }
47
50
  })
48
51
  end
49
52
 
@@ -57,11 +60,19 @@ module OmniAuth
57
60
  @raw_info ||= access_token.get('https://www.googleapis.com/oauth2/v1/userinfo').parsed
58
61
  end
59
62
 
60
- private
61
-
62
- def request_has_approval_prompt
63
- request.env && request.params && request.params['approval_prompt']
63
+ def build_access_token_with_access_token
64
+ if !request.params['id_token'].nil? &&
65
+ !request.params['access_token'].nil? &&
66
+ verify_token(request.params['id_token'], request.params['access_token'])
67
+ ::OAuth2::AccessToken.from_hash(client, request.params.dup)
68
+ else
69
+ build_access_token_without_access_token
70
+ end
64
71
  end
72
+ alias_method :build_access_token_without_access_token, :build_access_token
73
+ alias :build_access_token :build_access_token_with_access_token
74
+
75
+ private
65
76
 
66
77
  def prune!(hash)
67
78
  hash.delete_if do |_, value|
@@ -74,6 +85,14 @@ module OmniAuth
74
85
  raw_info['verified_email'] ? raw_info['email'] : nil
75
86
  end
76
87
 
88
+ def verify_token(id_token, access_token)
89
+ # Verify id_token as well
90
+ # request fails and raises error when id_token or access_token is invalid
91
+ raw_response = client.request(:get, 'https://www.googleapis.com/oauth2/v2/tokeninfo',
92
+ params: {id_token: id_token, access_token: access_token}).parsed
93
+ raw_response['issued_to'] == options.client_id
94
+ end
95
+
77
96
  end
78
97
  end
79
98
  end
@@ -46,9 +46,9 @@ describe OmniAuth::Strategies::GoogleOauth2 do
46
46
  end
47
47
 
48
48
  describe '#authorize_params' do
49
- %w(approval_prompt access_type state hd).each do |k|
49
+ %w(approval_prompt access_type state hd any_other).each do |k|
50
50
  it "should set the #{k} authorize option dynamically in the request" do
51
- @options = {k.to_sym => ''}
51
+ @options = {:authorize_options => [k.to_sym], k.to_sym => ''}
52
52
  subject.stub(:request) { double('Request', {:params => { k => 'something' }, :env => {}}) }
53
53
  subject.authorize_params[k].should eq('something')
54
54
  end
@@ -139,4 +139,74 @@ describe OmniAuth::Strategies::GoogleOauth2 do
139
139
  subject.extra.should_not have_key(:raw_info)
140
140
  end
141
141
  end
142
+
143
+ describe 'populate auth hash url' do
144
+ it 'should populate url map in auth hash if link present in raw_info' do
145
+ subject.stub(:raw_info) { { 'name' => 'Foo', 'link' => 'https://plus.google.com/123456' } }
146
+ subject.info[:urls]['Google'].should eq('https://plus.google.com/123456')
147
+ end
148
+
149
+ it 'should not populate url map in auth hash if no link present in raw_info' do
150
+ subject.stub(:raw_info) { { 'name' => 'Foo' } }
151
+ subject.info.should_not have_key(:urls)
152
+ end
153
+ end
154
+
155
+ describe 'build_access_token' do
156
+ it 'should read access_token from hash' do
157
+ @request.stub(:params).and_return('id_token' => 'valid_id_token', 'access_token' => 'valid_access_token')
158
+ subject.should_receive(:verify_token).with('valid_id_token', 'valid_access_token').and_return true
159
+ subject.should_receive(:client).and_return(:client)
160
+
161
+ token = subject.build_access_token
162
+ token.should be_instance_of(::OAuth2::AccessToken)
163
+ token.token.should eq('valid_access_token')
164
+ token.client.should eq(:client)
165
+ end
166
+
167
+ it 'should call super' do
168
+ subject.should_receive(:build_access_token_without_access_token)
169
+ subject.build_access_token
170
+ end
171
+ end
172
+
173
+ describe 'verify_token' do
174
+ before(:each) do
175
+ subject.options.client_options[:connection_build] = proc do |builder|
176
+ builder.request :url_encoded
177
+ builder.adapter :test do |stub|
178
+ stub.get('/oauth2/v2/tokeninfo?id_token=valid_id_token&access_token=valid_access_token') do |env|
179
+ [200, {'Content-Type' => 'application/json; charset=UTF-8'}, MultiJson.encode(
180
+ :issued_to => '000000000000.apps.googleusercontent.com',
181
+ :audience => '000000000000.apps.googleusercontent.com',
182
+ :user_id => '000000000000000000000',
183
+ :scope => 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email',
184
+ :expires_in => 3514,
185
+ :email => 'me@example.com',
186
+ :verified_email => true,
187
+ :access_type => 'online'
188
+ )]
189
+ end
190
+ stub.get('/oauth2/v2/tokeninfo?id_token=invalid_id_token&access_token=invalid_access_token') do |env|
191
+ [400, {'Content-Type' => 'application/json; charset=UTF-8'}, MultiJson.encode(:error_description => 'Invalid Value')]
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ it 'should verify token if access_token and id_token are valid and app_id equals' do
198
+ subject.options.client_id = '000000000000.apps.googleusercontent.com'
199
+ subject.send(:verify_token, 'valid_id_token', 'valid_access_token').should == true
200
+ end
201
+
202
+ it 'should not verify token if access_token and id_token are valid but app_id is false' do
203
+ subject.send(:verify_token, 'valid_id_token', 'valid_access_token').should == false
204
+ end
205
+
206
+ it 'should raise error if access_token or id_token is invalid' do
207
+ expect {
208
+ subject.send(:verify_token, 'invalid_id_token', 'invalid_access_token')
209
+ }.to raise_error(OAuth2::Error)
210
+ end
211
+ end
142
212
  end
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.1.13
4
+ version: 0.1.15
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-07-15 00:00:00.000000000 Z
13
+ date: 2013-04-18 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: omniauth
@@ -112,7 +112,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
112
112
  version: '0'
113
113
  segments:
114
114
  - 0
115
- hash: 1263356288876244939
115
+ hash: -4040101131185532486
116
116
  required_rubygems_version: !ruby/object:Gem::Requirement
117
117
  none: false
118
118
  requirements:
@@ -121,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
121
  version: '0'
122
122
  segments:
123
123
  - 0
124
- hash: 1263356288876244939
124
+ hash: -4040101131185532486
125
125
  requirements: []
126
126
  rubyforge_project:
127
127
  rubygems_version: 1.8.24