shopify_api 14.2.0 → 14.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 58a7734df66af05601ae989f099fa50ad862bc60ded39ee711ec17a72e4ab1c5
4
- data.tar.gz: ad5c4b5724ce8defa5058df1b59f743cebf834e425f75620070e304d9d274655
3
+ metadata.gz: 0f2fd17eb9b42d6054e8b676c5d446cbc9890b7156f5c4b402a5efb776ad1421
4
+ data.tar.gz: 1deefc0fdcc79f57b70fb1fbeb2072916847b8d68c614b4b150dafd86f0ff6d5
5
5
  SHA512:
6
- metadata.gz: 29330b758a67691c6724cd78adfabf9b23466eccf6030510b0035d7e2ca0ea39fbc2eead5b6e2d53f8047aa137373ce8f2c402b71e06bd622208ccc24a34fb7d
7
- data.tar.gz: 761253df5dbecfda0b33f4b4eb891d33536cc9489398bb0cb64f637912cdd346e2ab48fb8490e5e32d5e7c837c69dded7fb17af1eef412ed3253dba4446ea1b7
6
+ metadata.gz: 3cfdd1a407afb83e8557d607c65dd74322a7ca944fe58b2b42d68a911917442f51c78a0a7e59759a4fca59ae063c7af253ad8a297614a409621c52e0baf60ee6
7
+ data.tar.gz: e218a6a5c77d9de8e3b0a755c0bcb1b4941bb5cfba503564ffa4ca0a59c19b76e8c0cb56cb1ee458091bd43004fa3b16c200fa4696b058a0b90c9c3475bd37d0
data/CHANGELOG.md CHANGED
@@ -4,6 +4,16 @@ Note: For changes to the API, see https://shopify.dev/changelog?filter=api
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 14.3.0
8
+ - [#1312](https://github.com/Shopify/shopify-api-ruby/pull/1312) Use same leeway for `exp` and `nbf` when parsing JWT
9
+ - [#1314](https://github.com/Shopify/shopify-api-ruby/pull/1314)
10
+ - Add new session util method `SessionUtils::session_id_from_shopify_id_token`
11
+ - `SessionUtils::current_session_id` now accepts shopify Id token in the format of `Bearer this_token` or just `this_token`
12
+ - [#1315](https://github.com/Shopify/shopify-api-ruby/pull/1315) Add helper/alias methods to `ShopifyAPI::Auth::JwtPayload`:
13
+ - `shopify_domain` alias for `shop` - returns the sanitized shop domain
14
+ - `shopify_user_id` - returns the user Id (`sub`) as an Integer value
15
+ - `expires_at` alias for `exp` - returns the expiration time
16
+
7
17
  ## 14.2.0
8
18
  - [#1309](https://github.com/Shopify/shopify-api-ruby/pull/1309) Add `Session#copy_attributes_from` method
9
19
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shopify_api (14.2.0)
4
+ shopify_api (14.3.0)
5
5
  activesupport
6
6
  concurrent-ruby
7
7
  hash_diff
@@ -46,13 +46,28 @@ Session persistence is handled by the [ShopifyApp](https://github.com/Shopify/sh
46
46
  #### Cookie
47
47
  Cookie based authentication is not supported for embedded apps due to browsers dropping support for third party cookies due to security concerns. Non-embedded apps are able to use cookies for session storage/retrieval.
48
48
 
49
- For *non-embedded* apps, you can pass the cookies into `ShopifyAPI::Utils::SessionUtils.current_session_id(nil, cookies, true)` for online (user) sessions or `ShopifyAPI::Utils::SessionUtils.current_session_id(nil, cookies, false)` for offline (store) sessions.
49
+ For *non-embedded* apps, you can pass the cookies into:
50
+ - `ShopifyAPI::Utils::SessionUtils.current_session_id(nil, cookies, true)` for online (user) sessions or
51
+ - `ShopifyAPI::Utils::SessionUtils.current_session_id(nil, cookies, false)` for offline (store) sessions.
50
52
 
51
53
  #### Getting Session ID From Embedded Requests
52
- For *embedded* apps, you can pass the auth header into `ShopifyAPI::Utils::SessionUtils.current_session_id(auth_header, nil, true)` for online (user) sessions or `ShopifyAPI::Utils::SessionUtils.current_session_id(auth_header, nil, false)` for offline (store) sessions. This function needs an `auth_header` which is the `HTTP_AUTHORIZATION` header.
53
54
 
54
55
  If your app uses client side rendering instead of server side rendering, you will need to use App Bridge's [authenticatedFetch](https://shopify.dev/docs/apps/auth/oauth/session-tokens/getting-started) to make authenticated API requests from the client.
55
56
 
57
+ For *embedded* apps:
58
+
59
+ If you have an `HTTP_AUTHORIZATION` header or `id_token` from the request URL params , you can pass that as `shopify_id_token` into:
60
+ - `ShopifyAPI::Utils::SessionUtils.current_session_id(shopify_id_token, nil, true)` for online (user) sessions or
61
+ - `ShopifyAPI::Utils::SessionUtils.current_session_id(shopify_id_token, nil, false)` for offline (store) sessions.
62
+
63
+ `current_session_id` accepts shopify_id_token in the format of `Bearer this_token` or just `this_token`.
64
+
65
+ You can also use this method to get session ID:
66
+ - `ShopifyAPI::Utils::SessionUtils::session_id_from_shopify_id_token(id_token: id_token, online: true)` for online (user) sessions or
67
+ - `ShopifyAPI::Utils::SessionUtils::session_id_from_shopify_id_token(id_token: id_token, online: false)` for offline (store) sessions.
68
+
69
+ `session_id_from_shopify_id_token` does **NOT** accept shopify_id_token in the format of `Bearer this_token`, you must pass in `this_token`.
70
+
56
71
  #### Start Making Authenticated Shopify Requests
57
72
 
58
73
  You can now start making authenticated Shopify API calls using the Admin [REST](usage/rest.md) or [GraphQL](usage/graphql.md) Clients or the [Storefront GraphQL Client](usage/graphql_storefront.md).
@@ -6,7 +6,8 @@ module ShopifyAPI
6
6
  class JwtPayload
7
7
  extend T::Sig
8
8
 
9
- JWT_EXPIRATION_LEEWAY = 10
9
+ JWT_LEEWAY = 10
10
+ JWT_EXPIRATION_LEEWAY = JWT_LEEWAY
10
11
 
11
12
  sig { returns(String) }
12
13
  attr_reader :iss, :dest, :aud, :sub, :jti, :sid
@@ -14,6 +15,8 @@ module ShopifyAPI
14
15
  sig { returns(Integer) }
15
16
  attr_reader :exp, :nbf, :iat
16
17
 
18
+ alias_method :expire_at, :exp
19
+
17
20
  sig { params(token: String).void }
18
21
  def initialize(token)
19
22
  payload_hash = begin
@@ -42,6 +45,12 @@ module ShopifyAPI
42
45
  def shop
43
46
  @dest.gsub("https://", "")
44
47
  end
48
+ alias_method :shopify_domain, :shop
49
+
50
+ sig { returns(Integer) }
51
+ def shopify_user_id
52
+ @sub.to_i
53
+ end
45
54
 
46
55
  # TODO: Remove before releasing v11
47
56
  sig { params(shop: String).returns(T::Boolean) }
@@ -73,8 +82,7 @@ module ShopifyAPI
73
82
 
74
83
  sig { params(token: String, api_secret_key: String).returns(T::Hash[String, T.untyped]) }
75
84
  def decode_token(token, api_secret_key)
76
- JWT.decode(token, api_secret_key, true,
77
- { exp_leeway: JWT_EXPIRATION_LEEWAY, algorithm: "HS256" })[0]
85
+ JWT.decode(token, api_secret_key, true, leeway: JWT_LEEWAY, algorithm: "HS256")[0]
78
86
  rescue JWT::DecodeError => err
79
87
  raise ShopifyAPI::Errors::InvalidJwtTokenError, "Error decoding session token: #{err.message}"
80
88
  end
@@ -11,28 +11,16 @@ module ShopifyAPI
11
11
 
12
12
  sig do
13
13
  params(
14
- auth_header: T.nilable(String),
14
+ shopify_id_token: T.nilable(String),
15
15
  cookies: T.nilable(T::Hash[String, String]),
16
16
  online: T::Boolean,
17
17
  ).returns(T.nilable(String))
18
18
  end
19
- def current_session_id(auth_header, cookies, online)
19
+ def current_session_id(shopify_id_token, cookies, online)
20
20
  if Context.embedded?
21
- if auth_header
22
- matches = auth_header.match(/^Bearer (.+)$/)
23
- unless matches
24
- ShopifyAPI::Logger.warn("Missing Bearer token in authorization header")
25
- raise Errors::MissingJwtTokenError, "Missing Bearer token in authorization header"
26
- end
27
-
28
- jwt_payload = Auth::JwtPayload.new(T.must(matches[1]))
29
- shop = jwt_payload.shop
30
-
31
- if online
32
- jwt_session_id(shop, jwt_payload.sub)
33
- else
34
- offline_session_id(shop)
35
- end
21
+ if shopify_id_token
22
+ id_token = shopify_id_token.gsub("Bearer ", "")
23
+ session_id_from_shopify_id_token(id_token: id_token, online: online)
36
24
  else
37
25
  # falling back to session cookie
38
26
  raise Errors::CookieNotFoundError, "JWT token or Session cookie not found for app" unless
@@ -48,6 +36,25 @@ module ShopifyAPI
48
36
  end
49
37
  end
50
38
 
39
+ sig do
40
+ params(
41
+ id_token: T.nilable(String),
42
+ online: T::Boolean,
43
+ ).returns(String)
44
+ end
45
+ def session_id_from_shopify_id_token(id_token:, online:)
46
+ raise Errors::MissingJwtTokenError, "Missing Shopify ID Token" if id_token.nil? || id_token.empty?
47
+
48
+ payload = Auth::JwtPayload.new(id_token)
49
+ shop = payload.shop
50
+
51
+ if online
52
+ jwt_session_id(shop, payload.sub)
53
+ else
54
+ offline_session_id(shop)
55
+ end
56
+ end
57
+
51
58
  sig { params(shop: String, user_id: String).returns(String) }
52
59
  def jwt_session_id(shop, user_id)
53
60
  "#{shop}_#{user_id}"
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module ShopifyAPI
5
- VERSION = "14.2.0"
5
+ VERSION = "14.3.0"
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shopify_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 14.2.0
4
+ version: 14.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-04-15 00:00:00.000000000 Z
11
+ date: 2024-04-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport