omniauth-shopify-app 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/build.yml +25 -0
- data/.gitignore +4 -0
- data/Gemfile +8 -0
- data/README.md +107 -0
- data/Rakefile +9 -0
- data/example/Gemfile +6 -0
- data/example/config.ru +68 -0
- data/lib/omniauth/shopify/encryptor.rb +121 -0
- data/lib/omniauth/shopify/oauth_session.rb +81 -0
- data/lib/omniauth/shopify/version.rb +5 -0
- data/lib/omniauth/shopify.rb +3 -0
- data/lib/omniauth/strategies/shopify.rb +310 -0
- data/lib/omniauth-shopify-app.rb +1 -0
- data/omniauth-shopify-app.gemspec +32 -0
- data/test/integration_test.rb +486 -0
- data/test/test_helper.rb +16 -0
- metadata +194 -0
@@ -0,0 +1,486 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class IntegrationTest < Minitest::Test
|
4
|
+
def setup
|
5
|
+
build_app(scope: OmniAuth::Strategies::Shopify::DEFAULT_SCOPE)
|
6
|
+
end
|
7
|
+
|
8
|
+
def teardown
|
9
|
+
FakeWeb.clean_registry
|
10
|
+
FakeWeb.last_request = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_authorize
|
14
|
+
response = authorize('snowdevil.myshopify.com')
|
15
|
+
assert_equal 302, response.status
|
16
|
+
assert_match %r{\A#{Regexp.quote(shopify_authorize_url)}}, response.location
|
17
|
+
redirect_params = Rack::Utils.parse_query(URI(response.location).query)
|
18
|
+
assert_equal "123", redirect_params['client_id']
|
19
|
+
assert_equal "https://app.example.com/auth/shopify/callback", redirect_params['redirect_uri']
|
20
|
+
assert_equal "read_products", redirect_params['scope']
|
21
|
+
assert_nil redirect_params['grant_options']
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_authorize_includes_auth_type_when_per_user_permissions_are_requested
|
25
|
+
build_app(per_user_permissions: true)
|
26
|
+
response = authorize('snowdevil.myshopify.com')
|
27
|
+
redirect_params = Rack::Utils.parse_query(URI(response.location).query)
|
28
|
+
assert_equal 'per-user', redirect_params['grant_options[]']
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_authorize_overrides_site_with_https_scheme
|
32
|
+
build_app setup: lambda { |env|
|
33
|
+
params = Rack::Utils.parse_query(env['QUERY_STRING'])
|
34
|
+
env['omniauth.strategy'].options[:client_options][:site] = "http://#{params['shop']}"
|
35
|
+
}
|
36
|
+
|
37
|
+
response = request.get('https://app.example.com/auth/shopify?shop=snowdevil.myshopify.com')
|
38
|
+
assert_match %r{\A#{Regexp.quote(shopify_authorize_url)}}, response.location
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_site_validation
|
42
|
+
code = SecureRandom.hex(16)
|
43
|
+
|
44
|
+
[
|
45
|
+
'foo.example.com', # shop doesn't end with .myshopify.com
|
46
|
+
'http://snowdevil.myshopify.com', # shop contains protocol
|
47
|
+
'snowdevil.myshopify.com/path', # shop contains path
|
48
|
+
'user@snowdevil.myshopify.com', # shop contains user
|
49
|
+
'snowdevil.myshopify.com:22', # shop contains port
|
50
|
+
].each do |shop, valid|
|
51
|
+
@shop = shop
|
52
|
+
response = authorize(shop)
|
53
|
+
assert_auth_failure(response, 'invalid_site')
|
54
|
+
|
55
|
+
response = callback(sign_with_new_secret(shop: shop, code: code))
|
56
|
+
assert_auth_failure(response, 'invalid_site')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_callback
|
61
|
+
access_token = SecureRandom.hex(16)
|
62
|
+
code = SecureRandom.hex(16)
|
63
|
+
expect_access_token_request(access_token, OmniAuth::Strategies::Shopify::DEFAULT_SCOPE)
|
64
|
+
|
65
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
66
|
+
|
67
|
+
assert_callback_success(response, access_token, code)
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_callback_with_legacy_signature
|
71
|
+
build_app scope: OmniAuth::Strategies::Shopify::DEFAULT_SCOPE
|
72
|
+
access_token = SecureRandom.hex(16)
|
73
|
+
code = SecureRandom.hex(16)
|
74
|
+
expect_access_token_request(access_token, OmniAuth::Strategies::Shopify::DEFAULT_SCOPE)
|
75
|
+
|
76
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]).merge(signature: 'ignored'))
|
77
|
+
|
78
|
+
assert_callback_success(response, access_token, code)
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_callback_custom_params
|
82
|
+
access_token = SecureRandom.hex(16)
|
83
|
+
code = SecureRandom.hex(16)
|
84
|
+
|
85
|
+
expect_access_token_request(access_token, OmniAuth::Strategies::Shopify::DEFAULT_SCOPE)
|
86
|
+
|
87
|
+
now = Time.now.to_i
|
88
|
+
params = { shop: 'snowdevil.myshopify.com', code: code, timestamp: now, next: '/products?page=2&q=red%20shirt', state: opts["rack.session"]["omniauth.state"] }
|
89
|
+
encoded_params = "code=#{code}&next=%2Fproducts%3Fpage%3D2%26q%3Dred%2520shirt&shop=snowdevil.myshopify.com&state=#{opts["rack.session"]["omniauth.state"]}×tamp=#{now}"
|
90
|
+
params[:hmac] = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @secret, encoded_params)
|
91
|
+
|
92
|
+
response = callback(params)
|
93
|
+
|
94
|
+
assert_callback_success(response, access_token, code)
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_callback_with_spaces_in_scope
|
98
|
+
build_app scope: 'write_products, read_orders'
|
99
|
+
access_token = SecureRandom.hex(16)
|
100
|
+
code = SecureRandom.hex(16)
|
101
|
+
expect_access_token_request(access_token, 'read_orders,write_products')
|
102
|
+
|
103
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
104
|
+
|
105
|
+
assert_callback_success(response, access_token, code)
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_callback_rejects_invalid_hmac
|
109
|
+
@secret = 'wrong_secret'
|
110
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: SecureRandom.hex(16)))
|
111
|
+
|
112
|
+
assert_auth_failure(response, 'invalid_signature')
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_callback_rejects_old_timestamps
|
116
|
+
expired_timestamp = Time.now.to_i - OmniAuth::Strategies::Shopify::CODE_EXPIRES_AFTER - 1
|
117
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: SecureRandom.hex(16), timestamp: expired_timestamp))
|
118
|
+
|
119
|
+
assert_auth_failure(response, 'invalid_signature')
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_callback_rejects_missing_hmac
|
123
|
+
code = SecureRandom.hex(16)
|
124
|
+
|
125
|
+
response = callback(shop: 'snowdevil.myshopify.com', code: code, timestamp: Time.now.to_i)
|
126
|
+
|
127
|
+
assert_auth_failure(response, 'invalid_signature')
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_callback_rejects_body_params
|
131
|
+
code = SecureRandom.hex(16)
|
132
|
+
params = sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: code)
|
133
|
+
body = Rack::Utils.build_nested_query(unsigned: 'value')
|
134
|
+
|
135
|
+
response = request.get("https://app.example.com/auth/shopify/callback?#{Rack::Utils.build_query(params)}",
|
136
|
+
input: body,
|
137
|
+
"CONTENT_TYPE" => 'application/x-www-form-urlencoded',
|
138
|
+
'rack.session' => {
|
139
|
+
'shopify.omniauth_params' => { shop: 'snowdevil.myshopify.com' }
|
140
|
+
})
|
141
|
+
|
142
|
+
assert_auth_failure(response, 'invalid_signature')
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_provider_options
|
146
|
+
build_app scope: 'read_products,read_orders,write_content',
|
147
|
+
callback_path: '/admin/auth/legacy/callback',
|
148
|
+
myshopify_domain: 'myshopify.dev:3000',
|
149
|
+
setup: lambda { |env|
|
150
|
+
shop = Rack::Request.new(env).GET['shop']
|
151
|
+
shop += ".myshopify.dev:3000" unless shop.include?(".")
|
152
|
+
env['omniauth.strategy'].options[:client_options][:site] = "https://#{shop}"
|
153
|
+
}
|
154
|
+
|
155
|
+
response = request.get("https://app.example.com/auth/shopify?shop=snowdevil.myshopify.dev:3000")
|
156
|
+
assert_equal 302, response.status
|
157
|
+
assert_match %r{\A#{Regexp.quote("https://snowdevil.myshopify.dev:3000/admin/oauth/authorize?")}}, response.location
|
158
|
+
redirect_params = Rack::Utils.parse_query(URI(response.location).query)
|
159
|
+
assert_equal 'read_products,read_orders,write_content', redirect_params['scope']
|
160
|
+
assert_equal 'https://app.example.com/admin/auth/legacy/callback', redirect_params['redirect_uri']
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_default_setup_reads_shop_from_session
|
164
|
+
build_app
|
165
|
+
response = authorize('snowdevil.myshopify.com')
|
166
|
+
assert_equal 302, response.status
|
167
|
+
assert_match %r{\A#{Regexp.quote("https://snowdevil.myshopify.com/admin/oauth/authorize?")}}, response.location
|
168
|
+
redirect_params = Rack::Utils.parse_query(URI(response.location).query)
|
169
|
+
assert_equal 'https://app.example.com/auth/shopify/callback', redirect_params['redirect_uri']
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_default_setup_reads_shop_from_params
|
173
|
+
build_app
|
174
|
+
|
175
|
+
response = request.get('https://app.example.com/auth/shopify?shop=snowdevil.myshopify.com', opts)
|
176
|
+
|
177
|
+
assert_equal 302, response.status
|
178
|
+
assert_match %r{\A#{Regexp.quote("https://snowdevil.myshopify.com/admin/oauth/authorize?")}}, response.location
|
179
|
+
redirect_params = Rack::Utils.parse_query(URI(response.location).query)
|
180
|
+
assert_equal 'https://app.example.com/auth/shopify/callback', redirect_params['redirect_uri']
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_unnecessary_read_scopes_are_removed
|
184
|
+
build_app scope: 'read_content,read_products,write_products,unauthenticated_read_checkouts,unauthenticated_write_checkouts',
|
185
|
+
callback_path: '/admin/auth/legacy/callback',
|
186
|
+
myshopify_domain: 'myshopify.dev:3000',
|
187
|
+
setup: lambda { |env|
|
188
|
+
shop = Rack::Request.new(env).GET['shop']
|
189
|
+
env['omniauth.strategy'].options[:client_options][:site] = "https://#{shop}"
|
190
|
+
}
|
191
|
+
|
192
|
+
response = request.get("https://app.example.com/auth/shopify?shop=snowdevil.myshopify.dev:3000")
|
193
|
+
assert_equal 302, response.status
|
194
|
+
redirect_params = Rack::Utils.parse_query(URI(response.location).query)
|
195
|
+
assert_equal 'read_content,write_products,unauthenticated_write_checkouts', redirect_params['scope']
|
196
|
+
end
|
197
|
+
|
198
|
+
def test_callback_with_invalid_state_fails
|
199
|
+
access_token = SecureRandom.hex(16)
|
200
|
+
code = SecureRandom.hex(16)
|
201
|
+
expect_access_token_request(access_token, OmniAuth::Strategies::Shopify::DEFAULT_SCOPE)
|
202
|
+
|
203
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: code, state: 'invalid'))
|
204
|
+
|
205
|
+
assert_equal 302, response.status
|
206
|
+
assert_equal '/auth/failure?message=csrf_detected&strategy=shopify', response.location
|
207
|
+
end
|
208
|
+
|
209
|
+
def test_callback_with_mismatching_scope_succeeds
|
210
|
+
access_token = SecureRandom.hex(16)
|
211
|
+
code = SecureRandom.hex(16)
|
212
|
+
expect_access_token_request(access_token, 'some_invalid_scope', nil)
|
213
|
+
|
214
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
215
|
+
|
216
|
+
assert_callback_success(response, access_token, code)
|
217
|
+
end
|
218
|
+
|
219
|
+
def test_callback_with_no_scope_succeeds
|
220
|
+
access_token = SecureRandom.hex(16)
|
221
|
+
code = SecureRandom.hex(16)
|
222
|
+
expect_access_token_request(access_token, nil)
|
223
|
+
|
224
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
225
|
+
|
226
|
+
assert_callback_success(response, access_token, code)
|
227
|
+
end
|
228
|
+
|
229
|
+
def test_callback_with_missing_access_scope_succeeds
|
230
|
+
build_app scope: 'first_scope,second_scope'
|
231
|
+
|
232
|
+
access_token = SecureRandom.hex(16)
|
233
|
+
code = SecureRandom.hex(16)
|
234
|
+
expect_access_token_request(access_token, 'first_scope')
|
235
|
+
|
236
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
237
|
+
|
238
|
+
assert_callback_success(response, access_token, code)
|
239
|
+
end
|
240
|
+
|
241
|
+
def test_callback_with_extra_access_scope_succeeds
|
242
|
+
build_app scope: 'first_scope,second_scope'
|
243
|
+
|
244
|
+
access_token = SecureRandom.hex(16)
|
245
|
+
code = SecureRandom.hex(16)
|
246
|
+
expect_access_token_request(access_token, 'second_scope,first_scope,third_scope')
|
247
|
+
|
248
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
249
|
+
|
250
|
+
assert_callback_success(response, access_token, code)
|
251
|
+
end
|
252
|
+
|
253
|
+
def test_callback_with_scopes_out_of_order_works
|
254
|
+
build_app scope: 'first_scope,second_scope'
|
255
|
+
|
256
|
+
access_token = SecureRandom.hex(16)
|
257
|
+
code = SecureRandom.hex(16)
|
258
|
+
expect_access_token_request(access_token, 'second_scope,first_scope')
|
259
|
+
|
260
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
261
|
+
|
262
|
+
assert_callback_success(response, access_token, code)
|
263
|
+
end
|
264
|
+
|
265
|
+
def test_callback_with_duplicate_read_scopes_works
|
266
|
+
build_app scope: 'read_products,write_products,unauthenticated_read_products,unauthenticated_write_products'
|
267
|
+
|
268
|
+
access_token = SecureRandom.hex(16)
|
269
|
+
code = SecureRandom.hex(16)
|
270
|
+
expect_access_token_request(access_token, 'write_products,unauthenticated_write_products')
|
271
|
+
|
272
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
273
|
+
|
274
|
+
assert_callback_success(response, access_token, code)
|
275
|
+
end
|
276
|
+
|
277
|
+
def test_callback_with_extra_coma_works
|
278
|
+
build_app scope: 'read_content,,write_products,'
|
279
|
+
|
280
|
+
access_token = SecureRandom.hex(16)
|
281
|
+
code = SecureRandom.hex(16)
|
282
|
+
expect_access_token_request(access_token, 'read_content,write_products')
|
283
|
+
|
284
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
285
|
+
|
286
|
+
assert_callback_success(response, access_token, code)
|
287
|
+
end
|
288
|
+
|
289
|
+
def test_callback_when_per_user_permissions_are_present_but_not_requested
|
290
|
+
build_app(scope: 'scope', per_user_permissions: false)
|
291
|
+
|
292
|
+
access_token = SecureRandom.hex(16)
|
293
|
+
code = SecureRandom.hex(16)
|
294
|
+
expect_access_token_request(access_token, 'scope', { id: 1, email: 'bob@bobsen.com'})
|
295
|
+
|
296
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
297
|
+
|
298
|
+
assert_equal 302, response.status
|
299
|
+
assert_equal '/auth/failure?message=invalid_permissions&strategy=shopify', response.location
|
300
|
+
end
|
301
|
+
|
302
|
+
def test_callback_when_per_user_permissions_are_not_present_and_options_is_nil
|
303
|
+
build_app(scope: 'scope', per_user_permissions: nil)
|
304
|
+
|
305
|
+
access_token = SecureRandom.hex(16)
|
306
|
+
code = SecureRandom.hex(16)
|
307
|
+
expect_access_token_request(access_token, 'scope', nil)
|
308
|
+
|
309
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
310
|
+
|
311
|
+
assert_callback_success(response, access_token, code)
|
312
|
+
end
|
313
|
+
|
314
|
+
def test_callback_when_per_user_permissions_are_not_present_but_requested
|
315
|
+
build_app(scope: 'scope', per_user_permissions: true)
|
316
|
+
|
317
|
+
access_token = SecureRandom.hex(16)
|
318
|
+
code = SecureRandom.hex(16)
|
319
|
+
expect_access_token_request(access_token, 'scope', nil)
|
320
|
+
|
321
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
322
|
+
|
323
|
+
assert_equal 302, response.status
|
324
|
+
assert_equal '/auth/failure?message=invalid_permissions&strategy=shopify', response.location
|
325
|
+
end
|
326
|
+
|
327
|
+
def test_callback_works_when_per_user_permissions_are_present_and_requested
|
328
|
+
build_app(scope: 'scope', per_user_permissions: true)
|
329
|
+
|
330
|
+
access_token = SecureRandom.hex(16)
|
331
|
+
code = SecureRandom.hex(16)
|
332
|
+
expect_access_token_request(access_token, 'scope', { id: 1, email: 'bob@bobsen.com'})
|
333
|
+
|
334
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
335
|
+
|
336
|
+
assert_equal 200, response.status
|
337
|
+
end
|
338
|
+
|
339
|
+
def test_callback_when_a_session_is_present
|
340
|
+
build_app(scope: 'scope', per_user_permissions: true)
|
341
|
+
|
342
|
+
access_token = SecureRandom.hex(16)
|
343
|
+
code = SecureRandom.hex(16)
|
344
|
+
session = SecureRandom.hex
|
345
|
+
expect_access_token_request(access_token, 'scope', { id: 1, email: 'bob@bobsen.com'}, session)
|
346
|
+
|
347
|
+
response = callback(sign_with_new_secret(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
348
|
+
|
349
|
+
assert_equal 200, response.status
|
350
|
+
end
|
351
|
+
|
352
|
+
def test_callback_works_with_old_secret
|
353
|
+
build_app scope: OmniAuth::Strategies::Shopify::DEFAULT_SCOPE
|
354
|
+
access_token = SecureRandom.hex(16)
|
355
|
+
code = SecureRandom.hex(16)
|
356
|
+
expect_access_token_request(access_token, OmniAuth::Strategies::Shopify::DEFAULT_SCOPE)
|
357
|
+
|
358
|
+
signed_params = sign_with_old_secret(
|
359
|
+
shop: 'snowdevil.myshopify.com',
|
360
|
+
code: code,
|
361
|
+
state: opts["rack.session"]["omniauth.state"]
|
362
|
+
)
|
363
|
+
|
364
|
+
response = callback(signed_params)
|
365
|
+
|
366
|
+
assert_callback_success(response, access_token, code)
|
367
|
+
end
|
368
|
+
|
369
|
+
def test_callback_when_creds_are_invalid
|
370
|
+
build_app scope: OmniAuth::Strategies::Shopify::DEFAULT_SCOPE
|
371
|
+
|
372
|
+
FakeWeb.register_uri(
|
373
|
+
:post,
|
374
|
+
%r{snowdevil.myshopify.com/admin/oauth/access_token},
|
375
|
+
status: [ "401", "Invalid token" ],
|
376
|
+
body: "Token is invalid or has already been requested"
|
377
|
+
)
|
378
|
+
|
379
|
+
signed_params = sign_with_new_secret(
|
380
|
+
shop: 'snowdevil.myshopify.com',
|
381
|
+
code: SecureRandom.hex(16),
|
382
|
+
state: opts["rack.session"]["omniauth.state"]
|
383
|
+
)
|
384
|
+
|
385
|
+
response = callback(signed_params)
|
386
|
+
|
387
|
+
assert_equal 302, response.status
|
388
|
+
assert_equal '/auth/failure?message=invalid_credentials&strategy=shopify', response.location
|
389
|
+
end
|
390
|
+
|
391
|
+
private
|
392
|
+
|
393
|
+
def sign_with_old_secret(params)
|
394
|
+
params = add_time(params)
|
395
|
+
encoded_params = OmniAuth::Strategies::Shopify.encoded_params_for_signature(params)
|
396
|
+
params['hmac'] = OmniAuth::Strategies::Shopify.hmac_sign(encoded_params, @old_secret)
|
397
|
+
params
|
398
|
+
end
|
399
|
+
|
400
|
+
def add_time(params)
|
401
|
+
params = params.dup
|
402
|
+
params[:timestamp] ||= Time.now.to_i
|
403
|
+
params
|
404
|
+
end
|
405
|
+
|
406
|
+
def sign_with_new_secret(params)
|
407
|
+
params = add_time(params)
|
408
|
+
encoded_params = OmniAuth::Strategies::Shopify.encoded_params_for_signature(params)
|
409
|
+
params['hmac'] = OmniAuth::Strategies::Shopify.hmac_sign(encoded_params, @secret)
|
410
|
+
params
|
411
|
+
end
|
412
|
+
|
413
|
+
def expect_access_token_request(access_token, scope, associated_user=nil, session=nil)
|
414
|
+
FakeWeb.register_uri(:post, %r{snowdevil.myshopify.com/admin/oauth/access_token},
|
415
|
+
body: JSON.dump(
|
416
|
+
access_token: access_token,
|
417
|
+
scope: scope,
|
418
|
+
associated_user: associated_user,
|
419
|
+
session: session,
|
420
|
+
),
|
421
|
+
content_type: 'application/json')
|
422
|
+
end
|
423
|
+
|
424
|
+
def assert_callback_success(response, access_token, code)
|
425
|
+
credentials = ::Base64.decode64(FakeWeb.last_request['authorization'].split(" ", 2)[1] || "")
|
426
|
+
assert_equal "123:#{@secret}", credentials
|
427
|
+
|
428
|
+
token_request_params = Rack::Utils.parse_query(FakeWeb.last_request.body)
|
429
|
+
assert_equal code, token_request_params['code']
|
430
|
+
|
431
|
+
assert_equal 'snowdevil.myshopify.com', @omniauth_result.uid
|
432
|
+
assert_equal access_token, @omniauth_result.credentials.token
|
433
|
+
assert_equal false, @omniauth_result.credentials.expires
|
434
|
+
|
435
|
+
assert_equal 200, response.status
|
436
|
+
assert_equal "OK", response.body
|
437
|
+
end
|
438
|
+
|
439
|
+
def assert_auth_failure(response, reason)
|
440
|
+
assert_nil FakeWeb.last_request
|
441
|
+
assert_equal 302, response.status
|
442
|
+
assert_match %r{\A#{Regexp.quote("/auth/failure?message=#{reason}")}}, response.location
|
443
|
+
end
|
444
|
+
|
445
|
+
def build_app(options={})
|
446
|
+
@old_secret = '12d34s1'
|
447
|
+
@secret = '53cr3tz'
|
448
|
+
options.merge!(old_client_secret: @old_secret)
|
449
|
+
app = proc { |env|
|
450
|
+
@omniauth_result = env['omniauth.auth']
|
451
|
+
[200, {Rack::CONTENT_TYPE => "text/plain"}, "OK"]
|
452
|
+
}
|
453
|
+
|
454
|
+
opts["rack.session"]["omniauth.state"] = SecureRandom.hex(32)
|
455
|
+
app = OmniAuth::Builder.new(app) do
|
456
|
+
provider :shopify, '123', '53cr3tz' , options
|
457
|
+
end
|
458
|
+
@app = Rack::Session::Cookie.new(app, secret: SecureRandom.hex(64))
|
459
|
+
end
|
460
|
+
|
461
|
+
def shop
|
462
|
+
@shop ||= 'snowdevil.myshopify.com'
|
463
|
+
end
|
464
|
+
|
465
|
+
def authorize(shop)
|
466
|
+
@opts['rack.session']['shopify.omniauth_params'] = { shop: shop }
|
467
|
+
request.get('https://app.example.com/auth/shopify', opts)
|
468
|
+
end
|
469
|
+
|
470
|
+
def callback(params)
|
471
|
+
@opts['rack.session']['shopify.omniauth_params'] = { shop: shop }
|
472
|
+
request.get("https://app.example.com/auth/shopify/callback?#{Rack::Utils.build_query(params)}", opts)
|
473
|
+
end
|
474
|
+
|
475
|
+
def opts
|
476
|
+
@opts ||= { "rack.session" => {} }
|
477
|
+
end
|
478
|
+
|
479
|
+
def request
|
480
|
+
Rack::MockRequest.new(@app)
|
481
|
+
end
|
482
|
+
|
483
|
+
def shopify_authorize_url
|
484
|
+
"https://snowdevil.myshopify.com/admin/oauth/authorize?"
|
485
|
+
end
|
486
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$: << File.expand_path("../../lib", __FILE__)
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'omniauth-shopify-app'
|
4
|
+
|
5
|
+
require 'minitest/autorun'
|
6
|
+
require 'minitest/focus'
|
7
|
+
require 'rack/session'
|
8
|
+
require 'fakeweb'
|
9
|
+
require 'json'
|
10
|
+
require 'active_support/core_ext/hash'
|
11
|
+
require 'byebug'
|
12
|
+
|
13
|
+
OmniAuth.config.logger = Logger.new(nil)
|
14
|
+
OmniAuth.config.allowed_request_methods = [:post, :get]
|
15
|
+
|
16
|
+
FakeWeb.allow_net_connect = false
|
metadata
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: omniauth-shopify-app
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Hopper Gee
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-06-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: oauth2
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.4'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.4'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: omniauth
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '2.0'
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '2.0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: activesupport
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: minitest
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '5.6'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '5.6'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rspec
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 3.9.0
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 3.9.0
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: fakeweb
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '1.3'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '1.3'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: rack-session
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '2.0'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '2.0'
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: rake
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
type: :development
|
125
|
+
prerelease: false
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
- !ruby/object:Gem::Dependency
|
132
|
+
name: minitest-focus
|
133
|
+
requirement: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
type: :development
|
139
|
+
prerelease: false
|
140
|
+
version_requirements: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
description:
|
146
|
+
email:
|
147
|
+
- hopper.gee@hey.com
|
148
|
+
executables: []
|
149
|
+
extensions: []
|
150
|
+
extra_rdoc_files: []
|
151
|
+
files:
|
152
|
+
- ".github/workflows/build.yml"
|
153
|
+
- ".gitignore"
|
154
|
+
- Gemfile
|
155
|
+
- README.md
|
156
|
+
- Rakefile
|
157
|
+
- example/Gemfile
|
158
|
+
- example/config.ru
|
159
|
+
- lib/omniauth-shopify-app.rb
|
160
|
+
- lib/omniauth/shopify.rb
|
161
|
+
- lib/omniauth/shopify/encryptor.rb
|
162
|
+
- lib/omniauth/shopify/oauth_session.rb
|
163
|
+
- lib/omniauth/shopify/version.rb
|
164
|
+
- lib/omniauth/strategies/shopify.rb
|
165
|
+
- omniauth-shopify-app.gemspec
|
166
|
+
- test/integration_test.rb
|
167
|
+
- test/test_helper.rb
|
168
|
+
homepage: https://github.com/OldWayShopifyDev/omniauth-shopify-app
|
169
|
+
licenses:
|
170
|
+
- MIT
|
171
|
+
metadata:
|
172
|
+
allowed_push_host: https://rubygems.org
|
173
|
+
post_install_message:
|
174
|
+
rdoc_options: []
|
175
|
+
require_paths:
|
176
|
+
- lib
|
177
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - ">="
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: 2.1.9
|
182
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - ">="
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: '0'
|
187
|
+
requirements: []
|
188
|
+
rubygems_version: 3.3.26
|
189
|
+
signing_key:
|
190
|
+
specification_version: 4
|
191
|
+
summary: Shopify strategy for OmniAuth
|
192
|
+
test_files:
|
193
|
+
- test/integration_test.rb
|
194
|
+
- test/test_helper.rb
|