omniauth-google-oauth2 0.6.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 81997c7fd03317eea408408d6f18159972b8f3dc
4
- data.tar.gz: d16e88cdee0f4fd599d4b8d8c05ac40ebc761c07
2
+ SHA256:
3
+ metadata.gz: '0532842d8362fc36a8797376dc54e09b1d11fa178d462114225eecbe87274785'
4
+ data.tar.gz: 63b1d0a5a3a6249b77b58cbfa4e696ae18748b59280cb73f36cb98f88abd98c1
5
5
  SHA512:
6
- metadata.gz: eb2b1d82471d000a983728b92676e682d7006b0a3551bcb109cd9216a3114e6d78500bdd3d29ae71b55c95f4b7a2315f4d3930bf017edc7a2506641bb51cfc06
7
- data.tar.gz: 27605544db858360570a109a922689ec100bd70ff4ea63318a67f2ba6bc5a91c1f464e9110088addba9c83fc62a6fcae13ce54f22f53abcaf5933d04cb1538ff
6
+ metadata.gz: 6d6f34a8629057f1f89d613155567d0bb50917648794e64527976ba7ca4c372ed8806fd57bbe5cec55d2d60545ef2e9bd038da80e1101b96642018f1d8226951
7
+ data.tar.gz: b00da9be3e3f97af0cdfe9a15be3d99126176a26bc21337febb893be88949da0573b4f6a843fbedac3dc982a4fda29ca532b63d3a1272fc8544468b3a42d32aa
@@ -0,0 +1,21 @@
1
+ name: CI
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ ruby-version: ['2.3', '2.4', '2.5', '2.6', '2.7', '3.0', '3.1']
11
+
12
+ steps:
13
+ - uses: actions/checkout@v2
14
+ - name: Set up Ruby ${{ matrix.ruby-version }}
15
+ uses: ruby/setup-ruby@v1
16
+ with:
17
+ ruby-version: ${{ matrix.ruby-version }}
18
+ bundler-cache: true # 'bundle install' and cache
19
+ - name: Run specs
20
+ run: |
21
+ bundle exec rake
data/.rubocop.yml CHANGED
@@ -1,11 +1,9 @@
1
- ClassLength:
2
- Enabled: false
3
- Layout/IndentHeredoc:
1
+ Metrics/ClassLength:
4
2
  Enabled: false
5
3
  Metrics/AbcSize:
6
4
  Enabled: false
7
5
  Metrics/BlockLength:
8
- ExcludedMethods: ['describe', 'context']
6
+ ExcludedMethods: ['describe', 'context', 'shared_examples']
9
7
  Metrics/CyclomaticComplexity:
10
8
  Enabled: false
11
9
  Metrics/LineLength:
@@ -20,4 +18,3 @@ Style/MutableConstant:
20
18
  Enabled: false
21
19
  Gemspec/RequiredRubyVersion:
22
20
  Enabled: false
23
-
data/.travis.yml CHANGED
@@ -1,7 +1,9 @@
1
1
  language: ruby
2
+ cache: bundler
2
3
  rvm:
3
- - '2.1.10'
4
- - '2.2.7'
5
- - '2.3.4'
6
- - '2.4.1'
7
- - '2.5.0'
4
+ - '2.3.8'
5
+ - '2.4.10'
6
+ - '2.5.8'
7
+ - '2.6.6'
8
+ - '2.7.2'
9
+ - '3.0.0'
data/CHANGELOG.md CHANGED
@@ -1,6 +1,135 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
+ ## 1.1.1 - 2022-09-05
5
+
6
+ ### Added
7
+ - Nothing.
8
+
9
+ ### Deprecated
10
+ - Nothing.
11
+
12
+ ### Removed
13
+ - Nothing.
14
+
15
+ ### Fixed
16
+ - Fixed JWT decoding issue. (Invalid segment encoding) [#431](https://github.com/zquestz/omniauth-google-oauth2/pull/431)
17
+
18
+ ## 1.1.0 - 2022-09-03
19
+
20
+ ### Added
21
+ - `overridable_authorize_options` has been added to restrict overriding authorize_options by request params. [#423](https://github.com/zquestz/omniauth-google-oauth2/pull/423)
22
+ - Support for oauth2 2.0.x. [#429](https://github.com/zquestz/omniauth-google-oauth2/pull/429)
23
+
24
+ ### Deprecated
25
+ - Nothing.
26
+
27
+ ### Removed
28
+ - Nothing.
29
+
30
+ ### Fixed
31
+ - Nothing.
32
+
33
+ ## 1.0.1 - 2022-03-10
34
+
35
+ ### Added
36
+ - Output granted scopes in credentials block of the auth hash.
37
+ - Migrated to GitHub actions.
38
+
39
+ ### Deprecated
40
+ - Nothing.
41
+
42
+ ### Removed
43
+ - Nothing.
44
+
45
+ ### Fixed
46
+ - Overriding the `redirect_uri` via params or JSON request body.
47
+
48
+ ## 1.0.0 - 2021-03-14
49
+
50
+ ### Added
51
+ - Support for Omniauth 2.x!
52
+
53
+ ### Deprecated
54
+ - Nothing.
55
+
56
+ ### Removed
57
+ - Support for Omniauth 1.x.
58
+
59
+ ### Fixed
60
+ - Nothing.
61
+
62
+ ## 0.8.2 - 2021-03-14
63
+
64
+ ### Added
65
+ - Constrains the version to Omniauth 1.x.
66
+
67
+ ### Deprecated
68
+ - Nothing.
69
+
70
+ ### Removed
71
+ - Nothing.
72
+
73
+ ### Fixed
74
+ - Nothing.
75
+
76
+ ## 0.8.1 - 2020-12-12
77
+
78
+ ### Added
79
+ - Support reading the access token from a json request body.
80
+
81
+ ### Deprecated
82
+ - Nothing.
83
+
84
+ ### Removed
85
+ - No longer verify the iat claim for JWT.
86
+
87
+ ### Fixed
88
+ - A few minor issues with .rubocop.yml.
89
+ - Issues with image resizing code when the image came with size information from Google.
90
+
91
+ ## 0.8.0 - 2019-08-21
92
+
93
+ ### Added
94
+ - Updated omniauth-oauth2 to v1.6.0 for security fixes.
95
+
96
+ ### Deprecated
97
+ - Nothing.
98
+
99
+ ### Removed
100
+ - Ruby 2.1 support.
101
+
102
+ ### Fixed
103
+ - Nothing.
104
+
105
+ ## 0.7.0 - 2019-06-03
106
+
107
+ ### Added
108
+ - Ensure `info[:email]` is always verified, and include `unverified_email`
109
+
110
+ ### Deprecated
111
+ - Nothing.
112
+
113
+ ### Removed
114
+ - Nothing.
115
+
116
+ ### Fixed
117
+ - Nothing.
118
+
119
+ ## 0.6.1 - 2019-03-07
120
+
121
+ ### Added
122
+ - Return `email` and `email_verified` keys in response.
123
+
124
+ ### Deprecated
125
+ - Nothing.
126
+
127
+ ### Removed
128
+ - Nothing.
129
+
130
+ ### Fixed
131
+ - Nothing.
132
+
4
133
  ## 0.6.0 - 2018-12-28
5
134
 
6
135
  ### Added
@@ -12,6 +141,7 @@ All notable changes to this project will be documented in this file.
12
141
  ### Removed
13
142
  - Support for JWT 1.x.
14
143
  - Support for `raw_friend_info` and `raw_image_info`.
144
+ - Stop using Google+ API endpoints.
15
145
 
16
146
  ### Fixed
17
147
  - Nothing.
data/README.md CHANGED
@@ -1,5 +1,4 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/omniauth-google-oauth2.svg)](https://badge.fury.io/rb/omniauth-google-oauth2)
2
- [![Build Status](https://travis-ci.org/zquestz/omniauth-google-oauth2.svg)](https://travis-ci.org/zquestz/omniauth-google-oauth2)
3
2
 
4
3
  # OmniAuth Google OAuth2 Strategy
5
4
 
@@ -34,6 +33,7 @@ Here's an example for adding the middleware to a Rails app in `config/initialize
34
33
  Rails.application.config.middleware.use OmniAuth::Builder do
35
34
  provider :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET']
36
35
  end
36
+ OmniAuth.config.allowed_request_methods = %i[get]
37
37
  ```
38
38
 
39
39
  You can now access the OmniAuth Google OAuth2 URL: `/auth/google_oauth2`
@@ -54,10 +54,10 @@ You can configure several options, which you pass in to the `provider` method vi
54
54
 
55
55
  * `prompt`: A space-delimited list of string values that determines whether the user is re-prompted for authentication and/or consent. Possible values are:
56
56
  * `none`: No authentication or consent pages will be displayed; it will return an error if the user is not already authenticated and has not pre-configured consent for the requested scopes. This can be used as a method to check for existing authentication and/or consent.
57
- * `consent`: The user will always be prompted for consent, even if he has previously allowed access a given set of scopes.
57
+ * `consent`: The user will always be prompted for consent, even if they have previously allowed access a given set of scopes.
58
58
  * `select_account`: The user will always be prompted to select a user account. This allows a user who has multiple current account sessions to select one amongst them.
59
59
 
60
- If no value is specified, the user only sees the authentication page if he is not logged in and only sees the consent page the first time he authorizes a given set of scopes.
60
+ If no value is specified, the user only sees the authentication page if they are not logged in and only sees the consent page the first time they authorize a given set of scopes.
61
61
 
62
62
  * `image_aspect_ratio`: The shape of the user's profile picture. Possible values are:
63
63
  * `original`: Picture maintains its original aspect ratio.
@@ -73,7 +73,7 @@ You can configure several options, which you pass in to the `provider` method vi
73
73
 
74
74
  * `hd`: (Optional) Limit sign-in to a particular Google Apps hosted domain. This can be simply string `'domain.com'` or an array `%w(domain.com domain.co)`. More information at: https://developers.google.com/accounts/docs/OpenIDConnect#hd-param
75
75
 
76
- * `jwt_leeway`: Number of seconds passed to the JWT library as leeway. Defaults to 60 seconds.
76
+ * `jwt_leeway`: Number of seconds passed to the JWT library as leeway. Defaults to 60 seconds. Note this only works if you use jwt 2.1, as the leeway option was removed in later versions.
77
77
 
78
78
  * `skip_jwt`: Skip JWT processing. This is for users who are seeing JWT decoding errors with the `iat` field. Always try adjusting the leeway before disabling JWT processing.
79
79
 
@@ -81,15 +81,19 @@ You can configure several options, which you pass in to the `provider` method vi
81
81
 
82
82
  * `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 Authorization](https://developers.google.com/accounts/docs/OAuth2WebServer#incrementalAuth) for additional details.
83
83
 
84
- * `openid_realm`: Set the OpenID realm value, to allow upgrading from OpenID based authentication to OAuth 2 based authentication. When this is set correctly an `openid_id` value will be set in `[:extra][:id_info]` in the authentication hash with the value of the user's OpenID ID URL.
84
+ * `openid_realm`: Set the OpenID realm value, to allow upgrading from OpenID based authentication to OAuth 2 based authentication. When this is set correctly an `openid_id` value will be set in `['extra']['id_info']` in the authentication hash with the value of the user's OpenID ID URL.
85
85
 
86
- 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:
86
+ * `provider_ignores_state`: You will need to set this to `true` when using the `One-time Code Flow` below. In this flow there is no server side redirect that would set the state.
87
+
88
+ * `overridable_authorize_options`: By default, all `authorize_options` can be overridden with request parameters. You can restrict the behavior by using this option.
89
+
90
+ 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 their account when logging in and the user's profile picture is returned as a thumbnail:
87
91
 
88
92
  ```ruby
89
93
  Rails.application.config.middleware.use OmniAuth::Builder do
90
94
  provider :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'],
91
95
  {
92
- scope: 'userinfo.email, userinfo.profile, http://gdata.youtube.com',
96
+ scope: 'email, profile, http://gdata.youtube.com',
93
97
  prompt: 'select_account',
94
98
  image_aspect_ratio: 'square',
95
99
  image_size: 50
@@ -176,6 +180,8 @@ devise :omniauthable, omniauth_providers: [:google_oauth2]
176
180
  Then make sure your callbacks controller is setup.
177
181
 
178
182
  ```ruby
183
+ # app/controllers/users/omniauth_callbacks_controller.rb:
184
+
179
185
  class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
180
186
  def google_oauth2
181
187
  # You need to implement the method below in your model (e.g. app/models/user.rb)
@@ -185,7 +191,7 @@ class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
185
191
  flash[:notice] = I18n.t 'devise.omniauth_callbacks.success', kind: 'Google'
186
192
  sign_in_and_redirect @user, event: :authentication
187
193
  else
188
- session['devise.google_data'] = request.env['omniauth.auth'].except(:extra) # Removing extra as it can overflow some session stores
194
+ session['devise.google_data'] = request.env['omniauth.auth'].except('extra') # Removing extra as it can overflow some session stores
189
195
  redirect_to new_user_registration_url, alert: @user.errors.full_messages.join("\n")
190
196
  end
191
197
  end
@@ -213,6 +219,10 @@ end
213
219
  For your views you can login using:
214
220
 
215
221
  ```erb
222
+ <%# omniauth-google-oauth2 1.0.x uses OmniAuth 2 and requires using HTTP Post to initiate authentication: %>
223
+ <%= link_to "Sign in with Google", user_google_oauth2_omniauth_authorize_path, method: :post %>
224
+
225
+ <%# omniauth-google-oauth2 prior 1.0.0: %>
216
226
  <%= link_to "Sign in with Google", user_google_oauth2_omniauth_authorize_path %>
217
227
 
218
228
  <%# Devise prior 4.1.0: %>
@@ -223,7 +233,7 @@ An overview is available at https://github.com/plataformatec/devise/wiki/OmniAut
223
233
 
224
234
  ### One-time Code Flow (Hybrid Authentication)
225
235
 
226
- 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:
236
+ Google describes the One-time Code Flow [here](https://developers.google.com/identity/sign-in/web/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:
227
237
 
228
238
  1. The client (web browser) authenticates the user directly via Google's JS API. During this process assorted modals may be rendered by Google.
229
239
  2. On successful authentication, Google returns a one-time use code, which requires the Google client secret (which is only available server-side).
@@ -232,7 +242,7 @@ Google describes the One-time Code Flow [here](https://developers.google.com/+/w
232
242
 
233
243
  This flow is immune to replay attacks, and conveys no useful information to a man in the middle.
234
244
 
235
- 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.
245
+ The omniauth-google-oauth2 gem supports this mode of operation when `provider_ignores_state` is set to `true`. 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.
236
246
 
237
247
  ```javascript
238
248
  // Basic hybrid auth example following the pattern at:
@@ -247,7 +257,7 @@ function init() {
247
257
  // Ready.
248
258
  $('.google-login-button').click(function(e) {
249
259
  e.preventDefault();
250
-
260
+
251
261
  gapi.auth2.authorize({
252
262
  client_id: 'YOUR_CLIENT_ID',
253
263
  cookie_policy: 'single_host_origin',
@@ -260,7 +270,7 @@ function init() {
260
270
  success: function(data) {
261
271
  // response from server
262
272
  }
263
- });
273
+ });
264
274
  } else {
265
275
  // google authentication failed
266
276
  }
@@ -280,6 +290,66 @@ In that case, ensure to send an additional parameter `redirect_uri=` (empty stri
280
290
 
281
291
  If you're making POST requests to `/auth/google_oauth2/callback` from another domain, then you need to make sure `'X-Requested-With': 'XMLHttpRequest'` header is included with your request, otherwise your server might respond with `OAuth2::Error, : Invalid Value` error.
282
292
 
293
+ #### Getting around the `redirect_uri_mismatch` error (See [Issue #365](https://github.com/zquestz/omniauth-google-oauth2/issues/365))
294
+
295
+ If you are struggling with a persistent `redirect_uri_mismatch`, you can instead pass the `access_token` from [`getAuthResponse`](https://developers.google.com/identity/sign-in/web/reference#googleusergetauthresponseincludeauthorizationdata) directly to the `auth/google_oauth2/callback` endpoint, like so:
296
+
297
+ ```javascript
298
+ // Initialize the GoogleAuth object
299
+ let googleAuth;
300
+ gapi.load('client:auth2', async () => {
301
+ await gapi.client.init({ scope: '...', client_id: '...' });
302
+ googleAuth = gapi.auth2.getAuthInstance();
303
+ });
304
+
305
+ // Call this when the Google Sign In button is clicked
306
+ async function signInGoogle() {
307
+ const googleUser = await googleAuth.signIn(); // wait for the user to authorize through the modal
308
+ const { access_token } = googleUser.getAuthResponse();
309
+
310
+ const data = new FormData();
311
+ data.append('access_token', access_token);
312
+
313
+ const response = await api.post('/auth/google_oauth2/callback', data)
314
+ console.log(response);
315
+ }
316
+ ```
317
+
318
+ #### Using Axios
319
+ If you're making a GET resquests from another domain using `access_token`.
320
+ ```
321
+ axios
322
+ .get(
323
+ 'url(path to your callback}',
324
+ { params: { access_token: 'token' } },
325
+ headers....
326
+ )
327
+ ```
328
+
329
+ If you're making a POST resquests from another domain using `access_token`.
330
+ ```
331
+ axios
332
+ .post(
333
+ 'url(path to your callback}',
334
+ { access_token: 'token' },
335
+ headers....
336
+ )
337
+
338
+ --OR--
339
+
340
+ axios
341
+ .post(
342
+ 'url(path to your callback}',
343
+ null,
344
+ {
345
+ params: {
346
+ access_token: 'token'
347
+ },
348
+ headers....
349
+ }
350
+ )
351
+ ```
352
+
283
353
  ## Fixing Protocol Mismatch for `redirect_uri` in Rails
284
354
 
285
355
  Just set the `full_host` in OmniAuth based on the Rails.env.
data/examples/Gemfile CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
- gem 'omniauth-google-oauth2', '~> 0.5'
5
+ gem 'omniauth-google-oauth2', '~> 1.1.1'
6
6
  gem 'rubocop'
7
7
  gem 'sinatra', '~> 1.4'
8
+ gem 'webrick'
data/examples/config.ru CHANGED
@@ -19,6 +19,19 @@ OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
19
19
 
20
20
  # Main example app for omniauth-google-oauth2
21
21
  class App < Sinatra::Base
22
+ configure do
23
+ set :sessions, true
24
+ set :inline_templates, true
25
+ end
26
+
27
+ use Rack::Session::Cookie, secret: ENV['RACK_COOKIE_SECRET']
28
+
29
+ use OmniAuth::Builder do
30
+ # For additional provider examples please look at 'omni_auth.rb'
31
+ # The key provider_ignores_state is only for AJAX flows. It is not recommended for normal logins.
32
+ provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], access_type: 'offline', prompt: 'consent', provider_ignores_state: true, scope: 'email,profile,calendar'
33
+ end
34
+
22
35
  get '/' do
23
36
  <<-HTML
24
37
  <!DOCTYPE html>
@@ -73,7 +86,12 @@ class App < Sinatra::Base
73
86
  </head>
74
87
  <body>
75
88
  <ul>
76
- <li><a href='/auth/google_oauth2'>Sign in with Google</a></li>
89
+ <li>
90
+ <form method='post' action='/auth/google_oauth2'>
91
+ <input type="hidden" name="authenticity_token" value="#{request.env['rack.session']['csrf']}">
92
+ <button type='submit'>Login with Google</button>
93
+ </form>
94
+ </li>
77
95
  <li><a href='#' class="googleplus-login">Sign in with Google via AJAX</a></li>
78
96
  </ul>
79
97
  </body>
@@ -109,12 +127,4 @@ class App < Sinatra::Base
109
127
  end
110
128
  end
111
129
 
112
- use Rack::Session::Cookie, secret: ENV['RACK_COOKIE_SECRET']
113
-
114
- use OmniAuth::Builder do
115
- # For additional provider examples please look at 'omni_auth.rb'
116
- # The key provider_ignores_state is only for AJAX flows. It is not recommended for normal logins.
117
- provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], access_type: 'offline', prompt: 'consent', provider_ignores_state: true, scope: 'email,profile,calendar'
118
- end
119
-
120
130
  run App.new
@@ -10,6 +10,10 @@ Rails.application.config.middleware.use OmniAuth::Builder do
10
10
  #
11
11
  provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], scope: 'email,profile'
12
12
 
13
+ # Custom redirect_uri
14
+ #
15
+ # provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], scope: 'email,profile', redirect_uri: 'https://localhost:3000/redirect'
16
+
13
17
  # Manual setup for offline access with a refresh token.
14
18
  #
15
19
  # provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], access_type: 'offline'
@@ -2,6 +2,6 @@
2
2
 
3
3
  module OmniAuth
4
4
  module GoogleOauth2
5
- VERSION = '0.6.0'
5
+ VERSION = '1.1.1'
6
6
  end
7
7
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'jwt'
4
+ require 'oauth2'
4
5
  require 'omniauth/strategies/oauth2'
5
6
  require 'uri'
6
7
 
@@ -13,13 +14,16 @@ module OmniAuth
13
14
  BASE_SCOPES = %w[profile email openid].freeze
14
15
  DEFAULT_SCOPE = 'email,profile'
15
16
  USER_INFO_URL = 'https://www.googleapis.com/oauth2/v3/userinfo'
17
+ IMAGE_SIZE_REGEXP = /(s\d+(-c)?)|(w\d+-h\d+(-c)?)|(w\d+(-c)?)|(h\d+(-c)?)|c/
18
+ AUTHORIZE_OPTIONS = %i[access_type hd login_hint prompt request_visible_actions scope state redirect_uri include_granted_scopes openid_realm device_id device_name]
16
19
 
17
20
  option :name, 'google_oauth2'
18
21
  option :skip_friends, true
19
22
  option :skip_image_info, true
20
23
  option :skip_jwt, false
21
24
  option :jwt_leeway, 60
22
- option :authorize_options, %i[access_type hd login_hint prompt request_visible_actions scope state redirect_uri include_granted_scopes openid_realm device_id device_name]
25
+ option :authorize_options, AUTHORIZE_OPTIONS
26
+ option :overridable_authorize_options, AUTHORIZE_OPTIONS
23
27
  option :authorized_client_ids, []
24
28
 
25
29
  option :client_options,
@@ -29,7 +33,7 @@ module OmniAuth
29
33
 
30
34
  def authorize_params
31
35
  super.tap do |params|
32
- options[:authorize_options].each do |k|
36
+ (options[:authorize_options] & options[:overridable_authorize_options]).each do |k|
33
37
  params[k] = request.params[k.to_s] unless [nil, ''].include?(request.params[k.to_s])
34
38
  end
35
39
 
@@ -47,6 +51,8 @@ module OmniAuth
47
51
  prune!(
48
52
  name: raw_info['name'],
49
53
  email: verified_email,
54
+ unverified_email: raw_info['email'],
55
+ email_verified: raw_info['email_verified'],
50
56
  first_name: raw_info['given_name'],
51
57
  last_name: raw_info['family_name'],
52
58
  image: image_url,
@@ -56,11 +62,17 @@ module OmniAuth
56
62
  )
57
63
  end
58
64
 
65
+ credentials do
66
+ # Tokens and expiration will be used from OAuth2 strategy credentials block
67
+ prune!({ 'scope' => token_info(access_token.token)['scope'] })
68
+ end
69
+
59
70
  extra do
60
71
  hash = {}
61
- hash[:id_token] = access_token['id_token']
62
- if !options[:skip_jwt] && !access_token['id_token'].nil?
63
- decoded = ::JWT.decode(access_token['id_token'], nil, false).first
72
+ token = nil_or_empty?(access_token['id_token']) ? access_token.token : access_token['id_token']
73
+ hash[:id_token] = token
74
+ if !options[:skip_jwt] && !nil_or_empty?(token)
75
+ decoded = ::JWT.decode(token, nil, false).first
64
76
 
65
77
  # We have to manually verify the claims because the third parameter to
66
78
  # JWT.decode is false since no verification key is provided.
@@ -72,7 +84,7 @@ module OmniAuth
72
84
  verify_sub: false,
73
85
  verify_expiration: true,
74
86
  verify_not_before: true,
75
- verify_iat: true,
87
+ verify_iat: false,
76
88
  verify_jti: false,
77
89
  leeway: options[:jwt_leeway])
78
90
 
@@ -92,31 +104,55 @@ module OmniAuth
92
104
  verify_hd(access_token)
93
105
  access_token
94
106
  end
107
+
95
108
  alias build_access_token custom_build_access_token
96
109
 
97
110
  private
98
111
 
112
+ def nil_or_empty?(obj)
113
+ obj.is_a?(String) ? obj.empty? : obj.nil?
114
+ end
115
+
99
116
  def callback_url
100
- options[:redirect_uri] || (full_host + script_name + callback_path)
117
+ options[:redirect_uri] || (full_host + callback_path)
101
118
  end
102
119
 
103
120
  def get_access_token(request)
104
- if request.xhr? && request.params['code']
105
- verifier = request.params['code']
106
- redirect_uri = request.params['redirect_uri'] || 'postmessage'
107
- client.auth_code.get_token(verifier, get_token_options(redirect_uri), deep_symbolize(options.auth_token_params || {}))
108
- elsif request.params['code'] && request.params['redirect_uri']
109
- verifier = request.params['code']
110
- redirect_uri = request.params['redirect_uri']
111
- client.auth_code.get_token(verifier, get_token_options(redirect_uri), deep_symbolize(options.auth_token_params || {}))
112
- elsif verify_token(request.params['access_token'])
121
+ verifier = request.params['code']
122
+ redirect_uri = request.params['redirect_uri']
123
+ access_token = request.params['access_token']
124
+ if verifier && request.xhr?
125
+ client_get_token(verifier, redirect_uri || 'postmessage')
126
+ elsif verifier
127
+ client_get_token(verifier, redirect_uri || callback_url)
128
+ elsif access_token && verify_token(access_token)
113
129
  ::OAuth2::AccessToken.from_hash(client, request.params.dup)
114
- else
115
- verifier = request.params['code']
116
- client.auth_code.get_token(verifier, get_token_options(callback_url), deep_symbolize(options.auth_token_params))
130
+ elsif request.content_type =~ /json/i
131
+ begin
132
+ body = JSON.parse(request.body.read)
133
+ request.body.rewind # rewind request body for downstream middlewares
134
+ verifier = body && body['code']
135
+ access_token = body && body['access_token']
136
+ redirect_uri ||= body && body['redirect_uri']
137
+ if verifier
138
+ client_get_token(verifier, redirect_uri || 'postmessage')
139
+ elsif verify_token(access_token)
140
+ ::OAuth2::AccessToken.from_hash(client, body.dup)
141
+ end
142
+ rescue JSON::ParserError => e
143
+ warn "[omniauth google-oauth2] JSON parse error=#{e}"
144
+ end
117
145
  end
118
146
  end
119
147
 
148
+ def client_get_token(verifier, redirect_uri)
149
+ client.auth_code.get_token(verifier, get_token_options(redirect_uri), get_token_params)
150
+ end
151
+
152
+ def get_token_params
153
+ deep_symbolize(options.auth_token_params || {})
154
+ end
155
+
120
156
  def get_scope(params)
121
157
  raw_scope = params[:scope] || DEFAULT_SCOPE
122
158
  scope_list = raw_scope.split(' ').map { |item| item.split(',') }.flatten
@@ -124,7 +160,11 @@ module OmniAuth
124
160
  scope_list.join(' ')
125
161
  end
126
162
 
127
- def get_token_options(redirect_uri)
163
+ def verified_email
164
+ raw_info['email_verified'] ? raw_info['email'] : nil
165
+ end
166
+
167
+ def get_token_options(redirect_uri = '')
128
168
  { redirect_uri: redirect_uri }.merge(token_params.to_hash(symbolize_keys: true))
129
169
  end
130
170
 
@@ -135,10 +175,6 @@ module OmniAuth
135
175
  end
136
176
  end
137
177
 
138
- def verified_email
139
- raw_info['email_verified'] ? raw_info['email'] : nil
140
- end
141
-
142
178
  def image_url
143
179
  return nil unless raw_info['picture']
144
180
 
@@ -149,6 +185,10 @@ module OmniAuth
149
185
  if path_index && image_size_opts_passed?
150
186
  u.path.insert(path_index, image_params)
151
187
  u.path = u.path.gsub('//', '/')
188
+
189
+ # Check if the image is already sized!
190
+ split_path = u.path.split('/')
191
+ u.path = u.path.sub("/#{split_path[-3]}", '') if split_path[-3] =~ IMAGE_SIZE_REGEXP
152
192
  end
153
193
 
154
194
  u.query = strip_unnecessary_query_parameters(u.query)
@@ -187,12 +227,21 @@ module OmniAuth
187
227
  URI.encode_www_form(stripped_params)
188
228
  end
189
229
 
230
+ def token_info(access_token)
231
+ return nil unless access_token
232
+
233
+ @token_info ||= Hash.new do |h, k|
234
+ h[k] = client.request(:get, 'https://www.googleapis.com/oauth2/v3/tokeninfo', params: { access_token: access_token }).parsed
235
+ end
236
+
237
+ @token_info[access_token]
238
+ end
239
+
190
240
  def verify_token(access_token)
191
241
  return false unless access_token
192
242
 
193
- raw_response = client.request(:get, 'https://www.googleapis.com/oauth2/v3/tokeninfo',
194
- params: { access_token: access_token }).parsed
195
- raw_response['aud'] == options.client_id || options.authorized_client_ids.include?(raw_response['aud'])
243
+ token_info = token_info(access_token)
244
+ token_info['aud'] == options.client_id || options.authorized_client_ids.include?(token_info['aud'])
196
245
  end
197
246
 
198
247
  def verify_hd(access_token)
@@ -18,11 +18,12 @@ Gem::Specification.new do |gem|
18
18
  gem.files = `git ls-files`.split("\n")
19
19
  gem.require_paths = ['lib']
20
20
 
21
- gem.required_ruby_version = '>= 2.1'
21
+ gem.required_ruby_version = '>= 2.2'
22
22
 
23
23
  gem.add_runtime_dependency 'jwt', '>= 2.0'
24
- gem.add_runtime_dependency 'omniauth', '>= 1.1.1'
25
- gem.add_runtime_dependency 'omniauth-oauth2', '>= 1.5'
24
+ gem.add_runtime_dependency 'oauth2', '~> 2.0.6'
25
+ gem.add_runtime_dependency 'omniauth', '~> 2.0'
26
+ gem.add_runtime_dependency 'omniauth-oauth2', '~> 1.8.0'
26
27
 
27
28
  gem.add_development_dependency 'rake', '~> 12.0'
28
29
  gem.add_development_dependency 'rspec', '~> 3.6'
@@ -3,6 +3,7 @@
3
3
  require 'spec_helper'
4
4
  require 'json'
5
5
  require 'omniauth-google-oauth2'
6
+ require 'stringio'
6
7
 
7
8
  describe OmniAuth::Strategies::GoogleOauth2 do
8
9
  let(:request) { double('Request', params: {}, cookies: {}, env: {}) }
@@ -177,8 +178,8 @@ describe OmniAuth::Strategies::GoogleOauth2 do
177
178
 
178
179
  describe 'scope' do
179
180
  it 'should expand scope shortcuts' do
180
- @options = { scope: 'plus.me' }
181
- expect(subject.authorize_params['scope']).to eq('https://www.googleapis.com/auth/plus.me')
181
+ @options = { scope: 'calendar' }
182
+ expect(subject.authorize_params['scope']).to eq('https://www.googleapis.com/auth/calendar')
182
183
  end
183
184
 
184
185
  it 'should leave base scopes as is' do
@@ -241,9 +242,18 @@ describe OmniAuth::Strategies::GoogleOauth2 do
241
242
  context "authorize option #{k}" do
242
243
  let(:request) { double('Request', params: { k.to_s => 'http://example.com' }, cookies: {}, env: {}) }
243
244
 
244
- it "should set the #{k} authorize option dynamically in the request" do
245
- @options = { k: '' }
246
- expect(subject.authorize_params[k.to_s]).to eq('http://example.com')
245
+ context 'when overridable_authorize_options is default' do
246
+ it "should set the #{k} authorize option dynamically in the request" do
247
+ @options = { k: '' }
248
+ expect(subject.authorize_params[k.to_s]).to eq('http://example.com')
249
+ end
250
+ end
251
+
252
+ context 'when overridable_authorize_options is empty' do
253
+ it "should not set the #{k} authorize option dynamically in the request" do
254
+ @options = { k: '', overridable_authorize_options: [] }
255
+ expect(subject.authorize_params[k.to_s]).not_to eq('http://example.com')
256
+ end
247
257
  end
248
258
  end
249
259
  end
@@ -251,9 +261,18 @@ describe OmniAuth::Strategies::GoogleOauth2 do
251
261
  describe 'custom authorize_options' do
252
262
  let(:request) { double('Request', params: { 'foo' => 'something' }, cookies: {}, env: {}) }
253
263
 
254
- it 'should support request overrides from custom authorize_options' do
255
- @options = { authorize_options: [:foo], foo: '' }
256
- expect(subject.authorize_params['foo']).to eq('something')
264
+ context 'when overridable_authorize_options is default' do
265
+ it 'should not support request overrides from custom authorize_options' do
266
+ @options = { authorize_options: [:foo], foo: '' }
267
+ expect(subject.authorize_params['foo']).not_to eq('something')
268
+ end
269
+ end
270
+
271
+ context 'when overridable_authorize_options is customized' do
272
+ it 'should support request overrides from custom authorize_options' do
273
+ @options = { authorize_options: [:foo], overridable_authorize_options: [:foo], foo: '' }
274
+ expect(subject.authorize_params['foo']).to eq('something')
275
+ end
257
276
  end
258
277
  end
259
278
  end
@@ -288,14 +307,92 @@ describe OmniAuth::Strategies::GoogleOauth2 do
288
307
  end
289
308
  end
290
309
 
291
- describe '#callback_path' do
310
+ describe '#callback_url' do
311
+ let(:base_url) { 'https://example.com' }
312
+
292
313
  it 'has the correct default callback path' do
293
- expect(subject.callback_path).to eq('/auth/google_oauth2/callback')
314
+ allow(subject).to receive(:full_host) { base_url }
315
+ allow(subject).to receive(:script_name) { '' }
316
+ expect(subject.send(:callback_url)).to eq(base_url + '/auth/google_oauth2/callback')
317
+ end
318
+
319
+ it 'should set the callback path with script_name if present' do
320
+ allow(subject).to receive(:full_host) { base_url }
321
+ allow(subject).to receive(:script_name) { '/v1' }
322
+ expect(subject.send(:callback_url)).to eq(base_url + '/v1/auth/google_oauth2/callback')
294
323
  end
295
324
 
296
325
  it 'should set the callback_path parameter if present' do
297
326
  @options = { callback_path: '/auth/foo/callback' }
298
- expect(subject.callback_path).to eq('/auth/foo/callback')
327
+ allow(subject).to receive(:full_host) { base_url }
328
+ allow(subject).to receive(:script_name) { '' }
329
+ expect(subject.send(:callback_url)).to eq(base_url + '/auth/foo/callback')
330
+ end
331
+ end
332
+
333
+ describe '#info' do
334
+ let(:client) do
335
+ OAuth2::Client.new('abc', 'def') do |builder|
336
+ builder.request :url_encoded
337
+ builder.adapter :test do |stub|
338
+ stub.get('/oauth2/v3/userinfo') { [200, { 'content-type' => 'application/json' }, response_hash.to_json] }
339
+ end
340
+ end
341
+ end
342
+ let(:access_token) { OAuth2::AccessToken.from_hash(client, { 'access_token' => 'a' }) }
343
+ before { allow(subject).to receive(:access_token).and_return(access_token) }
344
+
345
+ context 'with verified email' do
346
+ let(:response_hash) do
347
+ { email: 'something@domain.invalid', email_verified: true }
348
+ end
349
+
350
+ it 'should return equal email and unverified_email' do
351
+ expect(subject.info[:email]).to eq('something@domain.invalid')
352
+ expect(subject.info[:unverified_email]).to eq('something@domain.invalid')
353
+ end
354
+ end
355
+
356
+ context 'with unverified email' do
357
+ let(:response_hash) do
358
+ { email: 'something@domain.invalid', email_verified: false }
359
+ end
360
+
361
+ it 'should return nil email, and correct unverified email' do
362
+ expect(subject.info[:email]).to eq(nil)
363
+ expect(subject.info[:unverified_email]).to eq('something@domain.invalid')
364
+ end
365
+ end
366
+ end
367
+
368
+ describe '#credentials' do
369
+ let(:client) { OAuth2::Client.new('abc', 'def') }
370
+ let(:access_token) { OAuth2::AccessToken.from_hash(client, access_token: 'valid_access_token', expires_at: 123_456_789, refresh_token: 'valid_refresh_token') }
371
+ before(:each) do
372
+ allow(subject).to receive(:access_token).and_return(access_token)
373
+ subject.options.client_options[:connection_build] = proc do |builder|
374
+ builder.request :url_encoded
375
+ builder.adapter :test do |stub|
376
+ stub.get('/oauth2/v3/tokeninfo?access_token=valid_access_token') do
377
+ [200, { 'Content-Type' => 'application/json; charset=UTF-8' }, JSON.dump(
378
+ aud: '000000000000.apps.googleusercontent.com',
379
+ sub: '123456789',
380
+ scope: 'profile email'
381
+ )]
382
+ end
383
+ end
384
+ end
385
+ end
386
+
387
+ it 'should return access token and (optionally) refresh token' do
388
+ expect(subject.credentials.to_h).to \
389
+ match(hash_including(
390
+ 'token' => 'valid_access_token',
391
+ 'refresh_token' => 'valid_refresh_token',
392
+ 'scope' => 'profile email',
393
+ 'expires_at' => 123_456_789,
394
+ 'expires' => true
395
+ ))
299
396
  end
300
397
  end
301
398
 
@@ -308,12 +405,10 @@ describe OmniAuth::Strategies::GoogleOauth2 do
308
405
  end
309
406
  end
310
407
  end
311
- let(:access_token) { OAuth2::AccessToken.from_hash(client, {}) }
312
-
313
408
  before { allow(subject).to receive(:access_token).and_return(access_token) }
314
409
 
315
410
  describe 'id_token' do
316
- shared_examples 'id_token issued by valid issuer' do |issuer| # rubocop:disable Metrics/BlockLength
411
+ shared_examples 'id_token issued by valid issuer' do |issuer|
317
412
  context 'when the id_token is passed into the access token' do
318
413
  let(:token_info) do
319
414
  {
@@ -370,7 +465,10 @@ describe OmniAuth::Strategies::GoogleOauth2 do
370
465
  end
371
466
  end
372
467
 
373
- context 'when the id_token is missing' do
468
+ context 'when the access token is empty or nil' do
469
+ let(:access_token) { OAuth2::AccessToken.new(client, nil, { 'refresh_token' => 'foo' }) }
470
+ before { allow(subject.extra).to receive(:access_token).and_return(access_token) }
471
+
374
472
  it 'should not include id_token' do
375
473
  expect(subject.extra).not_to have_key(:id_token)
376
474
  end
@@ -382,6 +480,19 @@ describe OmniAuth::Strategies::GoogleOauth2 do
382
480
  end
383
481
 
384
482
  describe 'raw_info' do
483
+ let(:token_info) do
484
+ {
485
+ 'abc' => 'xyz',
486
+ 'exp' => Time.now.to_i + 3600,
487
+ 'nbf' => Time.now.to_i - 60,
488
+ 'iat' => Time.now.to_i,
489
+ 'aud' => 'appid',
490
+ 'iss' => 'accounts.google.com'
491
+ }
492
+ end
493
+ let(:id_token) { JWT.encode(token_info, 'secret') }
494
+ let(:access_token) { OAuth2::AccessToken.from_hash(client, 'id_token' => id_token) }
495
+
385
496
  context 'when skip_info is true' do
386
497
  before { subject.options[:skip_info] = true }
387
498
 
@@ -426,6 +537,12 @@ describe OmniAuth::Strategies::GoogleOauth2 do
426
537
  expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/s50/photo.jpg')
427
538
  end
428
539
 
540
+ it 'should return the image with size specified in the `image_size` option when sizing is in the picture' do
541
+ @options = { image_size: 50 }
542
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh4.googleusercontent.com/url/s96-c/photo.jpg' } }
543
+ expect(subject.info[:image]).to eq('https://lh4.googleusercontent.com/url/s50/photo.jpg')
544
+ end
545
+
429
546
  it 'should handle a picture with too many slashes correctly' do
430
547
  @options = { image_size: 50 }
431
548
  allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url//photo.jpg' } }
@@ -456,24 +573,48 @@ describe OmniAuth::Strategies::GoogleOauth2 do
456
573
  expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/w50-h40/photo.jpg')
457
574
  end
458
575
 
576
+ it 'should return the image with width and height specified in the `image_size` option when sizing is in the picture' do
577
+ @options = { image_size: { width: 50, height: 40 } }
578
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/w100-h80-c/photo.jpg' } }
579
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/w50-h40/photo.jpg')
580
+ end
581
+
459
582
  it 'should return square image when `image_aspect_ratio` is specified' do
460
583
  @options = { image_aspect_ratio: 'square' }
461
584
  allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg' } }
462
585
  expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/c/photo.jpg')
463
586
  end
464
587
 
588
+ it 'should return square image when `image_aspect_ratio` is specified and sizing is in the picture' do
589
+ @options = { image_aspect_ratio: 'square' }
590
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/c/photo.jpg' } }
591
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/c/photo.jpg')
592
+ end
593
+
465
594
  it 'should return square sized image when `image_aspect_ratio` and `image_size` is set' do
466
595
  @options = { image_aspect_ratio: 'square', image_size: 50 }
467
596
  allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg' } }
468
597
  expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/s50-c/photo.jpg')
469
598
  end
470
599
 
600
+ it 'should return square sized image when `image_aspect_ratio` and `image_size` is set and sizing is in the picture' do
601
+ @options = { image_aspect_ratio: 'square', image_size: 50 }
602
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/s90/photo.jpg' } }
603
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/s50-c/photo.jpg')
604
+ end
605
+
471
606
  it 'should return square sized image when `image_aspect_ratio` and `image_size` has height and width' do
472
607
  @options = { image_aspect_ratio: 'square', image_size: { width: 50, height: 40 } }
473
608
  allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg' } }
474
609
  expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/w50-h40-c/photo.jpg')
475
610
  end
476
611
 
612
+ it 'should return square sized image when `image_aspect_ratio` and `image_size` has height and width and sizing is in the picture' do
613
+ @options = { image_aspect_ratio: 'square', image_size: { width: 50, height: 40 } }
614
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/w100-h80/photo.jpg' } }
615
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/w50-h40-c/photo.jpg')
616
+ end
617
+
477
618
  it 'should return original image if image url does not end in `photo.jpg`' do
478
619
  @options = { image_size: 50 }
479
620
  allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/photograph.jpg' } }
@@ -536,20 +677,82 @@ describe OmniAuth::Strategies::GoogleOauth2 do
536
677
  end
537
678
 
538
679
  it 'should read access_token from hash if this is not an AJAX request with a code parameter' do
680
+ client = OAuth2::Client.new('abc', 'def') do |builder|
681
+ builder.request :url_encoded
682
+ builder.adapter :test do |stub|
683
+ stub.get('/oauth2/v3/userinfo') { [200, { 'content-type' => 'application/json' }, '{"sub": "12345"}'] }
684
+ end
685
+ end
686
+
539
687
  allow(request).to receive(:xhr?).and_return(false)
540
688
  allow(request).to receive(:params).and_return('access_token' => 'valid_access_token')
541
689
  expect(subject).to receive(:verify_token).with('valid_access_token').and_return true
542
- expect(subject).to receive(:client).and_return(:client)
690
+ expect(subject).to receive(:client).and_return(client)
691
+
692
+ token = subject.build_access_token
693
+ expect(token).to be_instance_of(::OAuth2::AccessToken)
694
+ expect(token.token).to eq('valid_access_token')
695
+ expect(token.client).to eq(client)
696
+ end
697
+
698
+ it 'reads the code from a json request body' do
699
+ body = StringIO.new(%({"code":"json_access_token"}))
700
+ client = double(:client)
701
+ auth_code = double(:auth_code)
702
+
703
+ allow(request).to receive(:xhr?).and_return(false)
704
+ allow(request).to receive(:content_type).and_return('application/json')
705
+ allow(request).to receive(:body).and_return(body)
706
+ allow(client).to receive(:auth_code).and_return(auth_code)
707
+ expect(subject).to receive(:client).and_return(client)
708
+
709
+ expect(auth_code).to receive(:get_token).with('json_access_token', { redirect_uri: 'postmessage' }, {})
710
+
711
+ subject.build_access_token
712
+ end
713
+
714
+ it 'reads the redirect uri from a json request body' do
715
+ body = StringIO.new(%({"code":"json_access_token", "redirect_uri":"sample"}))
716
+ client = double(:client)
717
+ auth_code = double(:auth_code)
718
+
719
+ allow(request).to receive(:xhr?).and_return(false)
720
+ allow(request).to receive(:content_type).and_return('application/json')
721
+ allow(request).to receive(:body).and_return(body)
722
+ allow(client).to receive(:auth_code).and_return(auth_code)
723
+ expect(subject).to receive(:client).and_return(client)
724
+
725
+ expect(auth_code).to receive(:get_token).with('json_access_token', { redirect_uri: 'sample' }, {})
726
+
727
+ subject.build_access_token
728
+ end
729
+
730
+ it 'reads the access token from a json request body' do
731
+ body = StringIO.new(%({"access_token":"valid_access_token"}))
732
+ client = OAuth2::Client.new('abc', 'def') do |builder|
733
+ builder.request :url_encoded
734
+ builder.adapter :test do |stub|
735
+ stub.get('/oauth2/v3/userinfo') { [200, { 'content-type' => 'application/json' }, '{"sub": "12345"}'] }
736
+ end
737
+ end
738
+
739
+ allow(request).to receive(:xhr?).and_return(false)
740
+ allow(request).to receive(:content_type).and_return('application/json')
741
+ allow(request).to receive(:body).and_return(body)
742
+ expect(subject).to receive(:client).and_return(client)
743
+
744
+ expect(subject).to receive(:verify_token).with('valid_access_token').and_return true
543
745
 
544
746
  token = subject.build_access_token
545
747
  expect(token).to be_instance_of(::OAuth2::AccessToken)
546
748
  expect(token.token).to eq('valid_access_token')
547
- expect(token.client).to eq(:client)
749
+ expect(token.client).to eq(client)
548
750
  end
549
751
 
550
752
  it 'should use callback_url without query_string if this is not an AJAX request' do
551
753
  allow(request).to receive(:xhr?).and_return(false)
552
754
  allow(request).to receive(:params).and_return('code' => 'valid_code')
755
+ allow(request).to receive(:content_type).and_return('application/x-www-form-urlencoded')
553
756
 
554
757
  client = double(:client)
555
758
  auth_code = double(:auth_code)
@@ -619,7 +822,7 @@ describe OmniAuth::Strategies::GoogleOauth2 do
619
822
  end
620
823
  end
621
824
  end
622
- let(:access_token) { OAuth2::AccessToken.from_hash(client, {}) }
825
+ let(:access_token) { OAuth2::AccessToken.from_hash(client, { 'access_token' => 'foo' }) }
623
826
 
624
827
  context 'when domain is nil' do
625
828
  let(:client) do
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omniauth-google-oauth2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Ellithorpe
8
8
  - Yury Korolev
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-12-29 00:00:00.000000000 Z
12
+ date: 2022-09-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: jwt
@@ -25,34 +25,48 @@ dependencies:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
27
  version: '2.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: oauth2
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 2.0.6
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 2.0.6
28
42
  - !ruby/object:Gem::Dependency
29
43
  name: omniauth
30
44
  requirement: !ruby/object:Gem::Requirement
31
45
  requirements:
32
- - - ">="
46
+ - - "~>"
33
47
  - !ruby/object:Gem::Version
34
- version: 1.1.1
48
+ version: '2.0'
35
49
  type: :runtime
36
50
  prerelease: false
37
51
  version_requirements: !ruby/object:Gem::Requirement
38
52
  requirements:
39
- - - ">="
53
+ - - "~>"
40
54
  - !ruby/object:Gem::Version
41
- version: 1.1.1
55
+ version: '2.0'
42
56
  - !ruby/object:Gem::Dependency
43
57
  name: omniauth-oauth2
44
58
  requirement: !ruby/object:Gem::Requirement
45
59
  requirements:
46
- - - ">="
60
+ - - "~>"
47
61
  - !ruby/object:Gem::Version
48
- version: '1.5'
62
+ version: 1.8.0
49
63
  type: :runtime
50
64
  prerelease: false
51
65
  version_requirements: !ruby/object:Gem::Requirement
52
66
  requirements:
53
- - - ">="
67
+ - - "~>"
54
68
  - !ruby/object:Gem::Version
55
- version: '1.5'
69
+ version: 1.8.0
56
70
  - !ruby/object:Gem::Dependency
57
71
  name: rake
58
72
  requirement: !ruby/object:Gem::Requirement
@@ -103,6 +117,7 @@ executables: []
103
117
  extensions: []
104
118
  extra_rdoc_files: []
105
119
  files:
120
+ - ".github/workflows/ci.yml"
106
121
  - ".gitignore"
107
122
  - ".rubocop.yml"
108
123
  - ".travis.yml"
@@ -125,7 +140,7 @@ homepage: https://github.com/zquestz/omniauth-google-oauth2
125
140
  licenses:
126
141
  - MIT
127
142
  metadata: {}
128
- post_install_message:
143
+ post_install_message:
129
144
  rdoc_options: []
130
145
  require_paths:
131
146
  - lib
@@ -133,16 +148,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
133
148
  requirements:
134
149
  - - ">="
135
150
  - !ruby/object:Gem::Version
136
- version: '2.1'
151
+ version: '2.2'
137
152
  required_rubygems_version: !ruby/object:Gem::Requirement
138
153
  requirements:
139
154
  - - ">="
140
155
  - !ruby/object:Gem::Version
141
156
  version: '0'
142
157
  requirements: []
143
- rubyforge_project:
144
- rubygems_version: 2.6.11
145
- signing_key:
158
+ rubygems_version: 3.0.9
159
+ signing_key:
146
160
  specification_version: 4
147
161
  summary: A Google OAuth2 strategy for OmniAuth 1.x
148
162
  test_files: []