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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 96ab64892eadeb5c81fd8028bd8e2e82192f776ff510410ad5e8049630e55f6d
4
- data.tar.gz: e6c4b2d1de8f465a0fee954aeb48325e36a91d1e4bef3f210a73c31f2171c112
3
+ metadata.gz: 641588bd9aad3201d3be389098e1d2a4a53b5179cc3edaec47c70a0f3191ca81
4
+ data.tar.gz: 2289268a506cc1c641ef83aefe912996c041343530efe6010b17a49b261f8ea5
5
5
  SHA512:
6
- metadata.gz: 80400b7ddc58bc8e6b238a0ee06501069b7f41433ed227e4c40f5cc7125e3443b07450273b6f5c15522b1fe3be9328b146f8e4c92b0d1071e9619c73b54d8133
7
- data.tar.gz: 799e561814b403cf430bf540c7dd1b1f0ba2ba4d5323891e716d34030c8ce679e65f3addae6c8f7f0bca7e1f24f22d80709bed7d3324788182b8e9a7b5d9c569
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 StandardError
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
- url = 'https://api.mlh.com/v4/users/me'
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
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module OmniAuth
4
4
  module MLH
5
- VERSION = '4.1.1'
5
+ VERSION = '4.2.0'
6
6
  end
7
7
  end
@@ -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.1.1')
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 API error' do
153
- it 'returns empty hash on error' do
154
- allow(access_token).to receive(:get).and_raise(StandardError)
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.1.1
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-05-20 00:00:00.000000000 Z
11
+ date: 2026-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oauth2