omniauth-mlh 1.0.1 → 4.0.1
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/test.yml +1 -1
- data/Gemfile +1 -0
- data/README.md +29 -8
- data/lib/omniauth/strategies/mlh.rb +79 -22
- data/lib/omniauth-mlh/version.rb +1 -1
- data/spec/omni_auth/mlh_spec.rb +7 -31
- data/spec/omni_auth/strategies/mlh_spec.rb +196 -0
- data/spec/spec_helper.rb +20 -12
- data/spec/support/shared_examples.rb +66 -28
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 354aa7b0fc62afc09143d95359d8d9ee5f414bb8f38fbf5e9ee501ad2d547c98
|
4
|
+
data.tar.gz: d21f4b91b2a41f297350a6adedf60b33adf3390c255f1254b4741713b0f75042
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6302f3601f4338a87ccbdd0e77044742dd99b06cfe985af75373e089fe90102f3babb72a6fe22dacd674e9178f407d30baf34bb85bf3059fead97d466f7b2c6
|
7
|
+
data.tar.gz: 0302bfd088521db00c27954a7bc0454219f46acc1112166655b16e449342839fcb385a73c223296c9a0ad05997ebe41bf9b432fdbb4d1f529cc382cbf2fcdc05
|
data/.github/workflows/test.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
# MLH/omniauth-mlh
|
2
2
|
|
3
|
+
[](https://badge.fury.io/rb/omniauth-mlh)
|
3
4
|
[](https://github.com/MLH/omniauth-mlh/actions/workflows/test.yml)
|
4
5
|
|
5
6
|
This is the official [OmniAuth](https://github.com/omniauth/omniauth) strategy for
|
6
|
-
authenticating with [MyMLH](https://my.mlh.io). To use it, you'll need to
|
7
|
-
[register an application](https://my.mlh.io/
|
7
|
+
authenticating with [MyMLH](https://my.mlh.io) in Ruby applications. To use it, you'll need to
|
8
|
+
[register an application](https://my.mlh.io/developers) and obtain a OAuth Application ID and Secret from MyMLH.
|
8
9
|
|
9
|
-
It now supports MyMLH API
|
10
|
+
It now supports MyMLH API V4. [Read the MyMLH V4 docs here](https://my.mlh.io/developers/docs).
|
10
11
|
|
11
12
|
Once you have done so, you can follow the instructions below:
|
12
13
|
|
13
14
|
## Requirements
|
14
15
|
|
15
|
-
This Gem requires your Ruby version to be at least `
|
16
|
-
downstream by [Omniauth](https://github.com/omniauth/omniauth/blob/master/omniauth.gemspec#L22).
|
16
|
+
This Gem requires your Ruby version to be at least `3.2.0`.
|
17
17
|
|
18
18
|
## Installation
|
19
19
|
|
@@ -33,9 +33,13 @@ Or install it yourself as:
|
|
33
33
|
|
34
34
|
## Usage (Rack)
|
35
35
|
|
36
|
+
You can find a list of potential scopes and expandable fields in the [docs](https://my.mlh.io/developers/docs). The below defaults are provided simply as an example.
|
37
|
+
|
36
38
|
```ruby
|
37
39
|
use OmniAuth::Builder do
|
38
|
-
provider :mlh, ENV['MY_MLH_KEY'], ENV['MY_MLH_SECRET'],
|
40
|
+
provider :mlh, ENV['MY_MLH_KEY'], ENV['MY_MLH_SECRET'],
|
41
|
+
scope: 'public offline_access user:read:profile',
|
42
|
+
expand_fields: ['education']
|
39
43
|
end
|
40
44
|
```
|
41
45
|
|
@@ -45,10 +49,27 @@ end
|
|
45
49
|
# config/devise.rb
|
46
50
|
|
47
51
|
Devise.setup do |config|
|
48
|
-
config.provider :mlh, ENV['MY_MLH_KEY'], ENV['MY_MLH_SECRET'],
|
52
|
+
config.provider :mlh, ENV['MY_MLH_KEY'], ENV['MY_MLH_SECRET'],
|
53
|
+
scope: 'public offline_access user:read:profile',
|
54
|
+
expand_fields: ['education']
|
49
55
|
end
|
50
56
|
```
|
51
57
|
|
58
|
+
## Accessing User Data
|
59
|
+
Once a user has been authorized and you have received a token in your callback, you may access the scoped information for that user via the info key on the request data, as per the below example from a simple Sinatra app:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
get '/auth/mlh/callback' do
|
63
|
+
auth = request.env['omniauth.auth']
|
64
|
+
user_data = auth['info']
|
65
|
+
first_name = user_data['first_name']
|
66
|
+
erb "
|
67
|
+
<h1>Hello #{first_name}</h1>"
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
You can find the full User object in the [docs](https://my.mlh.io/developers/docs).
|
72
|
+
|
52
73
|
## Contributing
|
53
74
|
|
54
75
|
For guidance on setting up a development environment and how to make a contribution to omniauth-mlh, see the [contributing guidelines](https://github.com/MLH/omniauth-mlh/blob/main/CONTRIBUTING.md).
|
@@ -60,6 +81,6 @@ We used part of [datariot/omniauth-paypal](http://github.com/datariot/omniauth-p
|
|
60
81
|
## Questions?
|
61
82
|
|
62
83
|
Have a question about the API or this library? Start by checking out the
|
63
|
-
[official MyMLH documentation](https://my.mlh.io/docs). If you still can't
|
84
|
+
[official MyMLH documentation](https://my.mlh.io/developers/docs). If you still can't
|
64
85
|
find an answer, tweet at [@MLHacks](http://twitter.com/mlhacks) or drop an
|
65
86
|
email to [engineering@mlh.io](mailto:engineering@mlh.io).
|
@@ -5,42 +5,99 @@ require 'ostruct'
|
|
5
5
|
|
6
6
|
module OmniAuth
|
7
7
|
module Strategies
|
8
|
+
# MLH OAuth2 Strategy
|
9
|
+
#
|
10
|
+
# @example Basic Usage
|
11
|
+
# use OmniAuth::Builder do
|
12
|
+
# provider :mlh, ENV['MLH_KEY'], ENV['MLH_SECRET']
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# @example With Expandable Fields
|
16
|
+
# use OmniAuth::Builder do
|
17
|
+
# provider :mlh, ENV['MLH_KEY'], ENV['MLH_SECRET'],
|
18
|
+
# expand_fields: ['education', 'professional_experience']
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# @example With Refresh Tokens (offline access)
|
22
|
+
# use OmniAuth::Builder do
|
23
|
+
# provider :mlh, ENV['MLH_KEY'], ENV['MLH_SECRET'],
|
24
|
+
# scope: 'user:read:profile offline_access'
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# When offline_access scope is requested, the strategy will include
|
28
|
+
# refresh_token in the credentials hash if provided by the server.
|
8
29
|
class MLH < OmniAuth::Strategies::OAuth2 # :nodoc:
|
9
30
|
option :name, :mlh
|
10
31
|
|
11
32
|
option :client_options, {
|
12
33
|
site: 'https://my.mlh.io',
|
13
34
|
authorize_url: 'oauth/authorize',
|
14
|
-
token_url: 'oauth/token'
|
35
|
+
token_url: 'oauth/token',
|
36
|
+
auth_scheme: :request_body # Change from basic auth to request body
|
15
37
|
}
|
16
38
|
|
39
|
+
# Support expandable fields through options
|
40
|
+
option :expand_fields, []
|
41
|
+
|
17
42
|
uid { data[:id] }
|
18
43
|
|
19
44
|
info do
|
20
|
-
|
21
|
-
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
26
|
-
:
|
27
|
-
:
|
28
|
-
:
|
29
|
-
:
|
30
|
-
|
31
|
-
|
32
|
-
:
|
33
|
-
:
|
34
|
-
:
|
35
|
-
:
|
36
|
-
|
45
|
+
{
|
46
|
+
# Basic fields
|
47
|
+
id: data[:id],
|
48
|
+
created_at: data[:created_at],
|
49
|
+
updated_at: data[:updated_at],
|
50
|
+
first_name: data[:first_name],
|
51
|
+
last_name: data[:last_name],
|
52
|
+
email: data[:email],
|
53
|
+
phone_number: data[:phone_number],
|
54
|
+
roles: data[:roles],
|
55
|
+
|
56
|
+
# Expandable fields
|
57
|
+
profile: data[:profile],
|
58
|
+
address: data[:address],
|
59
|
+
social_profiles: data[:social_profiles],
|
60
|
+
professional_experience: data[:professional_experience],
|
61
|
+
education: data[:education],
|
62
|
+
identifiers: data[:identifiers]
|
63
|
+
}
|
37
64
|
end
|
38
65
|
|
39
66
|
def data
|
40
|
-
@data ||=
|
41
|
-
|
42
|
-
|
43
|
-
|
67
|
+
@data ||= fetch_and_process_data.compact
|
68
|
+
rescue StandardError
|
69
|
+
{}
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def fetch_and_process_data
|
75
|
+
response = access_token.get(build_api_url)
|
76
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
77
|
+
return {} unless data.is_a?(Hash)
|
78
|
+
|
79
|
+
symbolize_nested_arrays(data)
|
80
|
+
end
|
81
|
+
|
82
|
+
def build_api_url
|
83
|
+
url = 'https://api.mlh.com/v4/users/me'
|
84
|
+
expand_fields = options[:expand_fields] || []
|
85
|
+
return url if expand_fields.empty?
|
86
|
+
|
87
|
+
expand_query = expand_fields.map { |f| "expand[]=#{f}" }.join('&')
|
88
|
+
"#{url}?#{expand_query}"
|
89
|
+
end
|
90
|
+
|
91
|
+
def symbolize_nested_arrays(hash)
|
92
|
+
hash.transform_values do |value|
|
93
|
+
case value
|
94
|
+
when Hash
|
95
|
+
symbolize_nested_arrays(value)
|
96
|
+
when Array
|
97
|
+
value.map { |item| item.is_a?(Hash) ? symbolize_nested_arrays(item) : item }
|
98
|
+
else
|
99
|
+
value
|
100
|
+
end
|
44
101
|
end
|
45
102
|
end
|
46
103
|
end
|
data/lib/omniauth-mlh/version.rb
CHANGED
data/spec/omni_auth/mlh_spec.rb
CHANGED
@@ -2,38 +2,14 @@
|
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
|
-
describe OmniAuth::MLH do
|
6
|
-
|
7
|
-
|
8
|
-
OmniAuth::
|
5
|
+
RSpec.describe OmniAuth::MLH do
|
6
|
+
it 'has a version number' do
|
7
|
+
expect(OmniAuth::MLH::VERSION).not_to be_nil
|
8
|
+
expect(OmniAuth::MLH::VERSION).to eq('4.0.1')
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
it 'has correct MyMLH site' do
|
15
|
-
expect(omniauth_mlh.client.site).to eq('https://my.mlh.io')
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'has correct authorize url' do
|
19
|
-
expect(omniauth_mlh.client.options[:authorize_url]).to eq('oauth/authorize')
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'has correct token url' do
|
23
|
-
expect(omniauth_mlh.client.options[:token_url]).to eq('oauth/token')
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'runs the setup block if passed one' do
|
27
|
-
counter = ''
|
28
|
-
@options = { setup: proc { |_env| counter = 'ok' } }
|
29
|
-
omniauth_mlh.setup_phase
|
30
|
-
expect(counter).to eq('ok')
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
describe '#callback_path' do
|
35
|
-
it 'has the correct callback path' do
|
36
|
-
expect(omniauth_mlh.callback_path).to eq('/auth/mlh/callback')
|
37
|
-
end
|
11
|
+
it 'loads the MLH strategy' do
|
12
|
+
expect(OmniAuth::Strategies::MLH).to be_a(Class)
|
13
|
+
expect(OmniAuth::Strategies::MLH.superclass).to eq(OmniAuth::Strategies::OAuth2)
|
38
14
|
end
|
39
15
|
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe OmniAuth::Strategies::MLH do
|
6
|
+
let(:strategy) { described_class.new(app, 'client_id', 'client_secret') }
|
7
|
+
|
8
|
+
let(:app) { ->(_env) { [200, {}, ['Hello.']] } }
|
9
|
+
let(:access_token) { instance_double(OAuth2::AccessToken, options: {}) }
|
10
|
+
|
11
|
+
before do
|
12
|
+
allow(strategy).to receive(:access_token).and_return(access_token)
|
13
|
+
end
|
14
|
+
|
15
|
+
shared_context 'with oauth response' do |response_data|
|
16
|
+
let(:oauth_response) do
|
17
|
+
instance_double(OAuth2::Response,
|
18
|
+
body: response_data.to_json,
|
19
|
+
parsed: response_data)
|
20
|
+
end
|
21
|
+
before do
|
22
|
+
allow(access_token).to receive(:get)
|
23
|
+
.with('https://api.mlh.com/v4/users/me')
|
24
|
+
.and_return(oauth_response)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#data' do
|
29
|
+
context 'with expandable fields' do
|
30
|
+
let(:response) do
|
31
|
+
instance_double(OAuth2::Response, body: {}.to_json, parsed: {})
|
32
|
+
end
|
33
|
+
let(:expand_url) { 'https://api.mlh.com/v4/users/me?expand[]=profile&expand[]=education' }
|
34
|
+
|
35
|
+
before do
|
36
|
+
allow(strategy).to receive(:options).and_return(expand_fields: ['profile', 'education'])
|
37
|
+
allow(access_token).to receive(:get).with(expand_url).and_return(response)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'constructs the correct URL with expand parameters' do
|
41
|
+
strategy.data
|
42
|
+
|
43
|
+
expect(access_token).to have_received(:get).with(expand_url)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'returns an empty hash for empty response' do
|
47
|
+
expect(strategy.data).to eq({})
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'with v4 API nested profile data' do
|
52
|
+
include_context 'with oauth response', {
|
53
|
+
'id' => 'test-id',
|
54
|
+
'first_name' => 'Jane',
|
55
|
+
'profile' => {
|
56
|
+
'age' => 22,
|
57
|
+
'gender' => 'Female'
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
it 'correctly parses nested profile data' do
|
62
|
+
result = strategy.data
|
63
|
+
|
64
|
+
expect(result).to be_a(Hash)
|
65
|
+
expect(result[:profile]).to eq({ age: 22, gender: 'Female' })
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'with v4 API empty response' do
|
70
|
+
include_context 'with oauth response', {}
|
71
|
+
|
72
|
+
it 'returns an empty hash for empty data' do
|
73
|
+
expect(strategy.data).to eq({})
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'with v4 API complex data structures' do
|
78
|
+
include_context 'with oauth response', {
|
79
|
+
'id' => 'test-id',
|
80
|
+
'education' => [{
|
81
|
+
'school' => {
|
82
|
+
'name' => 'Test University',
|
83
|
+
'location' => 'Test City'
|
84
|
+
},
|
85
|
+
'graduation_year' => 2024
|
86
|
+
}],
|
87
|
+
'social_profiles' => [
|
88
|
+
{ 'platform' => 'github', 'url' => 'https://github.com' },
|
89
|
+
'https://twitter.com'
|
90
|
+
],
|
91
|
+
'professional_experience' => [{
|
92
|
+
'company' => 'Tech Corp',
|
93
|
+
'positions' => [
|
94
|
+
{ 'title' => 'Engineer', 'years' => [2022, 2023] }
|
95
|
+
]
|
96
|
+
}]
|
97
|
+
}
|
98
|
+
|
99
|
+
it 'correctly processes complex nested structures' do
|
100
|
+
result = strategy.data
|
101
|
+
|
102
|
+
expect(result).to be_a(Hash)
|
103
|
+
expect(result[:education].first[:school]).to eq({ name: 'Test University', location: 'Test City' })
|
104
|
+
expect(result[:social_profiles]).to eq([{ platform: 'github', url: 'https://github.com' }, 'https://twitter.com'])
|
105
|
+
expect(result[:professional_experience].first[:positions].first[:years]).to eq([2022, 2023])
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'with v4 API nil and empty values' do
|
110
|
+
include_context 'with oauth response', {
|
111
|
+
'id' => 'test-id',
|
112
|
+
'first_name' => 'Jane',
|
113
|
+
'last_name' => nil,
|
114
|
+
'profile' => {
|
115
|
+
'age' => 22,
|
116
|
+
'gender' => nil,
|
117
|
+
'location' => {
|
118
|
+
'city' => nil,
|
119
|
+
'country' => 'USA'
|
120
|
+
}
|
121
|
+
},
|
122
|
+
'education' => nil,
|
123
|
+
'social_profiles' => {
|
124
|
+
'github' => {},
|
125
|
+
'linkedin' => {}
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
it 'handles nil values correctly' do
|
130
|
+
result = strategy.data
|
131
|
+
|
132
|
+
expect(result[:last_name]).to be_nil
|
133
|
+
expect(result[:profile][:gender]).to be_nil
|
134
|
+
expect(result[:profile][:location]).to eq({ city: nil, country: 'USA' })
|
135
|
+
expect(result[:education]).to be_nil
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'handles empty hash structures' do
|
139
|
+
result = strategy.data
|
140
|
+
|
141
|
+
expect(result[:social_profiles]).to eq({ github: {}, linkedin: {} })
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'with API error' do
|
146
|
+
it 'returns empty hash on error' do
|
147
|
+
allow(access_token).to receive(:get).and_raise(StandardError)
|
148
|
+
|
149
|
+
expect(strategy.data).to eq({})
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe '#uid' do
|
155
|
+
context 'with valid data' do
|
156
|
+
it 'returns the id from the data hash' do
|
157
|
+
allow(strategy).to receive(:data).and_return({ id: 'test-123' })
|
158
|
+
expect(strategy.uid).to eq('test-123')
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'with missing id' do
|
163
|
+
it 'returns nil when id is not present' do
|
164
|
+
allow(strategy).to receive(:data).and_return({})
|
165
|
+
expect(strategy.uid).to be_nil
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe '#info' do
|
171
|
+
let(:user_data) do
|
172
|
+
{
|
173
|
+
first_name: 'Jane',
|
174
|
+
last_name: 'Hacker',
|
175
|
+
email: 'jane@example.com',
|
176
|
+
roles: ['hacker']
|
177
|
+
}
|
178
|
+
end
|
179
|
+
|
180
|
+
before do
|
181
|
+
allow(strategy).to receive(:data).and_return(user_data)
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'includes basic user information' do
|
185
|
+
expect(strategy.info).to include(
|
186
|
+
first_name: 'Jane',
|
187
|
+
last_name: 'Hacker',
|
188
|
+
email: 'jane@example.com'
|
189
|
+
)
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'includes user roles' do
|
193
|
+
expect(strategy.info[:roles]).to eq(['hacker'])
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,23 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
4
|
-
|
5
|
-
require 'simplecov'
|
6
|
-
SimpleCov.start
|
7
|
-
|
8
4
|
require 'bundler/setup'
|
9
|
-
require 'rspec'
|
10
|
-
require 'rack/test'
|
11
5
|
require 'webmock/rspec'
|
6
|
+
require 'simplecov'
|
7
|
+
require 'active_support'
|
8
|
+
require 'active_support/core_ext/hash'
|
9
|
+
|
10
|
+
# Configure SimpleCov before requiring our library
|
11
|
+
SimpleCov.start do
|
12
|
+
add_filter '/spec/'
|
13
|
+
add_filter '/vendor/'
|
14
|
+
track_files 'lib/**/*.rb'
|
15
|
+
enable_coverage :branch
|
16
|
+
end
|
17
|
+
|
12
18
|
require 'omniauth'
|
19
|
+
require 'omniauth-oauth2'
|
13
20
|
require 'omniauth_mlh'
|
21
|
+
require 'omniauth/strategies/mlh'
|
14
22
|
|
15
|
-
Dir[File.expand_path('support
|
23
|
+
Dir[File.expand_path('support/**/*.rb', __dir__)].sort.each { |f| require f }
|
16
24
|
|
17
25
|
RSpec.configure do |config|
|
18
|
-
config.
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
OmniAuth.config.test_mode = true
|
26
|
+
config.expect_with :rspec do |c|
|
27
|
+
c.syntax = :expect
|
28
|
+
end
|
23
29
|
end
|
30
|
+
|
31
|
+
WebMock.disable_net_connect!(allow_localhost: true)
|
@@ -1,46 +1,84 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
RSpec.shared_examples 'basic data retrieval tests' do
|
4
|
+
context 'with successful API response' do
|
5
|
+
before do
|
6
|
+
allow(access_token).to receive(:get)
|
7
|
+
.with('https://api.mlh.com/v4/users/me')
|
8
|
+
.and_return(instance_double(OAuth2::Response, parsed: { 'data' => base_user_data }))
|
9
|
+
end
|
10
10
|
|
11
|
-
|
11
|
+
it 'returns symbolized user data' do
|
12
|
+
expect(strategy.data).to include(
|
13
|
+
id: 'c2ac35c6-aa8c-11ed-afa1-0242ac120002',
|
14
|
+
first_name: 'Jane'
|
15
|
+
)
|
12
16
|
end
|
13
17
|
end
|
18
|
+
end
|
14
19
|
|
15
|
-
|
16
|
-
|
17
|
-
|
20
|
+
RSpec.shared_examples 'expandable fields tests' do
|
21
|
+
let(:expanded_user_data) do
|
22
|
+
base_user_data.merge(
|
23
|
+
'profile' => {
|
24
|
+
'age' => 22,
|
25
|
+
'gender' => 'Female'
|
26
|
+
},
|
27
|
+
'education' => [{
|
28
|
+
'current' => true,
|
29
|
+
'school_name' => 'Hacker University',
|
30
|
+
'major' => 'Computer Science'
|
31
|
+
}]
|
32
|
+
)
|
33
|
+
end
|
18
34
|
|
19
|
-
|
20
|
-
|
35
|
+
context 'with expandable fields' do
|
36
|
+
before do
|
37
|
+
allow(access_token).to receive(:get)
|
38
|
+
.with('https://api.mlh.com/v4/users/me?expand[]=profile&expand[]=education')
|
39
|
+
.and_return(instance_double(OAuth2::Response, parsed: { 'data' => expanded_user_data }))
|
40
|
+
allow(strategy).to receive(:options).and_return(expand_fields: ['profile', 'education'])
|
21
41
|
end
|
22
42
|
|
23
|
-
it '
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
43
|
+
it 'fetches expanded fields' do
|
44
|
+
expect(strategy.data).to include(
|
45
|
+
profile: include(age: 22),
|
46
|
+
education: include(hash_including(school_name: 'Hacker University'))
|
47
|
+
)
|
28
48
|
end
|
29
49
|
end
|
50
|
+
end
|
30
51
|
|
31
|
-
|
32
|
-
|
33
|
-
|
52
|
+
RSpec.shared_examples 'error handling tests' do
|
53
|
+
context 'when API returns error' do
|
54
|
+
before do
|
55
|
+
allow(access_token).to receive(:get).and_raise(StandardError)
|
56
|
+
end
|
34
57
|
|
35
|
-
|
36
|
-
expect(
|
58
|
+
it 'returns empty hash on error' do
|
59
|
+
expect(strategy.data).to eq({})
|
37
60
|
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
RSpec.shared_examples 'info hash tests' do
|
65
|
+
let(:user_info) do
|
66
|
+
{
|
67
|
+
id: 'c2ac35c6-aa8c-11ed-afa1-0242ac120002',
|
68
|
+
first_name: 'Jane',
|
69
|
+
last_name: 'Hacker',
|
70
|
+
email: 'jane.hacker@example.com'
|
71
|
+
}
|
72
|
+
end
|
38
73
|
|
39
|
-
|
40
|
-
|
74
|
+
before do
|
75
|
+
allow(strategy).to receive(:data).and_return(user_info)
|
76
|
+
end
|
41
77
|
|
42
|
-
|
43
|
-
|
44
|
-
|
78
|
+
it 'returns formatted info hash' do
|
79
|
+
expect(strategy.info).to include(
|
80
|
+
name: 'Jane Hacker',
|
81
|
+
email: 'jane.hacker@example.com'
|
82
|
+
)
|
45
83
|
end
|
46
84
|
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
|
+
version: 4.0.1
|
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:
|
11
|
+
date: 2024-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oauth2
|
@@ -187,6 +187,7 @@ files:
|
|
187
187
|
- lib/omniauth_mlh.rb
|
188
188
|
- omniauth-mlh.gemspec
|
189
189
|
- spec/omni_auth/mlh_spec.rb
|
190
|
+
- spec/omni_auth/strategies/mlh_spec.rb
|
190
191
|
- spec/spec_helper.rb
|
191
192
|
- spec/support/shared_examples.rb
|
192
193
|
homepage: http://github.com/mlh/omniauth-mlh
|
@@ -208,7 +209,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
208
209
|
- !ruby/object:Gem::Version
|
209
210
|
version: '0'
|
210
211
|
requirements: []
|
211
|
-
rubygems_version: 3.4.
|
212
|
+
rubygems_version: 3.4.19
|
212
213
|
signing_key:
|
213
214
|
specification_version: 4
|
214
215
|
summary: Official OmniAuth strategy for MyMLH.
|