omniauth-mlh 4.1.1 → 4.2.0
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/lib/omniauth/strategies/mlh.rb +21 -2
- data/lib/omniauth-mlh/version.rb +1 -1
- data/spec/omni_auth/mlh_spec.rb +1 -1
- data/spec/omni_auth/strategies/mlh_spec.rb +144 -3
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 641588bd9aad3201d3be389098e1d2a4a53b5179cc3edaec47c70a0f3191ca81
|
|
4
|
+
data.tar.gz: 2289268a506cc1c641ef83aefe912996c041343530efe6010b17a49b261f8ea5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d5c441f65324cccf80c23cd39ab2d043c3e5ea4f93d08a009aeb2f6e3af1213f1281307692ef162d71af99d42de16defbd0fadfe46422b955b8136be770d45b0
|
|
7
|
+
data.tar.gz: cdcab860ce4e891fa4a92fc8f448e3464031ba0b9ba3eb221756606ad66763c717265234085b7441267dbf449ba95228ac6ef64c65715c95d55e4e37d59e7e2b
|
|
@@ -33,9 +33,14 @@ module OmniAuth
|
|
|
33
33
|
site: 'https://www.mlh.com',
|
|
34
34
|
authorize_url: '/oauth/authorize',
|
|
35
35
|
token_url: 'https://api.mlh.com/v4/oauth/token',
|
|
36
|
+
api_site: 'https://api.mlh.com',
|
|
36
37
|
auth_scheme: :request_body # Change from basic auth to request body
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
option :pkce, true
|
|
41
|
+
|
|
42
|
+
option :persist_credentials, true
|
|
43
|
+
|
|
39
44
|
# Support expandable fields through options
|
|
40
45
|
option :expand_fields, []
|
|
41
46
|
|
|
@@ -65,7 +70,8 @@ module OmniAuth
|
|
|
65
70
|
|
|
66
71
|
def data
|
|
67
72
|
@data ||= fetch_and_process_data.compact
|
|
68
|
-
rescue
|
|
73
|
+
rescue ::OAuth2::Error, JSON::ParserError => e
|
|
74
|
+
OmniAuth.logger.warn("OmniAuth MLH: failed to load user data (#{e.class})")
|
|
69
75
|
{}
|
|
70
76
|
end
|
|
71
77
|
|
|
@@ -79,8 +85,21 @@ module OmniAuth
|
|
|
79
85
|
symbolize_nested_arrays(data)
|
|
80
86
|
end
|
|
81
87
|
|
|
88
|
+
# Preserve the OmniAuth credential shape without exposing bearer material.
|
|
89
|
+
def credentials
|
|
90
|
+
return super if options.persist_credentials
|
|
91
|
+
|
|
92
|
+
OmniAuth::AuthHash.new(
|
|
93
|
+
token: '',
|
|
94
|
+
refresh_token: nil,
|
|
95
|
+
secret: '',
|
|
96
|
+
expires: false
|
|
97
|
+
)
|
|
98
|
+
end
|
|
99
|
+
|
|
82
100
|
def build_api_url
|
|
83
|
-
|
|
101
|
+
base = (options.dig(:client_options, :api_site) || 'https://api.mlh.com').to_s.chomp('/')
|
|
102
|
+
url = "#{base}/v4/users/me"
|
|
84
103
|
expand_fields = options[:expand_fields] || []
|
|
85
104
|
return url if expand_fields.empty?
|
|
86
105
|
|
data/lib/omniauth-mlh/version.rb
CHANGED
data/spec/omni_auth/mlh_spec.rb
CHANGED
|
@@ -5,7 +5,7 @@ require 'spec_helper'
|
|
|
5
5
|
RSpec.describe OmniAuth::MLH do
|
|
6
6
|
it 'has a version number' do
|
|
7
7
|
expect(OmniAuth::MLH::VERSION).not_to be_nil
|
|
8
|
-
expect(OmniAuth::MLH::VERSION).to eq('4.
|
|
8
|
+
expect(OmniAuth::MLH::VERSION).to eq('4.2.0')
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
it 'loads the MLH strategy' do
|
|
@@ -17,6 +17,31 @@ RSpec.describe OmniAuth::Strategies::MLH do
|
|
|
17
17
|
expect(strategy.options.client_options.authorize_url).to eq('/oauth/authorize')
|
|
18
18
|
expect(strategy.options.client_options.token_url).to eq('https://api.mlh.com/v4/oauth/token')
|
|
19
19
|
end
|
|
20
|
+
|
|
21
|
+
context 'with only api_site overridden' do
|
|
22
|
+
let(:strategy) do
|
|
23
|
+
described_class.new(app, 'client_id', 'client_secret',
|
|
24
|
+
client_options: { api_site: 'https://api.mlh.test' })
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
let(:token_request) do
|
|
28
|
+
stub_request(:post, 'https://api.mlh.com/v4/oauth/token')
|
|
29
|
+
.with(body: hash_including('client_id' => 'client_id', 'client_secret' => 'client_secret',
|
|
30
|
+
'code' => 'authorization-code', 'grant_type' => 'authorization_code'))
|
|
31
|
+
.to_return(body: { access_token: 'test-token', token_type: 'Bearer' }.to_json,
|
|
32
|
+
headers: { 'Content-Type' => 'application/json' })
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'preserves the OAuth authorization endpoint' do
|
|
36
|
+
expect(strategy.client.authorize_url).to eq('https://www.mlh.com/oauth/authorize')
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'exchanges a code at the default token endpoint with credentials in the request body' do
|
|
40
|
+
token_request
|
|
41
|
+
expect(strategy.client.auth_code.get_token('authorization-code').token).to eq('test-token')
|
|
42
|
+
expect(token_request).to have_been_requested
|
|
43
|
+
end
|
|
44
|
+
end
|
|
20
45
|
end
|
|
21
46
|
|
|
22
47
|
shared_context 'with oauth response' do |response_data|
|
|
@@ -25,6 +50,7 @@ RSpec.describe OmniAuth::Strategies::MLH do
|
|
|
25
50
|
body: response_data.to_json,
|
|
26
51
|
parsed: response_data)
|
|
27
52
|
end
|
|
53
|
+
|
|
28
54
|
before do
|
|
29
55
|
allow(access_token).to receive(:get)
|
|
30
56
|
.with('https://api.mlh.com/v4/users/me')
|
|
@@ -33,6 +59,21 @@ RSpec.describe OmniAuth::Strategies::MLH do
|
|
|
33
59
|
end
|
|
34
60
|
|
|
35
61
|
describe '#data' do
|
|
62
|
+
[nil, false].each do |expand_fields|
|
|
63
|
+
context "with expand_fields set to #{expand_fields.inspect}" do
|
|
64
|
+
let(:strategy) do
|
|
65
|
+
described_class.new(app, 'client_id', 'client_secret', expand_fields: expand_fields)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
include_context 'with oauth response', { 'id' => 'core-user-1' }
|
|
69
|
+
|
|
70
|
+
it 'fetches user data without expansion parameters' do
|
|
71
|
+
expect(strategy.data).to eq(id: 'core-user-1')
|
|
72
|
+
expect(access_token).to have_received(:get).with('https://api.mlh.com/v4/users/me')
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
36
77
|
context 'with expandable fields' do
|
|
37
78
|
let(:response) do
|
|
38
79
|
instance_double(OAuth2::Response, body: {}.to_json, parsed: {})
|
|
@@ -81,6 +122,30 @@ RSpec.describe OmniAuth::Strategies::MLH do
|
|
|
81
122
|
end
|
|
82
123
|
end
|
|
83
124
|
|
|
125
|
+
context 'with a custom api_site client option' do
|
|
126
|
+
let(:custom_strategy) do
|
|
127
|
+
described_class.new(app, 'client_id', 'client_secret',
|
|
128
|
+
client_options: { api_site: 'https://api.mlh.test' })
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
let(:response) do
|
|
132
|
+
instance_double(OAuth2::Response, body: { 'id' => 'core-user-1' }.to_json)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
before do
|
|
136
|
+
allow(custom_strategy).to receive(:access_token).and_return(access_token)
|
|
137
|
+
allow(access_token).to receive(:get)
|
|
138
|
+
.with('https://api.mlh.test/v4/users/me')
|
|
139
|
+
.and_return(response)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it 'fetches user data from the configured API base' do
|
|
143
|
+
custom_strategy.data
|
|
144
|
+
|
|
145
|
+
expect(access_token).to have_received(:get).with('https://api.mlh.test/v4/users/me')
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
84
149
|
context 'with v4 API complex data structures' do
|
|
85
150
|
include_context 'with oauth response', {
|
|
86
151
|
'id' => 'test-id',
|
|
@@ -149,13 +214,68 @@ RSpec.describe OmniAuth::Strategies::MLH do
|
|
|
149
214
|
end
|
|
150
215
|
end
|
|
151
216
|
|
|
152
|
-
context 'with
|
|
153
|
-
|
|
154
|
-
|
|
217
|
+
context 'with OAuth2::Error' do
|
|
218
|
+
let(:error_response) do
|
|
219
|
+
instance_double(OAuth2::Response,
|
|
220
|
+
status: 500,
|
|
221
|
+
headers: {},
|
|
222
|
+
body: '{"error": "server_error"}')
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
before do
|
|
226
|
+
allow(access_token).to receive(:get).and_raise(OAuth2::Error.new(error_response))
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
it 'returns empty payload for OAuth2 errors' do
|
|
230
|
+
expect(strategy.data).to eq({})
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
context 'with malformed JSON' do
|
|
235
|
+
before do
|
|
236
|
+
allow(access_token).to receive(:get)
|
|
237
|
+
.and_return(instance_double(OAuth2::Response, body: '<html>not json</html>'))
|
|
238
|
+
end
|
|
155
239
|
|
|
240
|
+
it 'returns empty payload for JSON parse failures' do
|
|
156
241
|
expect(strategy.data).to eq({})
|
|
157
242
|
end
|
|
158
243
|
end
|
|
244
|
+
|
|
245
|
+
context 'with unexpected error' do
|
|
246
|
+
let(:unexpected_error_class) { Class.new(StandardError) }
|
|
247
|
+
|
|
248
|
+
before do
|
|
249
|
+
allow(access_token).to receive(:get).and_raise(unexpected_error_class, 'boom')
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
it 'propagates unexpected errors' do
|
|
253
|
+
expect { strategy.data }.to raise_error(unexpected_error_class)
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
describe '#auth_hash' do
|
|
259
|
+
let(:strategy) do
|
|
260
|
+
described_class.new(app, 'client_id', 'client_secret', persist_credentials: false)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
let(:empty_credentials) do
|
|
264
|
+
{
|
|
265
|
+
'token' => '',
|
|
266
|
+
'refresh_token' => nil,
|
|
267
|
+
'secret' => '',
|
|
268
|
+
'expires' => false
|
|
269
|
+
}
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
before do
|
|
273
|
+
allow(strategy).to receive(:data).and_return(id: 'core-user-1')
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
it 'omits bearer credentials when persistence is disabled' do
|
|
277
|
+
expect(strategy.auth_hash['credentials']).to eq(empty_credentials)
|
|
278
|
+
end
|
|
159
279
|
end
|
|
160
280
|
|
|
161
281
|
describe '#uid' do
|
|
@@ -200,4 +320,25 @@ RSpec.describe OmniAuth::Strategies::MLH do
|
|
|
200
320
|
expect(strategy.info[:roles]).to eq(['hacker'])
|
|
201
321
|
end
|
|
202
322
|
end
|
|
323
|
+
|
|
324
|
+
describe 'PKCE authorization' do
|
|
325
|
+
before { OmniAuth.config.test_mode = true }
|
|
326
|
+
|
|
327
|
+
after { OmniAuth.config.test_mode = false }
|
|
328
|
+
|
|
329
|
+
it 'uses S256 by default and sends the verifier only in token params' do
|
|
330
|
+
params = strategy.authorize_params
|
|
331
|
+
|
|
332
|
+
expect(params).to include(code_challenge: be_present, code_challenge_method: 'S256')
|
|
333
|
+
expect(params).not_to have_key(:code_verifier)
|
|
334
|
+
expect(strategy.token_params[:code_verifier]).to eq(strategy.options.pkce_verifier).and be_present
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
it 'can be disabled via the pkce option' do
|
|
338
|
+
disabled_strategy = described_class.new(app, 'client_id', 'client_secret', pkce: false)
|
|
339
|
+
|
|
340
|
+
expect(disabled_strategy.authorize_params).not_to include(:code_challenge, :code_challenge_method)
|
|
341
|
+
expect(disabled_strategy.token_params).not_to have_key(:code_verifier)
|
|
342
|
+
end
|
|
343
|
+
end
|
|
203
344
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omniauth-mlh
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Major League Hacking (MLH)
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-09-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: oauth2
|