diff --git a/CHANGELOG.md b/CHANGELOG.md index 5872c10a..3e4e13b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Note: For changes to the API, see https://shopify.dev/changelog?filter=api ## Unreleased +- [#1312](https://github.com/Shopify/shopify-api-ruby/pull/1312) Use same leeway for `exp` and `nbf` when parsing JWT ## 14.2.0 - [#1309](https://github.com/Shopify/shopify-api-ruby/pull/1309) Add `Session#copy_attributes_from` method diff --git a/lib/shopify_api/auth/jwt_payload.rb b/lib/shopify_api/auth/jwt_payload.rb index e61b92bb..bd13326c 100644 --- a/lib/shopify_api/auth/jwt_payload.rb +++ b/lib/shopify_api/auth/jwt_payload.rb @@ -6,7 +6,8 @@ module Auth class JwtPayload extend T::Sig - JWT_EXPIRATION_LEEWAY = 10 + JWT_LEEWAY = 10 + JWT_EXPIRATION_LEEWAY = JWT_LEEWAY sig { returns(String) } attr_reader :iss, :dest, :aud, :sub, :jti, :sid @@ -73,8 +74,7 @@ def ==(other) sig { params(token: String, api_secret_key: String).returns(T::Hash[String, T.untyped]) } def decode_token(token, api_secret_key) - JWT.decode(token, api_secret_key, true, - { exp_leeway: JWT_EXPIRATION_LEEWAY, algorithm: "HS256" })[0] + JWT.decode(token, api_secret_key, true, leeway: JWT_LEEWAY, algorithm: "HS256")[0] rescue JWT::DecodeError => err raise ShopifyAPI::Errors::InvalidJwtTokenError, "Error decoding session token: #{err.message}" end diff --git a/test/auth/jwt_payload_test.rb b/test/auth/jwt_payload_test.rb index 7ca6bdd6..8bbe8a0a 100644 --- a/test/auth/jwt_payload_test.rb +++ b/test/auth/jwt_payload_test.rb @@ -76,7 +76,7 @@ def test_decode_jwt_payload_fails_with_expired_token def test_decode_jwt_payload_fails_if_not_activated_yet payload = @jwt_payload.dup - payload[:nbf] = (Time.now + 10).to_i + payload[:nbf] = (Time.now + 12).to_i jwt_token = JWT.encode(payload, ShopifyAPI::Context.api_secret_key, "HS256") assert_raises(ShopifyAPI::Errors::InvalidJwtTokenError) do ShopifyAPI::Auth::JwtPayload.new(jwt_token) @@ -92,6 +92,44 @@ def test_decode_jwt_payload_fails_with_invalid_api_key ShopifyAPI::Auth::JwtPayload.new(jwt_token) end end + + def test_decode_jwt_payload_succeeds_with_expiration_in_the_past_within_10s_leeway + payload = @jwt_payload.merge(exp: Time.now.to_i - 8) + jwt_token = JWT.encode(payload, ShopifyAPI::Context.api_secret_key, "HS256") + + decoded = ShopifyAPI::Auth::JwtPayload.new(jwt_token) + + assert_equal(payload, { + iss: decoded.iss, + dest: decoded.dest, + aud: decoded.aud, + sub: decoded.sub, + exp: decoded.exp, + nbf: decoded.nbf, + iat: decoded.iat, + jti: decoded.jti, + sid: decoded.sid, + }) + end + + def test_decode_jwt_payload_succeeds_with_not_before_in_the_future_within_10s_leeway + payload = @jwt_payload.merge(nbf: Time.now.to_i + 8) + jwt_token = JWT.encode(payload, ShopifyAPI::Context.api_secret_key, "HS256") + + decoded = ShopifyAPI::Auth::JwtPayload.new(jwt_token) + + assert_equal(payload, { + iss: decoded.iss, + dest: decoded.dest, + aud: decoded.aud, + sub: decoded.sub, + exp: decoded.exp, + nbf: decoded.nbf, + iat: decoded.iat, + jti: decoded.jti, + sid: decoded.sid, + }) + end end end end