truelayer-signing 0.2.3 → 0.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2bd92901150104b3d2212252852071a8c3a1ed2e0a8f48ce72cb934686931bb9
4
- data.tar.gz: 6ca20861d24c36d2380e070ad9d2cc47d55614cdafbf3f0ba77505b64984ec7c
3
+ metadata.gz: a85a356c6af43716627f510dc07d9fb08e400dab2ff005dc1bdd55d32d074012
4
+ data.tar.gz: 5f3ad1c1b8402e2fbfa39323f5c8a95682285284030e675ea94c3c66034ddf6d
5
5
  SHA512:
6
- metadata.gz: b1b988b8a58ba51af8f6f4462e1de6871a0d79fa3d12bba9f7d12bb6ee6e14e22acc9c09268b11d622d2a6848f6ba070cc2704e6baa4b501dc89c59eb5870206
7
- data.tar.gz: c4afccedb9a1f9b3676b1b69238ce0b9b4016fc883a947a44d9945f2c27944ba97373e4bd01c111cfc23ad5f4120c1075e12e4c25ed6714b1718576a6711dc62
6
+ metadata.gz: 79c0a907ed21bfd4987c3ce74bdb4bf07fe1a7c5be6560e6766c7d6a5a34f9f2b5f6c91a14765c4bac799813e7174be3f200aef10ef1f894fa8b52ab015cce5d
7
+ data.tar.gz: 9cddb3eeda2211d0409430639b0a62fb603290500b2b5a6d4f6d2b078db5f4c79ed2fada5f9bd5a1bb0e66c073e084be57e8a96ad982b81b1ee49db2e1eb7534
data/CHANGELOG.md CHANGED
@@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  - ...
11
11
 
12
+ ## [0.3.0] – 2026-06-16
13
+
14
+ - Require Ruby `>= 3.2`
15
+ - Update pinned version for `jwt` from `~> 2.7.0` to `~> 3.2.0`
16
+
12
17
  ## [0.2.3] – 2024-07-29
13
18
 
14
19
  - Fix `Gemspec/AddRuntimeDependency` linter error
@@ -26,7 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
26
31
  ## [0.2.0] – 2023-06-13
27
32
 
28
33
  - Add support for signature verification using a JWKS: `TrueLayerSigning.verify_with_jwks(jwks)`
29
- and `TrueLayerSigning.extract_jws_header(signature)`.
34
+ and `TrueLayerSigning.extract_jws_header(signature)`.
30
35
 
31
36
  ## [0.1.2] – 2023-05-19
32
37
 
data/Gemfile CHANGED
@@ -2,4 +2,5 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rubocop", "~> 1.57", require: false
5
+ gem "rake", "~> 13.4", require: false
6
+ gem "rubocop", "~> 1.88", require: false
@@ -6,23 +6,52 @@
6
6
  module JWT
7
7
  module_function
8
8
 
9
+ class TrueLayerToken < Token
10
+ # See https://github.com/jwt/ruby-jwt/blob/main/lib/jwt/token.rb#L61-L63
11
+ def encoded_payload
12
+ @encoded_payload ||= ::JWT::Base64.url_encode(payload)
13
+ end
14
+ end
15
+
9
16
  class TrueLayerEncode < Encode
17
+ # See https://github.com/jwt/ruby-jwt/blob/main/lib/jwt/encode.rb#L15-L19
18
+ def initialize(options)
19
+ super
20
+
21
+ @token = TrueLayerToken.new(payload: options[:payload], header: options[:headers])
22
+ @key = options[:key]
23
+ @algorithm = options[:algorithm]
24
+ end
25
+ end
26
+
27
+ class TrueLayerEncodedToken < EncodedToken
10
28
  private
11
29
 
12
- # See https://github.com/jwt/ruby-jwt/blob/main/lib/jwt/encode.rb#L53-L55
13
- def encode_payload
14
- ::JWT::Base64.url_encode(@payload)
30
+ # See https://github.com/jwt/ruby-jwt/blob/main/lib/jwt/encoded_token.rb#L189-L198
31
+ def decode_payload
32
+ raise JWT::DecodeError, "Encoded payload is empty" if encoded_payload == ""
33
+
34
+ if unencoded_payload?
35
+ verify_claims!(crit: ["b64"])
36
+ return parse_unencoded(encoded_payload)
37
+ end
38
+
39
+ ::JWT::Base64.url_decode(encoded_payload || "")
15
40
  end
16
41
  end
17
42
 
18
43
  class TrueLayerDecode < Decode
19
- private
44
+ # See https://github.com/jwt/ruby-jwt/blob/main/lib/jwt/decode.rb#L22-L30
45
+ def initialize(jwt, key, verify, options, &keyfinder)
46
+ super
20
47
 
21
- # See https://github.com/jwt/ruby-jwt/blob/main/lib/jwt/decode.rb#L154-L156
22
- def payload
23
- @payload ||= ::JWT::Base64.url_decode(@segments[1])
24
- rescue ::JSON::ParserError
25
- raise JWT::DecodeError, "Invalid segment encoding"
48
+ raise JWT::DecodeError, "Nil JSON web token" unless jwt
49
+
50
+ @token = TrueLayerEncodedToken.new(jwt)
51
+ @key = key
52
+ @options = options
53
+ @verify = verify
54
+ @keyfinder = keyfinder
26
55
  end
27
56
  end
28
57
 
@@ -35,8 +64,16 @@ module JWT
35
64
  ).segments
36
65
  end
37
66
 
38
- # rubocop:disable Style/OptionalArguments, Style/OptionalBooleanParameter
39
- def truelayer_decode(jwt, key, verify = true, options, &keyfinder)
67
+ # rubocop:disable Style/OptionalBooleanParameter, Naming/BlockForwarding
68
+ def truelayer_decode(jwt, key, verify = true, options = {}, &keyfinder)
69
+ # Support both `truelayer_decode(jwt, key, options)` and
70
+ # `truelayer_decode(jwt, key, verify, options)` call styles by treating a
71
+ # Hash in the `verify` position as the `options` argument.
72
+ if verify.is_a?(Hash)
73
+ options = verify
74
+ verify = true
75
+ end
76
+
40
77
  TrueLayerDecode.new(
41
78
  jwt,
42
79
  key,
@@ -45,5 +82,5 @@ module JWT
45
82
  &keyfinder
46
83
  ).decode_segments
47
84
  end
48
- # rubocop:enable Style/OptionalArguments, Style/OptionalBooleanParameter
85
+ # rubocop:enable Style/OptionalBooleanParameter, Naming/BlockForwarding
49
86
  end
@@ -2,8 +2,6 @@
2
2
 
3
3
  module TrueLayerSigning
4
4
  class Verifier < JwsBase
5
- EXPECTED_EC_KEY_COORDS_LENGTH = 66
6
-
7
5
  attr_reader :required_headers, :key_type, :key_value
8
6
 
9
7
  def initialize(args)
@@ -109,27 +107,12 @@ module TrueLayerSigning
109
107
 
110
108
  raise(Error, "JWKS does not include given `kid` value") unless jwk
111
109
 
112
- valid_jwk = apply_zero_padding_as_needed(jwk)
113
-
114
- JWT::JWK::EC.import(valid_jwk).public_key
110
+ JWT::JWK::EC.import(jwk).public_key
115
111
  else
116
112
  raise(Error, "Type of public key not recognised")
117
113
  end
118
114
  end
119
115
 
120
- def apply_zero_padding_as_needed(jwk)
121
- valid_jwk = jwk.clone
122
-
123
- %i(x y).each do |elem|
124
- coords = Base64.urlsafe_decode64(valid_jwk[elem])
125
- diff = EXPECTED_EC_KEY_COORDS_LENGTH - coords.length
126
-
127
- valid_jwk[elem] = Base64.urlsafe_encode64(("\x00" * diff) + coords) if diff.positive?
128
- end
129
-
130
- valid_jwk
131
- end
132
-
133
116
  def validate_required_headers!(headers)
134
117
  raise(Error, "Signature missing required header(s)") if required_headers&.any? do |key|
135
118
  !headers.key?(key.downcase)
@@ -123,7 +123,7 @@ class TrueLayerSigningTest < Minitest::Test
123
123
  body = { currency: "GBP", max_amount_in_minor: 50_000_00, name: "Foo???" }.to_json
124
124
  idempotency_key = "idemp-2076717c-9005-4811-a321-9e0787fa0382"
125
125
  path = "/merchant_accounts/a61acaef-ee05-4077-92f3-25543a11bd8d/sweeping"
126
- tl_signature = read_file("../../test-resources/tl-signature.txt")
126
+ tl_signature = read_file("../../test-resources/tl-signature.txt").strip
127
127
 
128
128
  result = TrueLayerSigning.verify_with_pem(PUBLIC_KEY)
129
129
  .set_method(:post)
@@ -403,7 +403,7 @@ class TrueLayerSigningTest < Minitest::Test
403
403
  end
404
404
 
405
405
  def test_verify_with_jwks_should_succeed
406
- hook_signature = read_file("../../test-resources/webhook-signature.txt")
406
+ hook_signature = read_file("../../test-resources/webhook-signature.txt").strip
407
407
  jwks = read_file("../../test-resources/jwks.json")
408
408
  body = { event_type: "example", event_id: "18b2842b-a57b-4887-a0a6-d3c7c36f1020" }.to_json
409
409
 
@@ -418,18 +418,6 @@ class TrueLayerSigningTest < Minitest::Test
418
418
 
419
419
  def test_verify_with_jwks_with_zero_padding_missing_should_succeed
420
420
  jwks = read_file("resources/missing-zero-padding-test-jwks.json")
421
-
422
- # JWKS with EC key missing zero padded coords is not supported by `jwt` gem
423
-
424
- jwks_as_json = JSON.parse(jwks, symbolize_names: true)
425
- jwk_missing_padding = jwks_as_json[:keys].find { |e| e[:kty] == "EC" }
426
- imported_jwk = JWT::JWK::EC.import(jwk_missing_padding)
427
-
428
- error = assert_raises(OpenSSL::PKey::EC::Point::Error) { imported_jwk.public_key.check_key }
429
- assert_equal("EC_POINT_bn2point: invalid encoding", error.message)
430
-
431
- # But supported by `truelayer-signing` using zero padding (prepend)
432
-
433
421
  payload = read_file("resources/missing-zero-padding-test-payload.json")
434
422
  body = JSON.parse(payload).to_json
435
423
 
@@ -438,11 +426,11 @@ class TrueLayerSigningTest < Minitest::Test
438
426
  .set_path("/a147f26a-f07e-47e3-9526-d52f1f1fdd55")
439
427
  .add_header("x-tl-webhook-timestamp", "2023-06-09T15:40:30Z")
440
428
  .set_body(body)
441
- .verify(read_file("resources/missing-zero-padding-test-signature.txt"))
429
+ .verify(read_file("resources/missing-zero-padding-test-signature.txt").strip)
442
430
  end
443
431
 
444
432
  def test_verify_with_jwks_with_wrong_timestamp_should_fail
445
- hook_signature = read_file("../../test-resources/webhook-signature.txt")
433
+ hook_signature = read_file("../../test-resources/webhook-signature.txt").strip
446
434
  jwks = read_file("../../test-resources/jwks.json")
447
435
  body = { event_type: "example", event_id: "18b2842b-a57b-4887-a0a6-d3c7c36f1020" }.to_json
448
436
 
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "lib"))
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = "truelayer-signing"
7
- s.version = "0.2.3"
7
+ s.version = "0.3.0"
8
8
  s.summary = "Ruby gem to produce and verify TrueLayer API requests signatures"
9
9
  s.description = "TrueLayer provides instant access to open banking to " \
10
10
  "easily integrate next-generation payments and financial data into any app." \
@@ -27,10 +27,10 @@ Gem::Specification.new do |s|
27
27
  "Rakefile",
28
28
  "lib/**/*.*",
29
29
  "test/**/*.*",
30
- "truelayer-signing.gemspec",
30
+ "truelayer-signing.gemspec"
31
31
  ]
32
32
  s.require_path = "lib"
33
33
 
34
- s.required_ruby_version = ">= 2.7"
35
- s.add_dependency("jwt", "~> 2.7.0")
34
+ s.required_ruby_version = ">= 3.2"
35
+ s.add_dependency("jwt", "~> 3.2.0")
36
36
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: truelayer-signing
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Plattret
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-07-29 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: jwt
@@ -16,14 +15,14 @@ dependencies:
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: 2.7.0
18
+ version: 3.2.0
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
- version: 2.7.0
25
+ version: 3.2.0
27
26
  description: TrueLayer provides instant access to open banking to easily integrate
28
27
  next-generation payments and financial data into any app.This helps easily sign
29
28
  TrueLayer API requests using a JSON web signature.
@@ -58,7 +57,6 @@ licenses:
58
57
  metadata:
59
58
  bug_tracker_uri: https://github.com/TrueLayer/truelayer-signing/issues
60
59
  changelog_uri: https://github.com/TrueLayer/truelayer-signing/blob/main/ruby/CHANGELOG.md
61
- post_install_message:
62
60
  rdoc_options: []
63
61
  require_paths:
64
62
  - lib
@@ -66,15 +64,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
66
64
  requirements:
67
65
  - - ">="
68
66
  - !ruby/object:Gem::Version
69
- version: '2.7'
67
+ version: '3.2'
70
68
  required_rubygems_version: !ruby/object:Gem::Requirement
71
69
  requirements:
72
70
  - - ">="
73
71
  - !ruby/object:Gem::Version
74
72
  version: '0'
75
73
  requirements: []
76
- rubygems_version: 3.5.11
77
- signing_key:
74
+ rubygems_version: 3.6.9
78
75
  specification_version: 4
79
76
  summary: Ruby gem to produce and verify TrueLayer API requests signatures
80
77
  test_files: []