omniauth-shopify-oauth2 1.1.14 → 1.1.15
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/Gemfile +4 -0
- data/README.md +10 -0
- data/lib/omniauth/shopify/version.rb +1 -1
- data/lib/omniauth/strategies/shopify.rb +22 -0
- data/omniauth-shopify-oauth2.gemspec +1 -1
- data/test/integration_test.rb +57 -7
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9fb79e1fb61eb4b740f0599821ce94f0d90bbf07
|
4
|
+
data.tar.gz: 5717243aa9a50abe95ce4b195d068a3b9e3605d1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 14c7013cd7138bb608422125897bec4662984b4080a7d13b479bb8fcdba8f838713ec8a0a7ef8eb7d8168b84532faec118b3a8f554d61df8e2bffa53ef542800
|
7
|
+
data.tar.gz: 2514636e95bf710c68545b420266e372ff9ad60df555c883df95bafa72a18125181473a29b5fdc1db0de68f6c38d82cec6c6752b54f46452db8858fad35bc24e
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -26,6 +26,16 @@ Rails.application.config.middleware.use OmniAuth::Builder do
|
|
26
26
|
end
|
27
27
|
```
|
28
28
|
|
29
|
+
Authenticate the user by having them visit /auth/shopify with a `shop` query parameter of their shop's myshopify.com domain. For example, the following form could be used
|
30
|
+
|
31
|
+
```html
|
32
|
+
<form action="/auth/shopify" method="get">
|
33
|
+
<label for="shop">Enter your store's URL:</label>
|
34
|
+
<input type="text" name="shop" placeholder="your-shop-url.myshopify.com">
|
35
|
+
<button type="submit">Log In</button>
|
36
|
+
</form>
|
37
|
+
```
|
38
|
+
|
29
39
|
## Configuring
|
30
40
|
|
31
41
|
You can configure the scope, which you pass in to the `provider` method via a `Hash`:
|
@@ -18,6 +18,10 @@ module OmniAuth
|
|
18
18
|
option :callback_url
|
19
19
|
option :myshopify_domain, 'myshopify.com'
|
20
20
|
|
21
|
+
# When `true`, the user's permission level will apply (in addition to
|
22
|
+
# the requested access scope) when making API requests to Shopify.
|
23
|
+
option :per_user_permissions, false
|
24
|
+
|
21
25
|
# When `true`, the authorization phase will fail if the granted scopes
|
22
26
|
# mismatch the requested scopes.
|
23
27
|
option :validate_granted_scopes, true
|
@@ -29,6 +33,16 @@ module OmniAuth
|
|
29
33
|
|
30
34
|
uid { URI.parse(options[:client_options][:site]).host }
|
31
35
|
|
36
|
+
extra do
|
37
|
+
if access_token
|
38
|
+
{
|
39
|
+
'associated_user' => access_token['associated_user'],
|
40
|
+
'associated_user_scope' => access_token['associated_user_scope'],
|
41
|
+
'scope' => access_token['scope'],
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
32
46
|
def valid_site?
|
33
47
|
!!(/\A(https|http)\:\/\/[a-zA-Z0-9][a-zA-Z0-9\-]*\.#{Regexp.quote(options[:myshopify_domain])}[\/]?\z/ =~ options[:client_options][:site])
|
34
48
|
end
|
@@ -71,6 +85,10 @@ module OmniAuth
|
|
71
85
|
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, secret, encoded_params)
|
72
86
|
end
|
73
87
|
|
88
|
+
def valid_permissions?(token)
|
89
|
+
token && (options[:per_user_permissions] == !token['associated_user'].nil?)
|
90
|
+
end
|
91
|
+
|
74
92
|
def fix_https
|
75
93
|
options[:client_options][:site].gsub!(/\Ahttp\:/, 'https:')
|
76
94
|
end
|
@@ -96,6 +114,9 @@ module OmniAuth
|
|
96
114
|
unless valid_scope?(token)
|
97
115
|
return fail!(:invalid_scope, CallbackError.new(:invalid_scope, "Scope does not match, it may have been tampered with."))
|
98
116
|
end
|
117
|
+
unless valid_permissions?(token)
|
118
|
+
return fail!(:invalid_permissions, CallbackError.new(:invalid_permissions, "Requested API access mode does not match."))
|
119
|
+
end
|
99
120
|
|
100
121
|
super
|
101
122
|
end
|
@@ -107,6 +128,7 @@ module OmniAuth
|
|
107
128
|
def authorize_params
|
108
129
|
super.tap do |params|
|
109
130
|
params[:scope] = normalized_scopes(params[:scope] || DEFAULT_SCOPE).join(SCOPE_DELIMITER)
|
131
|
+
params[:grant_options] = ['per-user'] if options[:per_user_permissions]
|
110
132
|
end
|
111
133
|
end
|
112
134
|
|
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.name = 'omniauth-shopify-oauth2'
|
7
7
|
s.version = OmniAuth::Shopify::VERSION
|
8
8
|
s.authors = ['Denis Odorcic']
|
9
|
-
s.email = ['
|
9
|
+
s.email = ['gems@shopify.com']
|
10
10
|
s.summary = 'Shopify strategy for OmniAuth'
|
11
11
|
s.homepage = 'https://github.com/Shopify/omniauth-shopify-oauth2'
|
12
12
|
s.license = 'MIT'
|
data/test/integration_test.rb
CHANGED
@@ -13,11 +13,19 @@ class IntegrationTest < Minitest::Test
|
|
13
13
|
def test_authorize
|
14
14
|
response = authorize('snowdevil.myshopify.com')
|
15
15
|
assert_equal 302, response.status
|
16
|
-
assert_match
|
16
|
+
assert_match %r{\A#{Regexp.quote(shopify_authorize_url)}}, response.location
|
17
17
|
redirect_params = Rack::Utils.parse_query(URI(response.location).query)
|
18
18
|
assert_equal "123", redirect_params['client_id']
|
19
19
|
assert_equal "https://app.example.com/auth/shopify/callback", redirect_params['redirect_uri']
|
20
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[]']
|
21
29
|
end
|
22
30
|
|
23
31
|
def test_authorize_overrides_site_with_https_scheme
|
@@ -27,7 +35,7 @@ class IntegrationTest < Minitest::Test
|
|
27
35
|
}
|
28
36
|
|
29
37
|
response = authorize('snowdevil.myshopify.com')
|
30
|
-
assert_match
|
38
|
+
assert_match %r{\A#{Regexp.quote(shopify_authorize_url)}}, response.location
|
31
39
|
end
|
32
40
|
|
33
41
|
def test_site_validation
|
@@ -142,7 +150,7 @@ class IntegrationTest < Minitest::Test
|
|
142
150
|
|
143
151
|
response = authorize('snowdevil')
|
144
152
|
assert_equal 302, response.status
|
145
|
-
assert_match
|
153
|
+
assert_match %r{\A#{Regexp.quote("https://snowdevil.myshopify.dev:3000/admin/oauth/authorize?")}}, response.location
|
146
154
|
redirect_params = Rack::Utils.parse_query(URI(response.location).query)
|
147
155
|
assert_equal 'read_products,read_orders,write_content', redirect_params['scope']
|
148
156
|
assert_equal 'https://app.example.com/admin/auth/legacy/callback', redirect_params['redirect_uri']
|
@@ -178,7 +186,7 @@ class IntegrationTest < Minitest::Test
|
|
178
186
|
def test_callback_with_mismatching_scope_fails
|
179
187
|
access_token = SecureRandom.hex(16)
|
180
188
|
code = SecureRandom.hex(16)
|
181
|
-
expect_access_token_request(access_token, 'some_invalid_scope')
|
189
|
+
expect_access_token_request(access_token, 'some_invalid_scope', nil)
|
182
190
|
|
183
191
|
response = callback(sign_params(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
184
192
|
|
@@ -247,6 +255,44 @@ class IntegrationTest < Minitest::Test
|
|
247
255
|
assert_callback_success(response, access_token, code)
|
248
256
|
end
|
249
257
|
|
258
|
+
def test_callback_when_per_user_permissions_are_present_but_not_requested
|
259
|
+
build_app(scope: 'scope', per_user_permissions: false)
|
260
|
+
|
261
|
+
access_token = SecureRandom.hex(16)
|
262
|
+
code = SecureRandom.hex(16)
|
263
|
+
expect_access_token_request(access_token, 'scope', { id: 1, email: 'bob@bobsen.com'})
|
264
|
+
|
265
|
+
response = callback(sign_params(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
266
|
+
|
267
|
+
assert_equal 302, response.status
|
268
|
+
assert_equal '/auth/failure?message=invalid_permissions&strategy=shopify', response.location
|
269
|
+
end
|
270
|
+
|
271
|
+
def test_callback_when_per_user_permissions_are_not_present_but_requested
|
272
|
+
build_app(scope: 'scope', per_user_permissions: true)
|
273
|
+
|
274
|
+
access_token = SecureRandom.hex(16)
|
275
|
+
code = SecureRandom.hex(16)
|
276
|
+
expect_access_token_request(access_token, 'scope', nil)
|
277
|
+
|
278
|
+
response = callback(sign_params(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
279
|
+
|
280
|
+
assert_equal 302, response.status
|
281
|
+
assert_equal '/auth/failure?message=invalid_permissions&strategy=shopify', response.location
|
282
|
+
end
|
283
|
+
|
284
|
+
def test_callback_works_when_per_user_permissions_are_present_and_requested
|
285
|
+
build_app(scope: 'scope', per_user_permissions: true)
|
286
|
+
|
287
|
+
access_token = SecureRandom.hex(16)
|
288
|
+
code = SecureRandom.hex(16)
|
289
|
+
expect_access_token_request(access_token, 'scope', { id: 1, email: 'bob@bobsen.com'})
|
290
|
+
|
291
|
+
response = callback(sign_params(shop: 'snowdevil.myshopify.com', code: code, state: opts["rack.session"]["omniauth.state"]))
|
292
|
+
|
293
|
+
assert_equal 200, response.status
|
294
|
+
end
|
295
|
+
|
250
296
|
private
|
251
297
|
|
252
298
|
def sign_params(params)
|
@@ -259,9 +305,9 @@ class IntegrationTest < Minitest::Test
|
|
259
305
|
params
|
260
306
|
end
|
261
307
|
|
262
|
-
def expect_access_token_request(access_token, scope)
|
308
|
+
def expect_access_token_request(access_token, scope, associated_user=nil)
|
263
309
|
FakeWeb.register_uri(:post, "https://snowdevil.myshopify.com/admin/oauth/access_token",
|
264
|
-
body: JSON.dump(access_token: access_token, scope: scope),
|
310
|
+
body: JSON.dump(access_token: access_token, scope: scope, associated_user: associated_user),
|
265
311
|
content_type: 'application/json')
|
266
312
|
end
|
267
313
|
|
@@ -282,7 +328,7 @@ class IntegrationTest < Minitest::Test
|
|
282
328
|
def assert_auth_failure(response, reason)
|
283
329
|
assert_nil FakeWeb.last_request
|
284
330
|
assert_equal 302, response.status
|
285
|
-
assert_match
|
331
|
+
assert_match %r{\A#{Regexp.quote("/auth/failure?message=#{reason}")}}, response.location
|
286
332
|
end
|
287
333
|
|
288
334
|
def build_app(options={})
|
@@ -314,4 +360,8 @@ class IntegrationTest < Minitest::Test
|
|
314
360
|
def request
|
315
361
|
Rack::MockRequest.new(@app)
|
316
362
|
end
|
363
|
+
|
364
|
+
def shopify_authorize_url
|
365
|
+
"https://snowdevil.myshopify.com/admin/oauth/authorize?"
|
366
|
+
end
|
317
367
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: omniauth-shopify-oauth2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Denis Odorcic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: omniauth-oauth2
|
@@ -68,7 +68,7 @@ dependencies:
|
|
68
68
|
version: '0'
|
69
69
|
description:
|
70
70
|
email:
|
71
|
-
-
|
71
|
+
- gems@shopify.com
|
72
72
|
executables: []
|
73
73
|
extensions: []
|
74
74
|
extra_rdoc_files: []
|
@@ -109,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
109
|
version: '0'
|
110
110
|
requirements: []
|
111
111
|
rubyforge_project:
|
112
|
-
rubygems_version: 2.
|
112
|
+
rubygems_version: 2.5.1
|
113
113
|
signing_key:
|
114
114
|
specification_version: 4
|
115
115
|
summary: Shopify strategy for OmniAuth
|