Skip to content

Commit c1163e0

Browse files
Merge pull request #1312 from Shopify/add_leeway_for_jwt_nbf
Use same leeway for `exp` and `nbf` when parsing JWT
2 parents 7b09b25 + 0eb2306 commit c1163e0

File tree

3 files changed

+43
-4
lines changed

3 files changed

+43
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
Note: For changes to the API, see https://shopify.dev/changelog?filter=api
44

55
## Unreleased
6+
- [#1312](https://github.com/Shopify/shopify-api-ruby/pull/1312) Use same leeway for `exp` and `nbf` when parsing JWT
67

78
## 14.2.0
89
- [#1309](https://github.com/Shopify/shopify-api-ruby/pull/1309) Add `Session#copy_attributes_from` method

lib/shopify_api/auth/jwt_payload.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ module Auth
66
class JwtPayload
77
extend T::Sig
88

9-
JWT_EXPIRATION_LEEWAY = 10
9+
JWT_LEEWAY = 10
10+
JWT_EXPIRATION_LEEWAY = JWT_LEEWAY
1011

1112
sig { returns(String) }
1213
attr_reader :iss, :dest, :aud, :sub, :jti, :sid
@@ -73,8 +74,7 @@ def ==(other)
7374

7475
sig { params(token: String, api_secret_key: String).returns(T::Hash[String, T.untyped]) }
7576
def decode_token(token, api_secret_key)
76-
JWT.decode(token, api_secret_key, true,
77-
{ exp_leeway: JWT_EXPIRATION_LEEWAY, algorithm: "HS256" })[0]
77+
JWT.decode(token, api_secret_key, true, leeway: JWT_LEEWAY, algorithm: "HS256")[0]
7878
rescue JWT::DecodeError => err
7979
raise ShopifyAPI::Errors::InvalidJwtTokenError, "Error decoding session token: #{err.message}"
8080
end

test/auth/jwt_payload_test.rb

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def test_decode_jwt_payload_fails_with_expired_token
7676

7777
def test_decode_jwt_payload_fails_if_not_activated_yet
7878
payload = @jwt_payload.dup
79-
payload[:nbf] = (Time.now + 10).to_i
79+
payload[:nbf] = (Time.now + 12).to_i
8080
jwt_token = JWT.encode(payload, ShopifyAPI::Context.api_secret_key, "HS256")
8181
assert_raises(ShopifyAPI::Errors::InvalidJwtTokenError) do
8282
ShopifyAPI::Auth::JwtPayload.new(jwt_token)
@@ -92,6 +92,44 @@ def test_decode_jwt_payload_fails_with_invalid_api_key
9292
ShopifyAPI::Auth::JwtPayload.new(jwt_token)
9393
end
9494
end
95+
96+
def test_decode_jwt_payload_succeeds_with_expiration_in_the_past_within_10s_leeway
97+
payload = @jwt_payload.merge(exp: Time.now.to_i - 8)
98+
jwt_token = JWT.encode(payload, ShopifyAPI::Context.api_secret_key, "HS256")
99+
100+
decoded = ShopifyAPI::Auth::JwtPayload.new(jwt_token)
101+
102+
assert_equal(payload, {
103+
iss: decoded.iss,
104+
dest: decoded.dest,
105+
aud: decoded.aud,
106+
sub: decoded.sub,
107+
exp: decoded.exp,
108+
nbf: decoded.nbf,
109+
iat: decoded.iat,
110+
jti: decoded.jti,
111+
sid: decoded.sid,
112+
})
113+
end
114+
115+
def test_decode_jwt_payload_succeeds_with_not_before_in_the_future_within_10s_leeway
116+
payload = @jwt_payload.merge(nbf: Time.now.to_i + 8)
117+
jwt_token = JWT.encode(payload, ShopifyAPI::Context.api_secret_key, "HS256")
118+
119+
decoded = ShopifyAPI::Auth::JwtPayload.new(jwt_token)
120+
121+
assert_equal(payload, {
122+
iss: decoded.iss,
123+
dest: decoded.dest,
124+
aud: decoded.aud,
125+
sub: decoded.sub,
126+
exp: decoded.exp,
127+
nbf: decoded.nbf,
128+
iat: decoded.iat,
129+
jti: decoded.jti,
130+
sid: decoded.sid,
131+
})
132+
end
95133
end
96134
end
97135
end

0 commit comments

Comments
 (0)