omniauth-google-oauth2 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +5 -4
- data/.github/workflows/rubocop.yml +19 -0
- data/.rubocop.yml +10 -3
- data/CHANGELOG.md +17 -0
- data/Gemfile +2 -0
- data/README.md +97 -108
- data/_config.yml +3 -0
- data/examples/Gemfile +1 -0
- data/examples/config.ru +65 -50
- data/examples/omni_auth.rb +7 -15
- data/lib/omniauth/google_oauth2/version.rb +1 -1
- data/lib/omniauth/strategies/google_oauth2.rb +3 -6
- data/omniauth-google-oauth2.gemspec +7 -8
- data/spec/omniauth/strategies/google_oauth2_spec.rb +57 -2
- metadata +10 -26
- data/spec/rubocop_spec.rb +0 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2bbffd3ded3fae87a753e2111d08282928af98cbd5cf2f478e0995b438d578dd
|
|
4
|
+
data.tar.gz: 937f939cb313ebe581318183d452bc62379a430f7b5f5e52331ef066bf3f77eb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d96c30940663274f66294fb6ad29c121f413dec3f95b3707af84d47713a52b21e51ac4d11fa4f8bfd8cb9b16f62fb74115a5fb841d8598714f1f8f530427ef2c
|
|
7
|
+
data.tar.gz: 2dc76d7caf6b605347236be49750ad28ec64ba81a001f19216a9748cc986b9ffb8c7afa88081c3e7403a1ed239f7820d6252267ca7defa5f40348f1290dafa4d
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
name: CI
|
|
1
|
+
name: CI
|
|
2
2
|
|
|
3
3
|
on: [push, pull_request]
|
|
4
4
|
|
|
@@ -6,16 +6,17 @@ jobs:
|
|
|
6
6
|
test:
|
|
7
7
|
runs-on: ubuntu-latest
|
|
8
8
|
strategy:
|
|
9
|
+
fail-fast: false
|
|
9
10
|
matrix:
|
|
10
|
-
ruby-version: ['2.5', '2.6', '2.7', '3.0', '3.1', '3.2', truffleruby-head]
|
|
11
|
+
ruby-version: ['2.5', '2.6', '2.7', '3.0', '3.1', '3.2', '3.3', '3.4', '4.0', truffleruby-head]
|
|
11
12
|
|
|
12
13
|
steps:
|
|
13
|
-
- uses: actions/checkout@
|
|
14
|
+
- uses: actions/checkout@v6
|
|
14
15
|
- name: Set up Ruby ${{ matrix.ruby-version }}
|
|
15
16
|
uses: ruby/setup-ruby@v1
|
|
16
17
|
with:
|
|
17
18
|
ruby-version: ${{ matrix.ruby-version }}
|
|
18
19
|
bundler-cache: true # 'bundle install' and cache
|
|
19
|
-
- name: Run specs
|
|
20
|
+
- name: Run specs
|
|
20
21
|
run: |
|
|
21
22
|
bundle exec rake
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: rubocop
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
rubocop:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v6
|
|
12
|
+
- name: Set up Ruby 3.4
|
|
13
|
+
uses: ruby/setup-ruby@v1
|
|
14
|
+
with:
|
|
15
|
+
ruby-version: '3.4'
|
|
16
|
+
- name: Lint Ruby code with RuboCop
|
|
17
|
+
run: |
|
|
18
|
+
bundle install
|
|
19
|
+
bundle exec rubocop --parallel
|
data/.rubocop.yml
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
NewCops: disable
|
|
3
|
+
SuggestExtensions: false
|
|
4
|
+
TargetRubyVersion: 2.5
|
|
5
|
+
|
|
1
6
|
Metrics/ClassLength:
|
|
2
7
|
Enabled: false
|
|
3
8
|
Metrics/AbcSize:
|
|
4
9
|
Enabled: false
|
|
5
10
|
Metrics/BlockLength:
|
|
6
|
-
|
|
11
|
+
AllowedMethods: ['describe', 'context', 'shared_examples']
|
|
7
12
|
Metrics/CyclomaticComplexity:
|
|
8
13
|
Enabled: false
|
|
9
14
|
Layout/LineLength:
|
|
@@ -18,6 +23,8 @@ Style/MutableConstant:
|
|
|
18
23
|
Enabled: false
|
|
19
24
|
Gemspec/RequiredRubyVersion:
|
|
20
25
|
Enabled: false
|
|
26
|
+
Gemspec/RequireMFA:
|
|
27
|
+
Enabled: false
|
|
21
28
|
Lint/RaiseException:
|
|
22
29
|
Enabled: false
|
|
23
30
|
Lint/StructNewOverride:
|
|
@@ -28,5 +35,5 @@ Style/HashTransformKeys:
|
|
|
28
35
|
Enabled: false
|
|
29
36
|
Style/HashTransformValues:
|
|
30
37
|
Enabled: false
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
Style/FetchEnvVar:
|
|
39
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
All notable changes to this project will be documented in this file.
|
|
3
3
|
|
|
4
|
+
## 1.2.2 - Unreleased
|
|
5
|
+
|
|
6
|
+
### Added
|
|
7
|
+
- Ruby 4.0 support in CI.
|
|
8
|
+
|
|
9
|
+
### Deprecated
|
|
10
|
+
- Nothing.
|
|
11
|
+
|
|
12
|
+
### Removed
|
|
13
|
+
- Unused `IMAGE_SIZE_REGEXP` constant.
|
|
14
|
+
- Dead `skip_friends` and `skip_image_info` options (Google+ was shut down in 2019).
|
|
15
|
+
- `CGI.parse` dependency replaced with `URI.decode_www_form` for Ruby 4.0 compatibility.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- Updated gemspec description to reference OmniAuth instead of OmniAuth 1.x.
|
|
19
|
+
- Modernized CI: bumped actions/checkout to v6, rake to 13.3, and rubocop to latest.
|
|
20
|
+
|
|
4
21
|
## 1.2.1 - 2025-01-18
|
|
5
22
|
|
|
6
23
|
### Added
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
Strategy to authenticate with Google via OAuth2 in OmniAuth.
|
|
6
6
|
|
|
7
|
-
Get your API key at: https://
|
|
7
|
+
Get your API key at: https://console.cloud.google.com Note the Client ID and the Client Secret.
|
|
8
8
|
|
|
9
|
-
For more details, read the Google docs: https://developers.google.com/
|
|
9
|
+
For more details, read the Google docs: https://developers.google.com/identity/protocols/oauth2
|
|
10
10
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
@@ -20,7 +20,7 @@ Then `bundle install`.
|
|
|
20
20
|
|
|
21
21
|
## Google API Setup
|
|
22
22
|
|
|
23
|
-
* Go to 'https://console.
|
|
23
|
+
* Go to 'https://console.cloud.google.com'
|
|
24
24
|
* Select your project.
|
|
25
25
|
* Go to Credentials, then select the "OAuth consent screen" tab on top, and provide an 'EMAIL ADDRESS' and a 'PRODUCT NAME'
|
|
26
26
|
* Wait 10 minutes for changes to take effect.
|
|
@@ -76,11 +76,11 @@ You can configure several options, which you pass in to the `provider` method vi
|
|
|
76
76
|
|
|
77
77
|
* `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
|
|
78
78
|
|
|
79
|
-
* `jwt_leeway`: Number of seconds passed to the JWT library as leeway. Defaults to 60 seconds.
|
|
79
|
+
* `jwt_leeway`: Number of seconds passed to the JWT library as leeway. Defaults to 60 seconds.
|
|
80
80
|
|
|
81
81
|
* `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.
|
|
82
82
|
|
|
83
|
-
* `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
|
|
83
|
+
* `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 (the user's unique Google ID).
|
|
84
84
|
|
|
85
85
|
* `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.
|
|
86
86
|
|
|
@@ -121,7 +121,7 @@ Here's an example of an authentication hash available in the callback by accessi
|
|
|
121
121
|
"last_name" => "Smith",
|
|
122
122
|
"image" => "https://lh4.googleusercontent.com/photo.jpg",
|
|
123
123
|
"urls" => {
|
|
124
|
-
"google" => "https://
|
|
124
|
+
"google" => "https://profiles.google.com/100000000000000000000"
|
|
125
125
|
}
|
|
126
126
|
},
|
|
127
127
|
"credentials" => {
|
|
@@ -148,7 +148,7 @@ Here's an example of an authentication hash available in the callback by accessi
|
|
|
148
148
|
"name" => "John Smith",
|
|
149
149
|
"given_name" => "John",
|
|
150
150
|
"family_name" => "Smith",
|
|
151
|
-
"profile" => "https://
|
|
151
|
+
"profile" => "https://profiles.google.com/100000000000000000000",
|
|
152
152
|
"picture" => "https://lh4.googleusercontent.com/photo.jpg?sz=50",
|
|
153
153
|
"email" => "john@example.com",
|
|
154
154
|
"email_verified" => "true",
|
|
@@ -227,26 +227,61 @@ end
|
|
|
227
227
|
For your views you can login using:
|
|
228
228
|
|
|
229
229
|
```erb
|
|
230
|
-
<%# omniauth-google-oauth2 1.0.x uses OmniAuth 2 and requires using HTTP Post to initiate authentication: %>
|
|
231
230
|
<%= link_to "Sign in with Google", user_google_oauth2_omniauth_authorize_path, method: :post %>
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
An overview is available at https://github.com/heartcombo/devise/wiki/OmniAuth:-Overview
|
|
234
|
+
|
|
235
|
+
#### Note about multi-platform authentication (Web, Android, IOS, ...)
|
|
236
|
+
|
|
237
|
+
If you authenticate your user from multiple different platforms with a single API you will likely have different Google `client_id` depending on the platform.
|
|
238
|
+
|
|
239
|
+
This could raise errors in the callback step because the `client_id` used in the callback needs to be the same as the one used in the sign in request.
|
|
232
240
|
|
|
233
|
-
|
|
234
|
-
<%= link_to "Sign in with Google", user_google_oauth2_omniauth_authorize_path %>
|
|
241
|
+
To handle multiple `client_id` you can register multiple omniauth middlewares in your devise initializer with different names and different client ids. You can then register each middleware in your omniauthable model and add a new action in your `OmniauthCallbacksController` for each additional middleware.
|
|
235
242
|
|
|
236
|
-
|
|
237
|
-
|
|
243
|
+
```ruby
|
|
244
|
+
# config/initializers/devise.rb
|
|
245
|
+
|
|
246
|
+
config.omniauth :google_oauth2, 'GOOGLE_CLIENT_ID', 'GOOGLE_CLIENT_SECRET', { name: 'google_oauth2' }
|
|
247
|
+
|
|
248
|
+
# Native mobile applications don't require a `client_secret`
|
|
249
|
+
config.omniauth :google_oauth2, 'GOOGLE_CLIENT_ID_ANDROID', { name: 'google_oauth2_android' }
|
|
250
|
+
config.omniauth :google_oauth2, 'GOOGLE_CLIENT_ID_IOS', { name: 'google_oauth2_ios' }
|
|
238
251
|
```
|
|
239
252
|
|
|
240
|
-
|
|
253
|
+
```ruby
|
|
254
|
+
# app/models/user.rb
|
|
255
|
+
|
|
256
|
+
devise :omniauthable, omniauth_providers: %i[google_oauth2 google_oauth2_android google_oauth2_ios]
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
```ruby
|
|
260
|
+
# app/controllers/users/omniauth_callbacks_controller.rb:
|
|
261
|
+
|
|
262
|
+
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
|
263
|
+
def google_oauth2
|
|
264
|
+
# ...
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def google_oauth2_android
|
|
268
|
+
# ...
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def google_oauth2_ios
|
|
272
|
+
# ...
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
```
|
|
241
276
|
|
|
242
277
|
### One-time Code Flow (Hybrid Authentication)
|
|
243
278
|
|
|
244
|
-
Google describes the One-time Code Flow [here](https://developers.google.com/identity/
|
|
279
|
+
Google describes the One-time Code Flow [here](https://developers.google.com/identity/protocols/oauth2). 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:
|
|
245
280
|
|
|
246
|
-
1. The client (web browser) authenticates the user directly via Google's
|
|
281
|
+
1. The client (web browser) authenticates the user directly via Google's OAuth 2 API. During this process assorted modals may be rendered by Google.
|
|
247
282
|
2. On successful authentication, Google returns a one-time use code, which requires the Google client secret (which is only available server-side).
|
|
248
283
|
3. Using a AJAX request, the code is POSTed to the Omniauth Google OAuth2 callback.
|
|
249
|
-
4. The Omniauth Google OAuth2 gem will validate the code via a server-side request to Google.
|
|
284
|
+
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.
|
|
250
285
|
|
|
251
286
|
This flow is immune to replay attacks, and conveys no useful information to a man in the middle.
|
|
252
287
|
|
|
@@ -254,38 +289,52 @@ The omniauth-google-oauth2 gem supports this mode of operation when `provider_ig
|
|
|
254
289
|
|
|
255
290
|
```javascript
|
|
256
291
|
// Basic hybrid auth example following the pattern at:
|
|
257
|
-
// https://developers.google.com/identity/
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
292
|
+
// https://developers.google.com/identity/protocols/oauth2/javascript-implicit-flow
|
|
293
|
+
|
|
294
|
+
const handleGoogleOauthSignIn = () => {
|
|
295
|
+
// Google's OAuth 2.0 endpoint for requesting an access token
|
|
296
|
+
const oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';
|
|
297
|
+
|
|
298
|
+
// Parameters to pass to OAuth 2.0 endpoint.
|
|
299
|
+
const params = new URLSearchParams({
|
|
300
|
+
client_id: YOUR_CLIENT_ID,
|
|
301
|
+
prompt: 'select_account',
|
|
302
|
+
redirect_uri: YOUR_REDIRECT_URI, // This redirect_uri needs to redirect to the same domain as the one where this request is made from.
|
|
303
|
+
response_type: 'code',
|
|
304
|
+
scope: 'email openid profile',
|
|
305
|
+
state: 'google', // The state will be added in the redirect_uri's query params. Use can this if you use the same redirect_uri with different omniauth provider to know which one you're currently handling for example.
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const url = `${oauth2Endpoint}?${params.toString()}`;
|
|
309
|
+
|
|
310
|
+
// Create <a> element to redirect to OAuth 2.0 endpoint.
|
|
311
|
+
const a = document.createElement('a');
|
|
312
|
+
a.href = url;
|
|
313
|
+
a.target = '_self';
|
|
314
|
+
|
|
315
|
+
// Add a to page and click it to open the OAuth 2.0 endpoint.
|
|
316
|
+
document.body.appendChild(a);
|
|
317
|
+
a.click();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Call this method when redirected to your `redirect_uri`
|
|
321
|
+
const handleGoogleOauthCallback = async () => {
|
|
322
|
+
// Get the query params Google included in your `redirect_uri`
|
|
323
|
+
const params = new URL(document.location.toString()).searchParams;
|
|
324
|
+
const code = params.get('code');
|
|
325
|
+
const state = params.get('state') // the `state` you added in the sign in request is here if you need it.
|
|
326
|
+
|
|
327
|
+
const response = fetch('your.api.domain/auth/google_oauth2/callback', {
|
|
328
|
+
body: JSON.stringify({
|
|
329
|
+
code,
|
|
330
|
+
redirect_uri: YOUR_REDIRECT_URI, // The `redirect_uri` used in the server needs to be the same as as initially used in the client.
|
|
331
|
+
}),
|
|
332
|
+
headers: {
|
|
333
|
+
'Content-type': 'application/json',
|
|
334
|
+
},
|
|
335
|
+
method: 'POST',
|
|
287
336
|
});
|
|
288
|
-
}
|
|
337
|
+
}
|
|
289
338
|
```
|
|
290
339
|
|
|
291
340
|
#### Note about mobile clients (iOS, Android)
|
|
@@ -298,66 +347,6 @@ In that case, ensure to send an additional parameter `redirect_uri=` (empty stri
|
|
|
298
347
|
|
|
299
348
|
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.
|
|
300
349
|
|
|
301
|
-
#### Getting around the `redirect_uri_mismatch` error (See [Issue #365](https://github.com/zquestz/omniauth-google-oauth2/issues/365))
|
|
302
|
-
|
|
303
|
-
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:
|
|
304
|
-
|
|
305
|
-
```javascript
|
|
306
|
-
// Initialize the GoogleAuth object
|
|
307
|
-
let googleAuth;
|
|
308
|
-
gapi.load('client:auth2', async () => {
|
|
309
|
-
await gapi.client.init({ scope: '...', client_id: '...' });
|
|
310
|
-
googleAuth = gapi.auth2.getAuthInstance();
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
// Call this when the Google Sign In button is clicked
|
|
314
|
-
async function signInGoogle() {
|
|
315
|
-
const googleUser = await googleAuth.signIn(); // wait for the user to authorize through the modal
|
|
316
|
-
const { access_token } = googleUser.getAuthResponse();
|
|
317
|
-
|
|
318
|
-
const data = new FormData();
|
|
319
|
-
data.append('access_token', access_token);
|
|
320
|
-
|
|
321
|
-
const response = await api.post('/auth/google_oauth2/callback', data)
|
|
322
|
-
console.log(response);
|
|
323
|
-
}
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
#### Using Axios
|
|
327
|
-
If you're making a GET resquests from another domain using `access_token`.
|
|
328
|
-
```
|
|
329
|
-
axios
|
|
330
|
-
.get(
|
|
331
|
-
'url(path to your callback}',
|
|
332
|
-
{ params: { access_token: 'token' } },
|
|
333
|
-
headers....
|
|
334
|
-
)
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
If you're making a POST resquests from another domain using `access_token`.
|
|
338
|
-
```
|
|
339
|
-
axios
|
|
340
|
-
.post(
|
|
341
|
-
'url(path to your callback}',
|
|
342
|
-
{ access_token: 'token' },
|
|
343
|
-
headers....
|
|
344
|
-
)
|
|
345
|
-
|
|
346
|
-
--OR--
|
|
347
|
-
|
|
348
|
-
axios
|
|
349
|
-
.post(
|
|
350
|
-
'url(path to your callback}',
|
|
351
|
-
null,
|
|
352
|
-
{
|
|
353
|
-
params: {
|
|
354
|
-
access_token: 'token'
|
|
355
|
-
},
|
|
356
|
-
headers....
|
|
357
|
-
}
|
|
358
|
-
)
|
|
359
|
-
```
|
|
360
|
-
|
|
361
350
|
## Fixing Protocol Mismatch for `redirect_uri` in Rails
|
|
362
351
|
|
|
363
352
|
Just set the `full_host` in OmniAuth based on the Rails.env.
|
|
@@ -369,7 +358,7 @@ OmniAuth.config.full_host = Rails.env.production? ? 'https://domain.com' : 'http
|
|
|
369
358
|
|
|
370
359
|
## License
|
|
371
360
|
|
|
372
|
-
Copyright (c) 2018 by Josh Ellithorpe
|
|
361
|
+
Copyright (c) 2018-2026 by Josh Ellithorpe
|
|
373
362
|
|
|
374
363
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
375
364
|
|
data/_config.yml
ADDED
data/examples/Gemfile
CHANGED
data/examples/config.ru
CHANGED
|
@@ -29,7 +29,7 @@ class App < Sinatra::Base
|
|
|
29
29
|
use OmniAuth::Builder do
|
|
30
30
|
# For additional provider examples please look at 'omni_auth.rb'
|
|
31
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
|
|
32
|
+
provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], access_type: 'offline', prompt: 'consent', provider_ignores_state: true, scope: 'email,profile'
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
get '/' do
|
|
@@ -38,62 +38,77 @@ class App < Sinatra::Base
|
|
|
38
38
|
<html>
|
|
39
39
|
<head>
|
|
40
40
|
<title>Google OAuth2 Example</title>
|
|
41
|
-
|
|
41
|
+
</head>
|
|
42
|
+
|
|
43
|
+
<body>
|
|
44
|
+
<ul>
|
|
45
|
+
<li>
|
|
46
|
+
<form method="post" action="/auth/google_oauth2">
|
|
47
|
+
<input type="hidden" name="authenticity_token" value="#{request.env['rack.session']['csrf']}">
|
|
48
|
+
<button type="submit">Login with Google</button>
|
|
49
|
+
</form>
|
|
50
|
+
</li>
|
|
51
|
+
|
|
52
|
+
<li>
|
|
53
|
+
<a href="#" class="googleplus-login">Sign in with Google via AJAX</a>
|
|
54
|
+
</li>
|
|
55
|
+
</ul>
|
|
56
|
+
|
|
42
57
|
<script>
|
|
43
|
-
|
|
44
|
-
return $.ajax({
|
|
45
|
-
url: 'https://apis.google.com/js/client:plus.js?onload=gpAsyncInit',
|
|
46
|
-
dataType: 'script',
|
|
47
|
-
cache: true
|
|
48
|
-
});
|
|
49
|
-
});
|
|
58
|
+
const a = document.querySelector('.googleplus-login');
|
|
50
59
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
cookie_policy: 'single_host_origin',
|
|
60
|
+
const handleGoogleOauthSignIn = () => {
|
|
61
|
+
const oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';
|
|
62
|
+
|
|
63
|
+
const params = new URLSearchParams({
|
|
56
64
|
client_id: '#{ENV['GOOGLE_KEY']}',
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
$('.googleplus-login').click(function(e) {
|
|
62
|
-
e.preventDefault();
|
|
63
|
-
gapi.auth.authorize({
|
|
64
|
-
immediate: false,
|
|
65
|
-
response_type: 'code',
|
|
66
|
-
cookie_policy: 'single_host_origin',
|
|
67
|
-
client_id: '#{ENV['GOOGLE_KEY']}',
|
|
68
|
-
scope: 'email profile'
|
|
69
|
-
}, function(response) {
|
|
70
|
-
if (response && !response.error) {
|
|
71
|
-
// google authentication succeed, now post data to server.
|
|
72
|
-
jQuery.ajax({type: 'POST', url: "/auth/google_oauth2/callback", data: response,
|
|
73
|
-
success: function(data) {
|
|
74
|
-
// Log the data returning from google.
|
|
75
|
-
console.log(data)
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
} else {
|
|
79
|
-
// google authentication failed.
|
|
80
|
-
console.log("FAILED")
|
|
81
|
-
}
|
|
82
|
-
});
|
|
65
|
+
prompt: 'select_account',
|
|
66
|
+
redirect_uri: 'http://localhost:3000/callback',
|
|
67
|
+
response_type: 'code',
|
|
68
|
+
scope: 'email openid profile',
|
|
83
69
|
});
|
|
84
|
-
|
|
70
|
+
|
|
71
|
+
const url = `${oauth2Endpoint}?${params.toString()}`;
|
|
72
|
+
window.location.href = url;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
a.addEventListener('click', event => {
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
handleGoogleOauthSignIn();
|
|
78
|
+
});
|
|
85
79
|
</script>
|
|
80
|
+
</body>
|
|
81
|
+
</html>
|
|
82
|
+
HTML
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
get '/callback' do
|
|
86
|
+
<<-HTML
|
|
87
|
+
<!DOCTYPE html>
|
|
88
|
+
<html>
|
|
89
|
+
<head>
|
|
90
|
+
<title>Google OAuth2 Example</title>
|
|
86
91
|
</head>
|
|
92
|
+
|
|
87
93
|
<body>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
<p>Redirected</p>
|
|
95
|
+
|
|
96
|
+
<script>
|
|
97
|
+
const handleGoogleOauthCallback = async () => {
|
|
98
|
+
const params = new URL(document.location.toString()).searchParams;
|
|
99
|
+
const code = params.get('code');
|
|
100
|
+
|
|
101
|
+
const response = fetch('http://localhost:3000/auth/google_oauth2/callback', {
|
|
102
|
+
body: JSON.stringify({ code, redirect_uri: 'http://localhost:3000/callback' }),
|
|
103
|
+
headers: {
|
|
104
|
+
'Content-type': 'application/json',
|
|
105
|
+
},
|
|
106
|
+
method: 'POST',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
handleGoogleOauthCallback();
|
|
111
|
+
</script>
|
|
97
112
|
</body>
|
|
98
113
|
</html>
|
|
99
114
|
HTML
|
data/examples/omni_auth.rb
CHANGED
|
@@ -2,36 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
# Google's OAuth2 docs. Make sure you are familiar with all the options
|
|
4
4
|
# before attempting to configure this gem.
|
|
5
|
-
# https://developers.google.com/
|
|
5
|
+
# https://developers.google.com/identity/protocols/oauth2
|
|
6
6
|
|
|
7
7
|
Rails.application.config.middleware.use OmniAuth::Builder do
|
|
8
8
|
# Default usage, this will give you offline access and a refresh token
|
|
9
9
|
# using default scopes 'email' and 'profile'
|
|
10
10
|
#
|
|
11
|
-
provider :google_oauth2, ENV['
|
|
11
|
+
provider :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'], scope: 'email, profile'
|
|
12
12
|
|
|
13
13
|
# Custom redirect_uri
|
|
14
14
|
#
|
|
15
|
-
# provider :google_oauth2, ENV['
|
|
15
|
+
# provider :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'], scope: 'email, profile', redirect_uri: 'https://localhost:3000/redirect'
|
|
16
16
|
|
|
17
17
|
# Manual setup for offline access with a refresh token.
|
|
18
18
|
#
|
|
19
|
-
# provider :google_oauth2, ENV['
|
|
19
|
+
# provider :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'], access_type: 'offline'
|
|
20
20
|
|
|
21
|
-
# Custom scope supporting
|
|
21
|
+
# Custom scope supporting YouTube. If you are customizing scopes, remember
|
|
22
22
|
# to include the default scopes 'email' and 'profile'
|
|
23
23
|
#
|
|
24
|
-
# provider :google_oauth2, ENV['
|
|
24
|
+
# provider :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'], scope: 'https://www.googleapis.com/auth/youtube.readonly, email, profile'
|
|
25
25
|
|
|
26
26
|
# Custom scope for users only using Google for account creation/auth and do not require a refresh token.
|
|
27
27
|
#
|
|
28
|
-
# provider :google_oauth2, ENV['
|
|
29
|
-
|
|
30
|
-
# To include information about people in your circles you must include the 'plus.login' scope.
|
|
31
|
-
#
|
|
32
|
-
# provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], skip_friends: false, scope: 'email, profile, plus.login'
|
|
33
|
-
|
|
34
|
-
# If you need to acquire whether user picture is a default one or uploaded by user.
|
|
35
|
-
#
|
|
36
|
-
# provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], skip_image_info: false
|
|
28
|
+
# provider :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'], access_type: 'online', prompt: ''
|
|
37
29
|
end
|
|
@@ -14,12 +14,9 @@ module OmniAuth
|
|
|
14
14
|
BASE_SCOPES = %w[profile email openid].freeze
|
|
15
15
|
DEFAULT_SCOPE = 'email,profile'
|
|
16
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
17
|
AUTHORIZE_OPTIONS = %i[access_type hd login_hint prompt request_visible_actions scope state redirect_uri include_granted_scopes enable_granular_consent openid_realm device_id device_name]
|
|
19
18
|
|
|
20
19
|
option :name, 'google_oauth2'
|
|
21
|
-
option :skip_friends, true
|
|
22
|
-
option :skip_image_info, true
|
|
23
20
|
option :skip_jwt, false
|
|
24
21
|
option :jwt_leeway, 60
|
|
25
22
|
option :authorize_options, AUTHORIZE_OPTIONS
|
|
@@ -149,7 +146,7 @@ module OmniAuth
|
|
|
149
146
|
|
|
150
147
|
def get_scope(params)
|
|
151
148
|
raw_scope = params[:scope] || DEFAULT_SCOPE
|
|
152
|
-
scope_list = raw_scope.split
|
|
149
|
+
scope_list = raw_scope.split.map { |item| item.split(',') }.flatten
|
|
153
150
|
scope_list.map! { |s| s =~ %r{^https?://} || BASE_SCOPES.include?(s) ? s : "#{BASE_SCOPE_URL}#{s}" }
|
|
154
151
|
scope_list.join(' ')
|
|
155
152
|
end
|
|
@@ -212,8 +209,8 @@ module OmniAuth
|
|
|
212
209
|
# strip `sz` parameter (defaults to sz=50) which overrides `image_size` options
|
|
213
210
|
return nil if query_parameters.nil?
|
|
214
211
|
|
|
215
|
-
params =
|
|
216
|
-
stripped_params = params.delete_if { |key| key == 'sz' }
|
|
212
|
+
params = URI.decode_www_form(query_parameters)
|
|
213
|
+
stripped_params = params.delete_if { |key, _value| key == 'sz' }
|
|
217
214
|
|
|
218
215
|
# don't return an empty Hash since that would result
|
|
219
216
|
# in URLs with a trailing ? character: http://image.url?
|
|
@@ -9,8 +9,8 @@ Gem::Specification.new do |gem|
|
|
|
9
9
|
gem.name = 'omniauth-google-oauth2'
|
|
10
10
|
gem.version = OmniAuth::GoogleOauth2::VERSION
|
|
11
11
|
gem.license = 'MIT'
|
|
12
|
-
gem.summary = %(A Google OAuth2 strategy for OmniAuth
|
|
13
|
-
gem.description = %(A Google OAuth2 strategy for OmniAuth
|
|
12
|
+
gem.summary = %(A Google OAuth2 strategy for OmniAuth)
|
|
13
|
+
gem.description = %(A Google OAuth2 strategy for OmniAuth. This allows you to login to Google with your ruby app.)
|
|
14
14
|
gem.authors = ['Josh Ellithorpe', 'Yury Korolev']
|
|
15
15
|
gem.email = ['quest@mac.com']
|
|
16
16
|
gem.homepage = 'https://github.com/zquestz/omniauth-google-oauth2'
|
|
@@ -20,12 +20,11 @@ Gem::Specification.new do |gem|
|
|
|
20
20
|
|
|
21
21
|
gem.required_ruby_version = '>= 2.5'
|
|
22
22
|
|
|
23
|
-
gem.
|
|
24
|
-
gem.
|
|
25
|
-
gem.
|
|
26
|
-
gem.
|
|
23
|
+
gem.add_dependency 'jwt', '>= 2.9.2'
|
|
24
|
+
gem.add_dependency 'oauth2', '~> 2.0'
|
|
25
|
+
gem.add_dependency 'omniauth', '~> 2.0'
|
|
26
|
+
gem.add_dependency 'omniauth-oauth2', '~> 1.8'
|
|
27
27
|
|
|
28
|
-
gem.add_development_dependency 'rake', '~>
|
|
28
|
+
gem.add_development_dependency 'rake', '~> 13.3'
|
|
29
29
|
gem.add_development_dependency 'rspec', '~> 3.6'
|
|
30
|
-
gem.add_development_dependency 'rubocop', '~> 0.49'
|
|
31
30
|
end
|
|
@@ -341,6 +341,23 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
|
341
341
|
end
|
|
342
342
|
end
|
|
343
343
|
|
|
344
|
+
describe '#uid' do
|
|
345
|
+
let(:client) do
|
|
346
|
+
OAuth2::Client.new('abc', 'def') do |builder|
|
|
347
|
+
builder.request :url_encoded
|
|
348
|
+
builder.adapter :test do |stub|
|
|
349
|
+
stub.get('/oauth2/v3/userinfo') { [200, { 'content-type' => 'application/json' }, '{"sub": "12345"}'] }
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
let(:access_token) { OAuth2::AccessToken.from_hash(client, { 'access_token' => 'a' }) }
|
|
354
|
+
before { allow(subject).to receive(:access_token).and_return(access_token) }
|
|
355
|
+
|
|
356
|
+
it 'should return the sub from raw_info as uid' do
|
|
357
|
+
expect(subject.uid).to eq('12345')
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
344
361
|
describe '#info' do
|
|
345
362
|
let(:client) do
|
|
346
363
|
OAuth2::Client.new('abc', 'def') do |builder|
|
|
@@ -687,6 +704,24 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
|
687
704
|
end
|
|
688
705
|
end
|
|
689
706
|
|
|
707
|
+
describe 'strip_unnecessary_query_parameters' do
|
|
708
|
+
it 'should return nil when query_parameters is nil' do
|
|
709
|
+
expect(subject.send(:strip_unnecessary_query_parameters, nil)).to be_nil
|
|
710
|
+
end
|
|
711
|
+
|
|
712
|
+
it 'should return nil when sz is the only parameter' do
|
|
713
|
+
expect(subject.send(:strip_unnecessary_query_parameters, 'sz=50')).to be_nil
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
it 'should strip sz and return remaining parameters' do
|
|
717
|
+
expect(subject.send(:strip_unnecessary_query_parameters, 'sz=50&hello=true&life=42')).to eq('hello=true&life=42')
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
it 'should return all parameters when sz is not present' do
|
|
721
|
+
expect(subject.send(:strip_unnecessary_query_parameters, 'hello=true&life=42')).to eq('hello=true&life=42')
|
|
722
|
+
end
|
|
723
|
+
end
|
|
724
|
+
|
|
690
725
|
describe 'build_access_token' do
|
|
691
726
|
it 'should use a hybrid authorization request_uri if this is an AJAX request with a code parameter' do
|
|
692
727
|
allow(request).to receive(:xhr?).and_return(true)
|
|
@@ -744,7 +779,7 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
|
744
779
|
expect(subject).to receive(:client).and_return(client)
|
|
745
780
|
|
|
746
781
|
token = subject.build_access_token
|
|
747
|
-
expect(token).to be_instance_of(
|
|
782
|
+
expect(token).to be_instance_of(OAuth2::AccessToken)
|
|
748
783
|
expect(token.token).to eq('valid_access_token')
|
|
749
784
|
expect(token.client).to eq(client)
|
|
750
785
|
end
|
|
@@ -798,11 +833,22 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
|
798
833
|
expect(subject).to receive(:verify_token).with('valid_access_token').and_return true
|
|
799
834
|
|
|
800
835
|
token = subject.build_access_token
|
|
801
|
-
expect(token).to be_instance_of(
|
|
836
|
+
expect(token).to be_instance_of(OAuth2::AccessToken)
|
|
802
837
|
expect(token.token).to eq('valid_access_token')
|
|
803
838
|
expect(token.client).to eq(client)
|
|
804
839
|
end
|
|
805
840
|
|
|
841
|
+
it 'should handle a malformed json request body gracefully' do
|
|
842
|
+
body = StringIO.new('not valid json{{{')
|
|
843
|
+
|
|
844
|
+
allow(request).to receive(:xhr?).and_return(false)
|
|
845
|
+
allow(request).to receive(:params).and_return({})
|
|
846
|
+
allow(request).to receive(:content_type).and_return('application/json')
|
|
847
|
+
allow(request).to receive(:body).and_return(body)
|
|
848
|
+
|
|
849
|
+
expect { subject.build_access_token }.to output(/JSON parse error/).to_stderr
|
|
850
|
+
end
|
|
851
|
+
|
|
806
852
|
it 'should use callback_url without query_string if this is not an AJAX request' do
|
|
807
853
|
allow(request).to receive(:xhr?).and_return(false)
|
|
808
854
|
allow(request).to receive(:params).and_return('code' => 'valid_code')
|
|
@@ -856,6 +902,10 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
|
856
902
|
expect(subject.send(:verify_token, 'valid_access_token')).to eq(false)
|
|
857
903
|
end
|
|
858
904
|
|
|
905
|
+
it 'should return false if access_token is nil' do
|
|
906
|
+
expect(subject.send(:verify_token, nil)).to eq(false)
|
|
907
|
+
end
|
|
908
|
+
|
|
859
909
|
it 'should raise error if access_token is invalid' do
|
|
860
910
|
expect do
|
|
861
911
|
subject.send(:verify_token, 'invalid_access_token')
|
|
@@ -945,5 +995,10 @@ describe OmniAuth::Strategies::GoogleOauth2 do
|
|
|
945
995
|
subject.send(:verify_hd, access_token)
|
|
946
996
|
end.to raise_error(OmniAuth::Strategies::GoogleOauth2::CallbackError)
|
|
947
997
|
end
|
|
998
|
+
|
|
999
|
+
it 'should verify hd if options hd is set to wildcard *' do
|
|
1000
|
+
subject.options.hd = '*'
|
|
1001
|
+
expect(subject.send(:verify_hd, access_token)).to eq(true)
|
|
1002
|
+
end
|
|
948
1003
|
end
|
|
949
1004
|
end
|
metadata
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omniauth-google-oauth2
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.2.
|
|
4
|
+
version: 1.2.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Josh Ellithorpe
|
|
8
8
|
- Yury Korolev
|
|
9
|
-
autorequire:
|
|
10
9
|
bindir: bin
|
|
11
10
|
cert_chain: []
|
|
12
|
-
date:
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
13
12
|
dependencies:
|
|
14
13
|
- !ruby/object:Gem::Dependency
|
|
15
14
|
name: jwt
|
|
@@ -73,14 +72,14 @@ dependencies:
|
|
|
73
72
|
requirements:
|
|
74
73
|
- - "~>"
|
|
75
74
|
- !ruby/object:Gem::Version
|
|
76
|
-
version: '
|
|
75
|
+
version: '13.3'
|
|
77
76
|
type: :development
|
|
78
77
|
prerelease: false
|
|
79
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
80
79
|
requirements:
|
|
81
80
|
- - "~>"
|
|
82
81
|
- !ruby/object:Gem::Version
|
|
83
|
-
version: '
|
|
82
|
+
version: '13.3'
|
|
84
83
|
- !ruby/object:Gem::Dependency
|
|
85
84
|
name: rspec
|
|
86
85
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -95,22 +94,8 @@ dependencies:
|
|
|
95
94
|
- - "~>"
|
|
96
95
|
- !ruby/object:Gem::Version
|
|
97
96
|
version: '3.6'
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
requirement: !ruby/object:Gem::Requirement
|
|
101
|
-
requirements:
|
|
102
|
-
- - "~>"
|
|
103
|
-
- !ruby/object:Gem::Version
|
|
104
|
-
version: '0.49'
|
|
105
|
-
type: :development
|
|
106
|
-
prerelease: false
|
|
107
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
108
|
-
requirements:
|
|
109
|
-
- - "~>"
|
|
110
|
-
- !ruby/object:Gem::Version
|
|
111
|
-
version: '0.49'
|
|
112
|
-
description: A Google OAuth2 strategy for OmniAuth 1.x. This allows you to login to
|
|
113
|
-
Google with your ruby app.
|
|
97
|
+
description: A Google OAuth2 strategy for OmniAuth. This allows you to login to Google
|
|
98
|
+
with your ruby app.
|
|
114
99
|
email:
|
|
115
100
|
- quest@mac.com
|
|
116
101
|
executables: []
|
|
@@ -119,12 +104,14 @@ extra_rdoc_files: []
|
|
|
119
104
|
files:
|
|
120
105
|
- ".github/FUNDING.yml"
|
|
121
106
|
- ".github/workflows/ci.yml"
|
|
107
|
+
- ".github/workflows/rubocop.yml"
|
|
122
108
|
- ".gitignore"
|
|
123
109
|
- ".rubocop.yml"
|
|
124
110
|
- CHANGELOG.md
|
|
125
111
|
- Gemfile
|
|
126
112
|
- README.md
|
|
127
113
|
- Rakefile
|
|
114
|
+
- _config.yml
|
|
128
115
|
- examples/Gemfile
|
|
129
116
|
- examples/config.ru
|
|
130
117
|
- examples/omni_auth.rb
|
|
@@ -134,13 +121,11 @@ files:
|
|
|
134
121
|
- lib/omniauth/strategies/google_oauth2.rb
|
|
135
122
|
- omniauth-google-oauth2.gemspec
|
|
136
123
|
- spec/omniauth/strategies/google_oauth2_spec.rb
|
|
137
|
-
- spec/rubocop_spec.rb
|
|
138
124
|
- spec/spec_helper.rb
|
|
139
125
|
homepage: https://github.com/zquestz/omniauth-google-oauth2
|
|
140
126
|
licenses:
|
|
141
127
|
- MIT
|
|
142
128
|
metadata: {}
|
|
143
|
-
post_install_message:
|
|
144
129
|
rdoc_options: []
|
|
145
130
|
require_paths:
|
|
146
131
|
- lib
|
|
@@ -155,8 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
155
140
|
- !ruby/object:Gem::Version
|
|
156
141
|
version: '0'
|
|
157
142
|
requirements: []
|
|
158
|
-
rubygems_version: 3.
|
|
159
|
-
signing_key:
|
|
143
|
+
rubygems_version: 3.6.9
|
|
160
144
|
specification_version: 4
|
|
161
|
-
summary: A Google OAuth2 strategy for OmniAuth
|
|
145
|
+
summary: A Google OAuth2 strategy for OmniAuth
|
|
162
146
|
test_files: []
|